You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android RecyclerView集成YouTube API滑动播视频黑屏问题

RecyclerView嵌入YouTube播放器滑动黑屏修复方案

核心问题根源

  • 异步拉取频道视频ID的任务执行完成后,没有主动通知RecyclerView适配器数据更新,适配器初始持有的是空列表
  • 所有播放器实例都直接绑定Activity生命周期,没有适配RecyclerView的条目复用逻辑,离屏播放器未被正确暂停/重置,新条目播放器初始化时序错乱
  • 未配置单页滑动吸附效果,滑动结束后无法精准定位到单个全屏条目,自动播放逻辑无有效触发时机
  • ViewHolder复用时旧的播放器监听器未移除,onReady回调可能拿到上一个条目的视频ID,导致加载异常
  • 不需要继承YouTubeBaseActivity,这是谷歌官方旧播放器库的要求,和你用的第三方android-youtube-player库无关,反而可能引发资源冲突

分步修复代码

1. 改造YouTubeActivity

把适配器提成成员变量,异步任务拿到数据后主动刷新;添加PagerSnapHelper实现单页滑动吸附;添加滑动监听,滑动停止后自动播放当前完全可见的视频,暂停其他非可见视频。

public class YouTubeActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private String apiKey;
    private String playListID;
    private ArrayList<String> channelVideoIDs;
    private RecyclerViewAdapter recyclerViewAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_you_tube);

        playListID = getString(R.string.youtube_playlist_id);
        apiKey = getString(R.string.youtube_api_key);
        channelVideoIDs = new ArrayList<>();
        initViews();
        new GetPlayListVideosTask().execute(); 
    }

    private void initViews(){
        recyclerView = findViewById(R.id.youtube_recyclerview);
        recyclerView.setHasFixedSize(true);
        LinearLayoutManager layoutManager = new LinearLayoutManager(YouTubeActivity.this, RecyclerView.VERTICAL, false);
        recyclerView.setLayoutManager(layoutManager);

        // 添加单页吸附效果,实现整页滑动定位
        PagerSnapHelper pagerSnapHelper = new PagerSnapHelper();
        pagerSnapHelper.attachToRecyclerView(recyclerView);

        recyclerViewAdapter = new RecyclerViewAdapter(channelVideoIDs, this.getLifecycle());
        recyclerView.setAdapter(recyclerViewAdapter);

        // 滑动停止后自动播放当前可见视频
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    int visiblePos = layoutManager.findFirstCompletelyVisibleItemPosition();
                    if (visiblePos != RecyclerView.NO_POSITION) {
                        recyclerViewAdapter.playVideoAtPosition(visiblePos);
                    }
                }
            }
        });
    }

    // 异步拉取视频ID任务
    private class GetPlayListVideosTask extends AsyncTask<Void, Void, ArrayList<String>> {
        @Override
        protected ArrayList<String> doInBackground(Void... voids) {
            // 替换为你原有拉取视频ID的逻辑
            return fetchVideoIdList();
        }

        @Override
        protected void onPostExecute(ArrayList<String> videoIds) {
            super.onPostExecute(videoIds);
            channelVideoIDs.clear();
            channelVideoIDs.addAll(videoIds);
            recyclerViewAdapter.notifyDataSetChanged();
            // 数据加载完成后自动播放第一个视频
            recyclerView.post(() -> recyclerViewAdapter.playVideoAtPosition(0));
        }
    }
}

2. 改造RecyclerViewAdapter

修复复用时的监听器残留问题,添加指定位置播放/全局暂停的方法,回收条目时主动释放播放器资源避免黑屏。

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
    private ArrayList<String> videoIDList;
    private Lifecycle lifecycle;
    private int currentPlayingPos = -1;
    private WeakReference<RecyclerView> recyclerViewRef;

    public RecyclerViewAdapter(ArrayList<String> videoIds, Lifecycle lifecycle) {
        this.videoIDList = videoIds;
        this.lifecycle = lifecycle;
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        recyclerViewRef = new WeakReference<>(recyclerView);
    }

    @NonNull
    @Override
    public RecyclerViewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View v = inflater.inflate(R.layout.single_video_layout, parent, false);
        YouTubePlayerView youTubePlayerView = v.findViewById(R.id.youTubePlayerView);
        lifecycle.addObserver(youTubePlayerView);
        return new ViewHolder(youTubePlayerView);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
        // 绑定时预加载视频,不自动播放
        viewHolder.cueVideo(videoIDList.get(position));
    }

    @Override
    public void onViewRecycled(@NonNull ViewHolder holder) {
        super.onViewRecycled(holder);
        // 条目回收时暂停播放,避免黑屏和声音泄漏
        holder.pause();
    }

    @Override
    public int getItemCount() {
        return videoIDList.size();
    }

    // 播放指定位置视频,暂停其他所有视频
    public void playVideoAtPosition(int position) {
        RecyclerView recyclerView = recyclerViewRef.get();
        if (recyclerView == null || currentPlayingPos == position) return;
        currentPlayingPos = position;
        
        for (int i = 0; i < getItemCount(); i++) {
            RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(i);
            if (holder instanceof ViewHolder) {
                if (i == position) {
                    ((ViewHolder) holder).play();
                } else {
                    ((ViewHolder) holder).pause();
                }
            }
        }
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        private YouTubePlayerView youTubePlayerView;
        private YouTubePlayer youTubePlayer;
        private String currentVideoId;
        private AbstractYouTubePlayerListener playerListener;

        ViewHolder(YouTubePlayerView playerView) {
            super(playerView);
            youTubePlayerView = playerView;
            // 单例化监听器,避免重复添加导致回调错乱
            playerListener = new AbstractYouTubePlayerListener() {
                @Override
                public void onReady(@NonNull YouTubePlayer initializedYouTubePlayer) {
                    youTubePlayer = initializedYouTubePlayer;
                    youTubePlayer.cueVideo(currentVideoId, 0);
                }
            };
            youTubePlayerView.addYouTubePlayerListener(playerListener);
        }

        // 预加载视频不自动播放
        void cueVideo(String videoId) {
            currentVideoId = videoId;
            if(youTubePlayer != null) {
                youTubePlayer.cueVideo(videoId, 0);
            }
        }

        // 播放当前视频
        void play() {
            if (youTubePlayer != null && currentVideoId != null) {
                youTubePlayer.play();
            }
        }

        // 暂停当前视频
        void pause() {
            if (youTubePlayer != null) {
                youTubePlayer.pause();
            }
        }
    }
}

验证注意事项

  • 依赖使用最新稳定版的android-youtube-player,测试请用真机,模拟器WebView兼容问题经常导致黑屏
  • 确保YouTube API密钥已经开启YouTube Data API v3权限,且控制台配置的包名、签名SHA1和当前测试包一致
  • 不需要给播放器加额外的父布局,item布局直接放YouTubePlayerView占满全屏即可

内容的提问来源于stack exchange,提问作者Wasif Hamdani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 01:45:44