관리 메뉴

나만을 위한 블로그

[Android] 유튜브 라이브러리로 영상을 전체화면으로 재생하는 방법 + 리사이클러뷰에서 웹뷰로 여러 영상 재생하기 본문

Android

[Android] 유튜브 라이브러리로 영상을 전체화면으로 재생하는 방법 + 리사이클러뷰에서 웹뷰로 여러 영상 재생하기

참깨빵위에참깨빵 2022. 8. 8. 22:43
728x90
반응형

내가 만든 앱에서 유튜브 영상을 재생하는 기능을 만들 때 그냥 재생만 하는 게 아니라 전체 화면으로도 재생시키고 싶을 수 있다.

그러나 이와 관련된 예제를 찾아도 찾기가 어려운데 어떻게든 찾아서 구현했다. 액티비티에서 하나의 영상을 재생하는 코드와 리사이클러뷰에 여러 영상을 띄우고 한 영상을 재생한 다음 전체화면으로 띄우는 코드를 포스팅한다.

 

먼저 액티비티에 하나의 유튜브 플레이어를 만들고 전체화면으로 재생시키는 예제다. 매니페스트에서 영상을 재생할 액티비티에 아래 코드를 넣는다. 

 

android:configChanges="orientation|screenSize|keyboardHidden|smallestScreenSize|screenLayout"

 

내 경우 영상을 재생시킬 액티비티의 매니페스트 태그는 이렇게 만들었다.

 

<activity
    android:name=".youtube.YoutubeTestActivity"
    android:exported="true"
    android:configChanges="orientation|screenSize|keyboardHidden|smallestScreenSize|screenLayout">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

 

그리고 아래 의존성을 앱 수준 gradle에 넣어준다.

 

implementation 'com.pierfrancescosoffritti.androidyoutubeplayer:chromecast-sender:0.26'

 

그 다음 아래 링크에서 파일을 다운받아 압축해제해서 "YoutubeAndroidPlayerApi.jar" 파일을 얻는다. 아니면 아래 파일을 다운로드하고 app 밑에 libs 폴더를 만든 다음 그곳에 넣어준다.

 

YouTubeAndroidPlayerApi.jar
0.10MB

 

파일을 넣었다면 이렇게 보여야 한다. 왼쪽 위를 보면 보통 Android로 되어 있는데 이걸 클릭해서 Project로 바꾼 다음 폴더를 만들고 그 안에 Ctrl + V 해도 된다.

 

 

이렇게 하면 기본 준비는 끝났다. 이제 액티비티 XML부터 만든다.

 

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".youtube.YoutubeTestActivity">

        <com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
            android:id="@+id/youTubePlayerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:autoPlay="false"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.example.kotlinprac.R
import com.example.kotlinprac.databinding.ActivityYoutubeTestBinding
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.YouTubePlayerFullScreenListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.YouTubePlayerListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.options.IFramePlayerOptions
import com.pierfrancescosoffritti.androidyoutubeplayer.core.ui.DefaultPlayerUiController

const val VIDEO_ID = "egTPUCVRIUI"

class YoutubeTestActivity : AppCompatActivity() {

    private val TAG = this.javaClass.simpleName
    private lateinit var binding: ActivityYoutubeTestBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_youtube_test)
        binding.run {
            val options = IFramePlayerOptions.Builder().controls(0).build()
            youTubePlayerView.enableAutomaticInitialization = false

            val listener: YouTubePlayerListener = object : AbstractYouTubePlayerListener() {
                override fun onReady(youTubePlayer: YouTubePlayer) {
                    // using pre-made custom ui
                    val defaultPlayerUiController = DefaultPlayerUiController(binding.youTubePlayerView, youTubePlayer)
                    binding.youTubePlayerView.setCustomPlayerUi(defaultPlayerUiController.rootView)
                    youTubePlayer.cueVideo(VIDEO_ID, 0F)
                }
            }
            youTubePlayerView.initialize(listener, options)
            youTubePlayerView.addFullScreenListener(object : YouTubePlayerFullScreenListener {
                override fun onYouTubePlayerEnterFullScreen() {
                    youTubePlayerView.enterFullScreen()
                }

                override fun onYouTubePlayerExitFullScreen() {
                    youTubePlayerView.exitFullScreen()
                }
            })
        }
    }

}

 

하나씩 확인해보자. 먼저 아래 코드 부분은 유튜브 영상 위에 시크바, 현재 재생 시간, 전체화면 아이콘이 있는 영상 밑 하단 바를 만드는 코드다. 전체화면 기능을 구현하려면 필수다.

 

val options = IFramePlayerOptions.Builder().controls(0).build()

 

이와 관련해선 라이브러리 공식문서 깃허브를 참고하는 게 좋다.

 

https://github.com/PierfrancescoSoffritti/android-youtube-player#create-your-own-custom-ui

 

GitHub - PierfrancescoSoffritti/android-youtube-player: YouTube Player library for Android and Chromecast, stable and customizab

YouTube Player library for Android and Chromecast, stable and customizable. - GitHub - PierfrancescoSoffritti/android-youtube-player: YouTube Player library for Android and Chromecast, stable and c...

github.com

커스텀은 이 라이브러리의 중요한 측면이다. 필요한 경우 플레이어의 기본 UI를 완전히 바꿀 수 있다...(중략)...새 UI가 플레이어 위에 오버레이된다. 이 때문에 IFramePlayerOptions로 YoutubePlayerView를 초기화해서 IFrame 플레이어 UI를 비활성화하는 것이 좋다

 

이 options 프로퍼티는 YoutubePlayerListener 초기화 코드 밑의 youtubePlayerView.initialize()의 2번 매개변수로 넘기면 된다. 1번 매개변수는 바로 위에서 정의한 YoutubePlayerListener 인터페이스다.

 

val listener: YouTubePlayerListener = object : AbstractYouTubePlayerListener() {
    override fun onReady(youTubePlayer: YouTubePlayer) {
        // using pre-made custom ui
        val defaultPlayerUiController = DefaultPlayerUiController(binding.youTubePlayerView, youTubePlayer)
        binding.youTubePlayerView.setCustomPlayerUi(defaultPlayerUiController.rootView)
        youTubePlayer.cueVideo(VIDEO_ID, 0F)
    }
}

 

setCustomPlayerUi()에 대해선 아래를 참고하자.

 

이 메서드는 레이아웃 또는 뷰의 정의를 포함하는 일반 XML 파일인 레이아웃 리소스의 ID(=XML 안의 YoutubePlayerView의 id값)를 받는다. 이 때문에 IFramePlayerOptions로 YoutubePlayerView를 초기화해서 IFrame 플레이어 UI를 비활성화하는 게 좋다

 

그 다음은 전체화면 설정을 위해 addFullScreenListener()를 구현한다. 전체화면이 설정되고 해제됐을 때 처리를 이 안에 넣으면 된다. 난 함수 2개만 넣었다.

 

youTubePlayerView.addFullScreenListener(object : YouTubePlayerFullScreenListener {
    override fun onYouTubePlayerEnterFullScreen() {
        youTubePlayerView.enterFullScreen()
    }

    override fun onYouTubePlayerExitFullScreen() {
        youTubePlayerView.exitFullScreen()
    }
})

 

이렇게 해서 앱을 빌드하면 아래와 같이 작동한다. 실행 전 핸드폰 설정에서 세로모드가 아닌 자동 회전 모드로 설정해서 핸드폰을 가로로 기울이면 가로 화면으로 바뀌도록 설정해야 한다.

 

 

영상을 재생하거나 시크바를 움직여 다른 위치로 이동해 재생할 수도 있다.

 

 

전체화면을 누르면 아래처럼 된다.

 

 

화면을 가로로 눕히면 이렇게 된다.

 

 

이제 리사이클러뷰도 확인해본다. 리사이클러뷰의 경우 웹뷰를 활용해서 구현했다.

먼저 아래 클래스를 복붙해서 프로젝트 어딘가에 둔다. 웹뷰를 통해 유튜브 영상을 재생할 때 WebChromeClient의 onShowCustomView(), onHideCustomView()를 재정의해야 전체화면 아이콘이 보이기 때문이다.

 

import android.app.Activity
import android.content.Context
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.webkit.JsResult
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.widget.FrameLayout
import com.example.kotlinprac.R

class ChromeClient(private val mActivity: Activity) : WebChromeClient() {
    private var mCustomView: View? = null
    override fun onJsAlert(view: WebView, url: String, message: String, result: JsResult): Boolean {
        result.confirm()
        return super.onJsAlert(view, url, message, result)
    }

    private var mOriginalOrientation = 0
    private var mFullscreenContainer: FullscreenHolder? = null
    private var mCustomViewCollback: CustomViewCallback? = null
    override fun onShowCustomView(view: View, callback: CustomViewCallback) {
        if (mCustomView != null) {
            callback.onCustomViewHidden()
            return
        }
        mOriginalOrientation = mActivity.requestedOrientation
        val decor = mActivity.window.decorView as FrameLayout
        mFullscreenContainer = FullscreenHolder(mActivity)
        mFullscreenContainer!!.addView(view, ViewGroup.LayoutParams.MATCH_PARENT)
        decor.addView(mFullscreenContainer, ViewGroup.LayoutParams.MATCH_PARENT)
        mCustomView = view
        mCustomViewCollback = callback
        mActivity.requestedOrientation = mOriginalOrientation
    }

    override fun onHideCustomView() {
        if (mCustomView == null) {
            return
        }
        val decor = mActivity.window.decorView as FrameLayout
        decor.removeView(mFullscreenContainer)
        mFullscreenContainer = null
        mCustomView = null
        mCustomViewCollback!!.onCustomViewHidden()
        mActivity.requestedOrientation = mOriginalOrientation
    }

    internal class FullscreenHolder(ctx: Context) : FrameLayout(ctx) {
        override fun onTouchEvent(evt: MotionEvent): Boolean {
            return true
        }

        init {
            setBackgroundColor(ctx.resources.getColor(R.color.black))
        }
    }
}

 

그리고 리사이클러뷰 관련 코드들을 작성한다. 아이템 XML의 이름은 item_youtube.xml이다.

데이터 바인딩으로 다른 처리를 하기 때문에 <data>가 들어가 있는데 XML과 어댑터에서 관련 코드들을 지워도 상관없다.

 

data class YoutubeData(
    val videoId: String
)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="model"
            type="com.example.kotlinprac.youtube.YoutubeData" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:layout_marginBottom="20dp">

        <WebView
            android:id="@+id/youtubePlayerWebView"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            app:autoPlay="false"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import android.webkit.WebViewClient
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlinprac.databinding.ItemYoutubeBinding

class YoutubeAdapter(
    private val context: Context,
    private val list: ArrayList<YoutubeData>
): RecyclerView.Adapter<YoutubeAdapter.YoutubeViewHolder>() {

    private lateinit var binding: ItemYoutubeBinding

    inner class YoutubeViewHolder(
        private val binding: ItemYoutubeBinding
    ): RecyclerView.ViewHolder(binding.root) {
        fun bind(item: YoutubeData) {
            binding.model = item
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): YoutubeViewHolder {
        binding = ItemYoutubeBinding.inflate(LayoutInflater.from(context), parent, false)
        return YoutubeViewHolder(binding)
    }

    override fun onBindViewHolder(holder: YoutubeViewHolder, position: Int) {
        val item = list[position]
        holder.bind(item)

        binding.run {
            youtubePlayerWebView.apply {
                settings.javaScriptEnabled = true
                webViewClient = WebViewClient()
                webChromeClient = ChromeClient((context as YoutubeRecyclerViewActivity))
                setInitialScale(0)
                loadUrl(item.videoId)
            }
        }
    }

    override fun getItemCount(): Int = list.size

}

 

그리고 액티비티에서 리사이클러뷰를 정의하고 초기화한다.

 

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".youtube.YoutubeRecyclerViewActivity">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/youtubeRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.kotlinprac.R
import com.example.kotlinprac.databinding.ActivityYoutubeRecyclerViewBinding

class YoutubeRecyclerViewActivity : AppCompatActivity() {

    private lateinit var binding: ActivityYoutubeRecyclerViewBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_youtube_recycler_view)
        binding.run {
            lifecycleOwner = this@YoutubeRecyclerViewActivity

            val youtubeIdList = arrayListOf(
                YoutubeData("http://www.youtube.com/embed/egTPUCVRIUI"),
                YoutubeData("http://www.youtube.com/embed/uXJbN8wEJ5k"),
                YoutubeData("http://www.youtube.com/embed/CfPxlb8-ZQ0"),
                YoutubeData("http://www.youtube.com/embed/XhLyvAKH1Mc"),
            )

            val youtubeAdapter = YoutubeAdapter(this@YoutubeRecyclerViewActivity, youtubeIdList)
            youtubeRecyclerView.apply {
                layoutManager = LinearLayoutManager(this@YoutubeRecyclerViewActivity)
                setHasFixedSize(true)
                adapter = youtubeAdapter
            }
        }
    }
}

 

중요한 것은 웹뷰로 넘기는 유튜브 링크는 "embed"가 들어가야 한다는 것이다.

보통 유튜브 영상 링크를 보면 watch라고 써 있는데 이 주소를 넘기는 게 아니라 embed로 바꿔서 넘겨야 한다.

모두 복붙하고 실행하면 아래처럼 작동한다.

 

 

실행하면 리사이클러뷰 아이템의 웹뷰에 유튜브 영상들이 재생 준비가 된 상태로 나타난다.

 

 

하나를 선택해서 전체화면을 누르면 위 이미지처럼 전체화면으로 바뀐다. 가로로 긴 영상이기 때문에 저렇게 나오는 걸 볼 수 있다. 이 상태에서 화면을 옆으로 기울이면 아래 화면으로 바뀐다.

 

 

다른 영상들 또한 재생해서 전체화면을 누르면 위처럼 작동한다.

반응형
Comments