Android自定义YouTubePlayerView视频加载后突然暂停问题排查
嘿,我帮你分析下这个YouTubePlayerView突然暂停没法继续播放的问题,结合你贴的布局代码,大概率是这几个坑导致的,咱们一个个来解决:
1. 上层布局拦截了触摸/焦点事件
你在FrameLayout里叠了多层LinearLayout(水印、标题栏、控制栏),这些上层布局默认是可以获取焦点或者拦截触摸事件的,会干扰YouTubePlayerView的内部交互逻辑,导致播放突然中断。
解决办法:
给所有上层的容器布局(除了需要处理点击的控件)添加这两个属性,让事件能透传到下层的YouTubePlayerView:
android:clickable="false" android:focusable="false"
比如你的水印布局、标题布局,还有控制栏的外层LinearLayout都加上这两个属性;而控制栏里的播放按钮(ImageView)单独设置android:clickable="true"和android:focusable="true",确保点击事件能被正常处理。
2. 生命周期没和YouTubePlayer正确绑定
如果你的Activity/Fragment发生配置变化(比如屏幕旋转)或者后台切前台时,没有正确保存和恢复YouTubePlayer的实例及播放状态,就会导致Player失效,暂停后没法继续播放。
解决办法:
- 保存播放状态:在Activity的
onSaveInstanceState里记录当前播放位置和播放状态; - 初始化时恢复状态:在
YouTubePlayer.OnInitializedListener的onInitializationSuccess方法里,根据保存的状态恢复播放。
示例代码:
private YouTubePlayer mYoutubePlayer; private boolean isPlaying = false; private int currentPlayPosition = 0; private static final String KEY_POSITION = "play_position"; private static final String KEY_IS_PLAYING = "is_playing"; @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mYoutubePlayer != null) { currentPlayPosition = mYoutubePlayer.getCurrentTimeMillis(); isPlaying = mYoutubePlayer.isPlaying(); } outState.putInt(KEY_POSITION, currentPlayPosition); outState.putBoolean(KEY_IS_PLAYING, isPlaying); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.your_layout); // 恢复保存的状态 if (savedInstanceState != null) { currentPlayPosition = savedInstanceState.getInt(KEY_POSITION); isPlaying = savedInstanceState.getBoolean(KEY_IS_PLAYING); } // 初始化YouTubePlayerView YouTubePlayerView playerView = findViewById(R.id.demo_youtubeplayer); playerView.initialize(YOUR_YOUTUBE_API_KEY, new YouTubePlayer.OnInitializedListener() { @Override public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer player, boolean wasRestored) { mYoutubePlayer = player; if (!wasRestored) { // 加载视频并跳转到保存的位置 player.loadVideo(YOUR_VIDEO_ID, currentPlayPosition); } // 如果之前是播放状态,恢复播放 if (isPlaying) { player.play(); } // 监听播放状态变化,更新UI和状态记录 player.setPlayerStateChangeListener(new YouTubePlayer.PlayerStateChangeListener() { @Override public void onVideoStarted() { isPlaying = true; } @Override public void onVideoPaused() { isPlaying = false; } // 其他回调方法按需实现 @Override public void onVideoEnded() {} @Override public void onLoading() {} @Override public void onLoaded(String s) {} @Override public void onError(YouTubePlayer.ErrorReason errorReason) {} }); } @Override public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult errorReason) { // 处理初始化失败,比如弹提示 } }); }
3. SeekBar同步逻辑有问题
如果SeekBar的进度更新和YouTubePlayer的播放状态不同步,或者拖动SeekBar时没有正确判断是用户手动操作,可能会触发Player的异常状态。
解决办法:
- 用
YouTubePlayer.PlaybackEventListener监听播放进度,定时更新SeekBar和时间文本; - 处理SeekBar拖动时,只在
fromUser为true时才调用player.seekToMillis(),避免循环触发。
示例代码:
// 在初始化成功后设置播放进度监听 mYoutubePlayer.setPlaybackEventListener(new YouTubePlayer.PlaybackEventListener() { private Handler mProgressHandler = new Handler(Looper.getMainLooper()); private Runnable mProgressRunnable; @Override public void onPlaying() { isPlaying = true; // 启动定时更新进度 mProgressRunnable = new Runnable() { @Override public void run() { if (mYoutubePlayer != null && isPlaying) { int currentMillis = mYoutubePlayer.getCurrentTimeMillis(); int totalMillis = mYoutubePlayer.getDurationMillis(); if (totalMillis > 0) { int progress = (int) ((currentMillis * 100.0) / totalMillis); SeekBar seekBar = findViewById(R.id.video_seekbar); seekBar.setProgress(progress); // 更新时间显示 TextView timeTv = findViewById(R.id.play_timetv); timeTv.setText(formatTime(currentMillis) + "/" + formatTime(totalMillis)); } mProgressHandler.postDelayed(this, 1000); } } }; mProgressHandler.post(mProgressRunnable); } @Override public void onPaused() { isPlaying = false; // 停止进度更新 if (mProgressRunnable != null) { mProgressHandler.removeCallbacks(mProgressRunnable); } } // 其他回调方法按需实现 @Override public void onStopped() {} @Override public void onBuffering(boolean b) {} @Override public void onSeekTo(int i) {} }); // 处理SeekBar拖动事件 SeekBar seekBar = findViewById(R.id.video_seekbar); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser && mYoutubePlayer != null) { int totalMillis = mYoutubePlayer.getDurationMillis(); int seekPosition = (int) ((progress * totalMillis) / 100.0); mYoutubePlayer.seekToMillis(seekPosition); } } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }); // 格式化时间的工具方法 private String formatTime(int millis) { int seconds = millis / 1000; int minutes = seconds / 60; seconds = seconds % 60; return String.format("%02d:%02d", minutes, seconds); }
4. YouTubePlayerView的焦点问题
YouTubePlayerView需要获取焦点才能维持正常播放,上层布局可能抢占了焦点,导致Player进入暂停状态。
解决办法:
在XML里给YouTubePlayerView添加焦点属性:
android:focusable="true" android:focusableInTouchMode="true"
然后在代码初始化后,调用playerView.requestFocus()让它获取焦点。
优化后的布局示例
我帮你调整了布局,把YouTubePlayerView放在最底层,给上层容器都加上了事件透传的属性,你可以参考:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black"> <!-- YouTubePlayerView 放在最底层,确保能获取焦点 --> <com.google.android.youtube.player.YouTubePlayerView android:id="@+id/demo_youtubeplayer" android:layout_width="match_parent" android:layout_height="match_parent" android:focusable="true" android:focusableInTouchMode="true" /> <!-- 水印布局,允许事件透传 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:orientation="vertical" android:clickable="false" android:focusable="false"> <TextView android:id="@+id/water_mark" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left|center" android:layout_marginLeft="30dp" android:alpha="0.50" android:padding="5dp" android:text="@string/water_mark" android:textColor="#66ffffff" android:textSize="20sp" /> </LinearLayout> <!-- 标题布局,允许事件透传 --> <LinearLayout android:id="@+id/video_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" android:background="@android:color/transparent" android:gravity="center_vertical" android:orientation="vertical" android:clickable="false" android:focusable="false"> <TextView android:id="@+id/youtubeview_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:ellipsize="marquee" android:fadingEdge="horizontal" android:focusable="false" android:focusableInTouchMode="false" android:padding="15dp" android:scrollHorizontally="true" android:singleLine="true" android:text="Medium Text" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@android:color/white" /> </LinearLayout> <!-- 控制栏布局,允许事件透传,仅播放按钮可点击 --> <LinearLayout android:id="@+id/video_control" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:background="@android:color/transparent" android:gravity="center_vertical" android:orientation="horizontal" android:clickable="false" android:focusable="false"> <ImageView android:id="@+id/youtube_playimg" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_weight="1" android:background="@null" android:src="@drawable/ic_media_play" android:clickable="true" android:focusable="true" /> <SeekBar android:id="@+id/video_seekbar" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="10dp" android:layout_weight="6" android:max="100" android:progress="0" /> <TextView android:id="@+id/play_timetv" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_weight="2" android:text="--:--" android:textColor="@android:color/white" /> </LinearLayout> </FrameLayout>
内容的提问来源于stack exchange,提问作者Sakthi Vel

