코딩스토리

[Android/안드로이드] ImageVideoView / url에 따라 이미지나 비디오를 하나의 뷰로 만들기 본문

Android/유용한 기술

[Android/안드로이드] ImageVideoView / url에 따라 이미지나 비디오를 하나의 뷰로 만들기

라크라꾸 2020. 1. 4. 18:52

인스타그램에서 ViewPager로 옆으로 스크롤하는부분인데 이미지가 나올 때 있고, 동영상이 나올 때가 있습니다. 이번 시간에는 하나의 뷰로 url의 확장자가 이미지형 확장자일 경우 이미지뷰를 보여주고, 비디오형 확장자일 경우는 비디오뷰를 보여줄 수 있는 이미지비디오뷰를 만들어보겠습니다.

 

비디오 뷰는 안드로이드에서 기본적으로 제공하는 비디오를 사용하지 않고 구글에서 만든 오픈소스 미디어 플레이 라이브러리인 ExoPlayer를 사용해 보겠습니다. ExoPlayer는 기존 비디오뷰보다 더욱 작고 유연하며 안정적이라 유튜브와 구글무비에서도 사용한답니다~

 

우선 ExoPlayer를 사용하기 위한 사용법에 대해 알아보겠습니다.

 

라이브러리 추가

build.gradle(Module: app)

  
  apply plugin: 'com.android.application'
  
  android {
      compileSdkVersion 29
      buildToolsVersion "29.0.2"
      defaultConfig {
          applicationId "com.example.imagevideoviewtest"
          minSdkVersion 15
          targetSdkVersion 29
          versionCode 1
          versionName "1.0"
          testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
      }
      buildTypes {
          release {
              minifyEnabled false
              proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
          }
      }
      compileOptions {
          targetCompatibility = "8"
          sourceCompatibility = "8"
      }
  }
  
  dependencies {
      implementation fileTree(dir: 'libs', include: ['*.jar'])
      implementation 'androidx.appcompat:appcompat:1.1.0'
      implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
      testImplementation 'junit:junit:4.12'
      androidTestImplementation 'androidx.test.ext:junit:1.1.1'
      androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
  
      // ExoPlayer
      implementation 'com.google.android.exoplayer:exoplayer:2.9.6'
      implementation 'com.google.android.exoplayer:exoplayer-core:2.9.6'
      implementation 'com.google.android.exoplayer:exoplayer-dash:2.9.6'
      implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.6'
  
      //Glide 이미지변환
      implementation 'com.github.bumptech.glide:glide:4.9.0'
      annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
  
  }
  

위 에서 ExoPlayer를 사용하기 위해 라이브러리를 추가해주고, 이미지를 로드해주는 Glide 라이브러리를 추가해줍니다.

 

위 코드중에 아래와 같은 부분이 있습니다. ExoPlayer는 자바8을 사용하기 때문에 이를 사용할 수 있게 해주는 라이브러리를 추가해줘야합니다.

추가해주지 않을 경우

java.lang.BootstrapMethodError: Exception from call site #62 bootstrap method

오류가 나게됩니다..

  
  compileOptions {
      targetCompatibility = "8"
      sourceCompatibility = "8"
  }
      

 

ExoPlayerView를 사용하기 위해서는 미디어 플레이어 초기화시켜줘야하고, 미디어 소스도 초기화해주는 등 한번만 만들게되면 상관이 없는데 여러군데에서 사용할게 되면 이 코드를 똑같이 여러개 만들어줘야합니다.

저는 그게 싫어서 url만 호출하면 되게 ExoPlayVier를 사용할 수 있는 모듈을 따로 만들어놓았습니다.

 

CustomExoPlayerView 생성

CustomExoPlayerView.java

  
  package com.example.imagevideoviewtest;
  
  import android.content.Context;
  import android.graphics.Color;
  import android.net.Uri;
  import android.util.AttributeSet;
  import android.util.Log;
  
  import com.google.android.exoplayer2.ExoPlayerFactory;
  import com.google.android.exoplayer2.SimpleExoPlayer;
  import com.google.android.exoplayer2.source.ExtractorMediaSource;
  import com.google.android.exoplayer2.source.TrackGroupArray;
  import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
  import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
  import com.google.android.exoplayer2.ui.PlayerView;
  import com.google.android.exoplayer2.upstream.DataSource;
  import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
  import com.google.android.exoplayer2.util.Util;
  
  
  public class CustomExoPlayerView extends PlayerView {
  
      SimpleExoPlayer player;
      DataSource.Factory mediaDataSourceFactory;
      DefaultTrackSelector trackSelector;
      TrackGroupArray lastSeenTrackGroupArray;
      AdaptiveTrackSelection.Factory videoTrackSelectionFactory;
  
      public CustomExoPlayerView(Context context) {
          super(context);
      }
  
      public CustomExoPlayerView(Context context, AttributeSet attrs) {
          super(context, attrs);
      }
  
      public CustomExoPlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
      }
  
  
      public void initializePlayer(String url) {
          if (trackSelector != null) {
              Log.i("DATA", "trankSelector : not null");
          } else {
              Log.i("DATA", "trankSelector : null");
          }
  
          trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
          mediaDataSourceFactory = new DefaultDataSourceFactory(getContext(), Util.getUserAgent(getContext(), "mediaPlayerSample"));
  
          ExtractorMediaSource mediaSource = new ExtractorMediaSource.Factory(mediaDataSourceFactory)
                  .createMediaSource(Uri.parse(url));
  
          player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector);
  
  
          player.prepare(mediaSource, false, false);
          player.setPlayWhenReady(true);
  
          setShutterBackgroundColor(Color.TRANSPARENT);
          setPlayer(player);
          requestFocus();
  
          lastSeenTrackGroupArray = null;
      }
  
      //현재 동영상의 시간
      public int getCurrentPosition(){
          return (int) player.getCurrentPosition();
      }
  
      //동영상이 실행되고있는지 확인
      public boolean isPlaying(){
          return player.getPlayWhenReady();
      }
  
      //동영상 정지
      public void pause(){
          player.setPlayWhenReady(false);
      }
  
      //동영상 재생
      public void start(){
          player.setPlayWhenReady(true);
      }
  
      //동영상 시간 설정
      public void seekTo(int position){
          player.seekTo(position);
      }
  
  
      @Override
      public void onResume() {
          super.onResume();
          Log.i("DATADATA","onResume");
          player.setPlayWhenReady(true);
      }
      @Override
      public void onPause() {
          super.onPause();
          Log.i("DATADATA","onPause");
          player.setPlayWhenReady(false);
      }
  
      //동영상 해제
      public void releasePlayer() {
          player.release();
          trackSelector = null;
      }
  }
  

상세 기능(현재 동영상이 재생되는지, 동영상 정지, 동영상 재생, 동영상 시간 설정, 동영상 시간 가져오기)기능은 여기서 사용하지 않지만 호출하면 사용할 수 있게 만들어 놓았습니다. releasePlayer를 통해 Activity에서 빠져나가거나 종료를 했을 때 동영상을 해제해줘야합니다. 해제해주지 않으면 화면을 이동해도 계속 동영상이 재생됩니다..

 

ExoPlayerView를 쉽게 생성하는 커스텀뷰를 만들었지만 저희가 이번시간에 만들게 될 뷰는 하나의 뷰에서 이미지와 동영상을 구분해 보여주는 뷰를 만들 것이라 이 기능을 가진 CustomView를 생성해줍니다.

 

이미지/동영상 구분해 보여주는 ImageVideoView 생성

ImageVideoView.java

  
  package com.example.imagevideoviewtest;
  
  import android.content.Context;
  import android.graphics.Color;
  import android.util.AttributeSet;
  import android.view.ViewGroup;
  import android.widget.ImageView;
  import android.widget.LinearLayout;
  
  import androidx.annotation.Nullable;
  
  import com.bumptech.glide.Glide;
  import com.bumptech.glide.load.engine.DiskCacheStrategy;
  
  public class ImageVideoView extends LinearLayout {
  
      Context mContext;
      CustomExoPlayerView customExoPlayerView;
      ImageView imageView;
  
      public ImageVideoView(Context context) {
          super(context);
          initView(context);
      }
  
      public ImageVideoView(Context context, @Nullable AttributeSet attrs) {
          super(context, attrs);
          initView(context);
      }
  
      public ImageVideoView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
          initView(context);
      }
  
      private void initView(Context context) {
          mContext = context;
          setBackgroundColor(Color.BLACK);
      }
  
      //url이 비디오 형식일 경우 exoplayerview를 실행
      //url이 이미지 형식일 경우 imageview load
      public void setUrl(String url) {
          if (isVideo(url)) {
              LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
              customExoPlayerView = new CustomExoPlayerView(mContext);
              customExoPlayerView.setLayoutParams(params);
              addView(customExoPlayerView);
              customExoPlayerView.initializePlayer(url);
          } else {
              LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
              imageView = new ImageView(mContext);
              imageView.setLayoutParams(params);
              addView(imageView);
              Glide.with(this).load(url).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).into(imageView);
          }
      }
  
      //확장자가 비디오인지 이미지인지 확인
      private Boolean isVideo(String path) {
          String extension = getExtension(path);
  
          if (extension.equals("mp4") || extension.equals("MP4") || extension.equals("MOV") || extension.equals("mov") || extension.equals("AVI") || extension.equals("avi") ||
                  extension.equals("MKV") || extension.equals("mkv") || extension.equals("WMV") || extension.equals("wmv") || extension.equals("TS") || extension.equals("ts") ||
                  extension.equals("TP") || extension.equals("tp") || extension.equals("FLV") || extension.equals("flv") || extension.equals("3GP") || extension.equals("3gp") ||
                  extension.equals("MPG") || extension.equals("mpg") || extension.equals("MPEG") || extension.equals("mpeg") || extension.equals("MPE") || extension.equals("mpe") ||
                  extension.equals("ASF") || extension.equals("asf") || extension.equals("ASX") || extension.equals("asx") || extension.equals("DAT") || extension.equals("dat") ||
                  extension.equals("RM") || extension.equals("rm")) {
              return true;
          } else {
              return false;
          }
      }
  
      //확장자 나누기
      private String getExtension(String url) {
          return url.substring(url.lastIndexOf(".") + 1, url.length());
      }
  
      //동영상 해제
      public void releasePlayer() {
          if(customExoPlayerView != null){
              customExoPlayerView.releasePlayer();
          }
      }
  }
  

호출할 떄 받아 온 url의 확장자를 가지고 비디오인지 이미지인지 분류를 해서 이미지일 경우 뷰에 이미지뷰를 넣어주고, 동영상일 경우 뷰에 아까만든 CustomExoPlayerView를 만들어줍니다.

 

호출 방법

activity_main.xml

  
  <?xml version="1.0" encoding="utf-8"?>
  <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      tools:context=".MainActivity">
  
      <com.example.imagevideoviewtest.ImageVideoView
          android:id="@+id/imageVideoView"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
  
  </androidx.constraintlayout.widget.ConstraintLayout>
  

 

MainActivity.java

  
  package com.example.imagevideoviewtest;
  
  import androidx.appcompat.app.AppCompatActivity;
  
  import android.os.Bundle;
  
  public class MainActivity extends AppCompatActivity {
  
      ImageVideoView imageVideoView;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
  
          imageVideoView = findViewById(R.id.imageVideoView);
  
          imageVideoView.setUrl("이미지or비디오 url");
      }
  
      @Override
      protected void onPause() {
          super.onPause();
          if(imageVideoView != null){
              imageVideoView.releasePlayer();
          }
      }
  
      @Override
      protected void onDestroy() {
          super.onDestroy();
          if(imageVideoView != null){
              imageVideoView.releasePlayer();
          }
      }
  
      @Override
      protected void onStop() {
          super.onStop();
          if(imageVideoView != null){
              imageVideoView.releasePlayer();
          }
      }
  }
  

ImageVideoView에 아까 생성한 setUrl메소드를 통해 url을 넘겨주게 되면 뷰에서 알아서 이미지인지, 비디오인지 구분을 해 보여주게 됩니다.

 

아까 말했다싶이 Activity가 종료되었을 때 releasePlayer를 통해 동영상을 해제시켜줍니다.

 

아! 여기서 동영상이나 이미지를 인터넷에 있는 url을 가져왔을 경우에는 실행을 해도 화면이 안나올겁니다...

이유는 Android 9.0 Pie에서는 https를 사용하도록 강요합니다. 이전 글에서 보여준 방법을 사용하시면 정상적으로 이미지나 동영상을 재생할 수 있습니다.

2020/01/04 - [Android/유용한 기술] - [Android/안드로이드] Android OS 9 Pie버전에서 http사용하기

 

[Android/안드로이드] Android OS 9 Pie버전에서 http사용하기

API주소나 인터넷에 있는 이미지나 동영상을 재생하기 위해 url을 가져왔는데 이미지나 동영상이 안보일 경우가 있습니다. Android OS 9 Pie버전부터는 "http://"URL 접근이 막혔습니다. 그리고 사용하고 있는 API..

lakue.tistory.com

 

결과화면

위에 사진은 url을 동영상 형식으로 주었을 경우 나온 화면이고, 아래 화면은 이미지 형식으로 주었을 경우에 나온 화면입니다.

 

궁금하신점이나 제가 빠뜨린점이 있다면 댓글 남겨주세요~~

Comments