class MediaAdapter(
private val mediaList: List<VideoItem>,
private val activity: FragmentActivity // Use FragmentActivity context for lifecycle support
) : RecyclerView.Adapter<MediaAdapter.MediaViewHolder>() {
class MediaViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val thumbnail: ImageView = itemView.findViewById(R.id.mediaThumbnail)
val title: TextView = itemView.findViewById(R.id.mediaTitle)
val description: TextView = itemView.findViewById(R.id.mediaDescription)
val webView: WebView = itemView.findViewById(R.id.webView)
val btnWebFullScreen: ImageView = itemView.findViewById(R.id.fullscreenIcon)
val playIcon: ImageView = itemView.findViewById(R.id.playIcon)
var youtubePlayerView: YouTubePlayerView = itemView.findViewById(R.id.youtube_player_view)
val progressBar: ProgressBar = itemView.findViewById(R.id.progressBar)
var youtubePlayerDuration:Float = 0.0F
init {
// Register FullscreenListener
youtubePlayerView.addFullscreenListener(object : FullscreenListener {
override fun onEnterFullscreen(fullscreenView: View, exitFullscreen: () -> Unit) {
// Handle entering fullscreen
println("full screen listener")
// Extract the video ID or URL to pass it to the full-screen activity
val videoId = youtubePlayerView.tag as? String
println("video Id url... $videoId")
if (videoId != null) {
println("duration id seconds $youtubePlayerDuration")
val tracker = YouTubePlayerTracker()
youtubePlayerView.addYouTubePlayerListener(tracker)
tracker.state
tracker.currentSecond
tracker.videoDuration
tracker.videoId
println("duration value ${tracker.currentSecond}")
println("current State ${tracker.state}")
println("current videoId ${tracker.videoId}")
val intent = Intent(itemView.context, FullScreenYoutubeViewActivity::class.java).apply {
putExtra("VIDEO_SOURCE", "YOUTUBE")
putExtra("VIDEO_ID_OR_URL", videoId)
putExtra("VIDEO_SEC",youtubePlayerDuration)
}
itemView.context.startActivity(intent)
} else {
Toast.makeText(itemView.context, "Unable to enter fullscreen mode", Toast.LENGTH_SHORT).show()
}
}
override fun onExitFullscreen() {
// Handle exiting fullscreen
}
})
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.row_item_media, parent, false)
return MediaViewHolder(view)
}
override fun onBindViewHolder(holder: MediaViewHolder, position: Int) {
val videoItem = mediaList[position]
holder.webView.settings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
holder.webView.visibility = View.GONE
holder.youtubePlayerView.visibility = View.GONE
holder.thumbnail.visibility = View.VISIBLE
// Load the thumbnail image (e.g., using Glide)
Glide.with(activity)
.load(videoItem.thumbnail)
.into(holder.thumbnail)
holder.title.text = videoItem.title
holder.description.text = videoItem.description
holder.webView.setBackgroundColor(Color.TRANSPARENT)
// Determine if the URL is an embedded video or direct MP4 link
val isEmbeddedVideo = videoItem.videoUrl.contains("www.youtube.com")
if (!isEmbeddedVideo) {
loadWebViewVideo(holder, videoItem)
} else {
loadYouTubeVideo(holder, videoItem.videoUrl)
}
}
private fun loadWebViewVideo(holder: MediaViewHolder, videoItem: VideoItem) {
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
holder.progressBar.visibility = View.VISIBLE // Show the loader
holder.thumbnail.visibility = View.VISIBLE
holder.webView.visibility = View.VISIBLE
holder.webView.settings.javaScriptEnabled = true
holder.webView.settings.mediaPlaybackRequiresUserGesture = false
holder.webView.settings.cacheMode = WebSettings.LOAD_DEFAULT
// Check if the WebView is already displaying the correct content
if (holder.webView.url != videoItem.videoUrl) {
holder.webView.loadUrl(videoItem.videoUrl)
}
holder.webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
holder.progressBar.visibility = View.GONE
holder.thumbnail.visibility = View.GONE
// holder.webView.visibility = View.VISIBLE
}
}
holder.webView.webChromeClient = object : WebChromeClient() {
override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
if (view is FrameLayout) {
enterFullscreenMode(view, callback, videoItem.videoUrl)
}
}
override fun onHideCustomView() {
exitFullscreenMode()
}
}
holder.btnWebFullScreen.setOnClickListener {
holder.webView.evaluateJavascript("(function(){return document.querySelector('video').currentTime;})()") { result ->
val currentPosition = result.toDoubleOrNull() ?: 0.0
val intent = Intent(activity, FullscreenVideoActivity::class.java).apply {
putExtra("videoUrl", videoItem.videoUrl)
putExtra("playbackPosition", currentPosition)
}
activity.startActivity(intent)
}
}
}
// new video play play only clicking the button
private fun loadYouTubeVideo(holder: MediaViewHolder, videoUrlItem: String) {
holder.progressBar.visibility = View.VISIBLE
try {
val videoId = extractVideoId(videoUrlItem)
holder.youtubePlayerView.enableAutomaticInitialization = false
if (videoId != null) {
val youtubePlayerListener = object : AbstractYouTubePlayerListener() {
override fun onReady(youTubePlayer: YouTubePlayer) {
if (holder.youtubePlayerView.tag != videoId) {
holder.progressBar.visibility = View.GONE // Hide the loader
holder.youtubePlayerView.tag = videoId
holder.thumbnail.visibility = View.VISIBLE
holder.youtubePlayerView.visibility = View.GONE
holder.webView.visibility = View.GONE
// Set up play button click listener
holder.playIcon.visibility = View.VISIBLE
holder.playIcon.setOnClickListener {
holder.thumbnail.visibility = View.GONE
holder.playIcon.visibility = View.GONE
holder.youtubePlayerView.visibility = View.VISIBLE
youTubePlayer.cueVideo(videoId, 0f) // Cue the video at 0 seconds
}
}
}
}
val iFramePlayerOptions = IFramePlayerOptions.Builder()
.controls(1)
.fullscreen(1)
.build()
holder.youtubePlayerView.initialize(youtubePlayerListener, iFramePlayerOptions)
} else {
Toast.makeText(activity, "Invalid YouTube URL", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
println("Exception: $e")
}
}
private fun extractVideoId(url: String): String? {
return when {
url.contains("youtube.com/watch") -> {
val pattern = Regex("v=([^&]+)")
pattern.find(url)?.groupValues?.get(1)
}
url.contains("youtu.be/") -> {
val pattern = Regex("youtu\\.be/([^?&]+)")
pattern.find(url)?.groupValues?.get(1)
}
url.contains("youtube.com/embed/") -> {
val pattern = Regex("embed/([^?&]+)")
pattern.find(url)?.groupValues?.get(1)
}
else -> null
}
}
override fun getItemCount(): Int {
return mediaList.size
}
private fun enterFullscreenMode(
view: View?,
callback: WebChromeClient.CustomViewCallback?,
videoUrl: String
) {
val intent = Intent(activity, FullscreenVideoActivity::class.java).apply {
putExtra("videoUrl", videoUrl)
}
activity.startActivity(intent)
}
private fun exitFullscreenMode() {
println("exit full screen")
}
}
在此代码中,我的 YouTube 播放器视频加载第一次正确播放,但是当我向下滚动并再次向上滚动时,列表中第一个播放的视频仍在加载,并且需要更多时间加载,有时它不播放视频 YouTube 播放器中的问题是什么视频实现请查看代码 这里我使用这个库来实现 实现(“com.pierfrancescosoffritti.androidyoutubeplayer:核心:12.1.0”)
RecyclerView 适配器中的 YouTube 播放器初始化 确保在绑定视图时 YouTubePlayerView 已正确初始化。
类 YoutubeVideoAdapter(private val videoList: List) : RecyclerView.Adapter
inner class YoutubeViewHolder(val binding: ItemYoutubeBinding) : RecyclerView.ViewHolder(binding.root), LifecycleObserver {
fun bind(videoId: String) {
val youTubePlayerView = binding.youtubePlayerView
lifecycle.addObserver(youTubePlayerView) // Add lifecycle observer to handle properly
youTubePlayerView.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {
override fun onReady(youTubePlayer: YouTubePlayer) {
youTubePlayer.cueVideo(videoId, 0f)
}
})
}
fun releasePlayer() {
binding.youtubePlayerView.release()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): YoutubeViewHolder {
val binding = ItemYoutubeBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return YoutubeViewHolder(binding)
}
override fun onBindViewHolder(holder: YoutubeViewHolder, position: Int) {
holder.bind(videoList[position])
}
override fun onViewRecycled(holder: YoutubeViewHolder) {
super.onViewRecycled(holder)
holder.releasePlayer() // Release player when the view is recycled
}
override fun getItemCount() = videoList.size
}
使用生命周期感知的 YouTubePlayerView 确保您的 YouTubePlayerView 具有生命周期感知能力,可以顺利处理初始化、释放和播放。
在您的 Activity 或 Fragment 中:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.adapter = YoutubeVideoAdapter(videoList)
lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
recyclerView.children.forEach { child ->
val playerView = child.findViewById<YouTubePlayerView>(R.id.youtubePlayerView)
playerView?.release() // Release player when the fragment/activity is paused
}
}
})
}
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { // Load the video when scrolling stops recyclerView.children.forEach { child -> val playerView = child.findViewById<YouTubePlayerView>(R.id.youtubePlayerView) if (playerView != null && !playerView.isInitialized) { playerView.getYouTubePlayerWhenReady { player -> // Initialize YouTube player if necessary } } } } else { // Pause or release videos when scrolling to avoid lag recyclerView.children.forEach { child -> val playerView = child.findViewById<YouTubePlayerView>(R.id.youtubePlayerView) playerView?.release() } } } })
在 AndroidManifest.xml 中:
xml
<application
android:hardwareAccelerated="true"
... >
</application>