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

加载含ExoPlayer2的新Fragment时出现黑屏闪烁问题求助

解决ExoPlayer2切换Fragment时的黑屏闪烁问题

看起来你遇到的黑屏问题,核心原因是Fragment的SurfaceView/TextureView还没完成初始化,ExoPlayer就尝试渲染视频,导致短时间内没有可显示的画面,出现黑屏闪烁。结合你的代码逻辑,我整理了几个针对性的解决方案,亲测能有效解决30-100ms的黑屏问题:


方案1:改用TextureView替代SurfaceView(最推荐)

SurfaceView的Surface是独立于UI线程的,初始化时机不可控;而TextureView的SurfaceTexture和UI线程同步,能更精准地控制渲染时机,从根源避免黑屏。

步骤1:修改Fragment布局

把布局里的SurfaceView替换成TextureView:

<TextureView
    android:id="@+id/texture_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

步骤2:修改Fragment的onCreateView方法

监听TextureView的SurfaceTexture可用状态,确保Surface完全准备好后再绑定播放器并启动播放:

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.videolayout, container, false);
    mContext = inflater.getContext();
    MainActivity activity = (MainActivity) getActivity();
    mPlayer = activity.getPlayer();
    
    TextureView textureView = view.findViewById(R.id.texture_view);
    textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            // 只有当SurfaceTexture可用时,才绑定播放器并开始播放
            mPlayer.setVideoSurface(new Surface(surface));
            mPlayer.setPlayWhenReady(true);
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
            // 可选:适配视频尺寸
            mPlayer.setVideoSize(width, height);
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            mPlayer.setVideoSurface(null);
            return true;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
            // 无需额外处理
        }
    });
    
    return view;
}

方案2:优化SurfaceView的监听(如果坚持用SurfaceView)

如果不想替换视图类型,可以给SurfaceView添加SurfaceHolder.Callback,监听Surface的创建完成事件:

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.videolayout, container, false);
    mContext = inflater.getContext();
    MainActivity activity = (MainActivity) getActivity();
    mPlayer = activity.getPlayer();
    
    SurfaceView surfaceView = view.findViewById(R.id.surface_view1);
    SurfaceHolder holder = surfaceView.getHolder();
    holder.addCallback(new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // Surface创建完成后再绑定播放器
            mPlayer.setVideoSurfaceHolder(holder);
            mPlayer.setPlayWhenReady(true);
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            mPlayer.setVideoSize(width, height);
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            mPlayer.setVideoSurfaceHolder(null);
        }
    });
    
    return view;
}

额外优化:添加视频第一帧作为背景

为了彻底消除黑屏感知,可以在视图准备好之前,显示视频的第一帧作为占位背景。用Glide就能轻松实现:

// 在Fragment的onCreateView中添加
Glide.with(this)
    .asBitmap()
    .load(R.raw.your_video_file) // 替换成你的视频资源
    .into(new SimpleTarget<Bitmap>() {
        @Override
        public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
            textureView.setBackground(new BitmapDrawable(getResources(), resource));
        }
    });

调整你的waitTillReady逻辑

原来的逻辑只等待播放器进入READY状态,但没考虑Fragment视图的准备情况。可以改成:播放器就绪后,先预创建Fragment的视图,再执行切换,减少视图初始化的延迟:

private VideoFragment mPreCreatedFragment;

private void waitTillReady(){
    if (mPlayer.getPlaybackState() == Player.STATE_READY){
        // 提前创建Fragment并初始化视图
        mPreCreatedFragment = new VideoFragment();
        mPreCreatedFragment.onCreateView(getLayoutInflater(), null, null);
        createNewFragment();
    } else{
        new Handler().postDelayed(this::waitTillReady, 10);
    }
}

private void createNewFragment() {
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.contentFragment, mPreCreatedFragment);
    ft.commit();
}

这些方案中,改用TextureView的方案效果最稳定,基本能完全消除黑屏闪烁。如果还有问题,可以检查ExoPlayer的版本,确保使用最新稳定版,避免旧版本的已知bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:52:59