Android VideoView 视频播放器 仿抖音

前言

最近项目有个需求 , 做个类似抖音的视频效果. 又因为包大小的问题不使用第三方SDK,所以使用原生的VideoView开发了一下, 搭配RecyclerView和PageSnapHelper来实现抖音的效果.

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

全部代码: github

看一下实现流程:
首先创建了一个继承自ConstraintLayout的View,用来实现自己的布局.东西不多, 里面主要是播放控制的按钮, 和一个可以拖动的进度条.在视频加载成功之前显示一个封面图.

然后创建了一个State的enum类:

    public enum VideoState {
        unKnow,
        loadFinish,
        playing,
        playEnd,
        error,
        pause
    }

这是主要是用于在视频进度有变化的时候用来保存一下状态 .

之后创建了一个handler , 用于实时更新进度条,以及已播放的时间.

    @SuppressLint("HandlerLeak")
    private final Handler mHandler = new Handler() {

        @SuppressLint("SetTextI18n")
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == UPDATE_PROGRESS) {
                if (videoView.isPlaying()) {
                    int currentTime = videoView.getCurrentPosition();
                    if (currentTime >= mDuration) {
                        videoView.seekTo(0);
                        seekBarProgress.setProgress(0);
                        alreadyTextView.setText("00:00");
                        mHandler.removeMessages(UPDATE_PROGRESS);
                    } else {
                        seekBarProgress.setProgress(currentTime);
                        mHandler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 500);
                        alreadyTextView.setText(TimeUtil.formatTimeWhichExist(currentTime));
                    }
                }
            }
        }
    };

然后实现一个SeekBar 的监听 ,
3个函数分别是:

  • OnSeekBarChangeListener: 进度条有改变
  • onStartTrackingTouch 手指按下进度条
  • onStopTrackingTouch 手指离开进度条
 private SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            alreadyTextView.setText(TimeUtil.formatTimeWhichExist(progress));
            if (mOnProgressChangedListener != null) {
                mOnProgressChangedListener.onProgressChanged(progress);
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            // 暂停刷新
            mHandler.removeMessages(UPDATE_PROGRESS);
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            int progress = seekBar.getProgress();
            if (videoView != null) {
                if (progress + 1000 < mDuration) {
                    // 设置当前播放的位置
                    videoView.seekTo(progress);
                    mHandler.sendEmptyMessage(UPDATE_PROGRESS);
                } else {
                    mVideoState = VideoState.playEnd;
                    start();
                }
            }
        }
    };

在按下的时候 , 停止跟随视频进度改变, 离开时 , 从当前位置继续播放.
但是视频是有关键帧的, 所以可能会跳到前一个关键帧的位置播放.
在进度有改变的时候, 实时改变已播放的时间的textview.

在播放器初始化的时候, 监听一下视频播放.

  • setOnPreparedListener 是去加载视频 , 有一个加载完成时的回调, 这里在加载完成时, 隐藏封面并开始播放.
  • setOnCompletionListener 是播放完成时的回调,此时改变进度并重新播放视频.
  • setOnErrorListener 是视频播放出错的回调
 videoView.setOnPreparedListener(mp -> {
            mVideoState = VideoState.loadFinish;
            mHandler.sendEmptyMessage(UPDATE_PROGRESS);
            totalPlayTextView.setText(Util.formatTimeWhichExist(mDuration));
            videoThumb.setVisibility(GONE);
            start();
        });

        videoView.setOnCompletionListener(mp -> {
            mHandler.removeMessages(UPDATE_PROGRESS);
            mVideoState = VideoState.playEnd;
            changePlayIcon();
            seekBarProgress.setProgress(0);
            alreadyTextView.setText("00:00");
            videoThumb.setVisibility(VISIBLE);
        });

        videoView.setOnErrorListener((mp, what, extra) -> {
            //异常回调
            mVideoState = VideoState.error;
            return false;
        });

之后定义了一些操控视频的方法 , 像播放,暂停,获取播放进度等等的public方法, 用来给外界调用.

 public void start() {
        if (mVideoState == VideoState.playEnd) {
            videoView.resume();
        } else {
            videoView.start();
        }
        mVideoState = VideoState.playing;
        changePlayIcon();
        mHandler.sendEmptyMessage(UPDATE_PROGRESS);
    }

    public void pause() {
        videoView.pause();
        mVideoState = VideoState.pause;
        changePlayIcon();
    }

    public void stop() {
        videoView.stopPlayback();
        mHandler.removeMessages(UPDATE_PROGRESS);
    }

    public void setVideoVisible() {
        controllerLayout.setVisibility(VISIBLE);
    }

    public void setVideoGone() {
        controllerLayout.setVisibility(GONE);
    }

    public VideoState getState() {
        if (videoView.isPlaying()) {
            mVideoState = VideoState.playing;
        }
        return mVideoState;
    }

    public int getCurrentPosition() {
        return videoView.getCurrentPosition();
    }

设置视频数据:

    public void setVideo(VideoInfo video) {
        try {
            videoView.setVideoURI(Uri.parse(video.getUrl()));
            mDuration = (int) video.getDuration();
            seekBarProgress.setMax(mDuration);
            start();

            //宽高比
            int width = video.getWidth();
            int height = video.getHeight();

            float aspectRatio = (float) width / height;
            ConstraintLayout.LayoutParams layoutParamsThumb = (ConstraintLayout.LayoutParams) videoThumb.getLayoutParams(); //取控件textView当前的布局参数
            setLayoutParam(layoutParamsThumb, aspectRatio);
            videoThumb.setLayoutParams(layoutParamsThumb);
        } catch (Throwable ignore) {
        }
    }

这里是播放的网络视频, 所以传进来的是一个url , 并且根据宽高比显示封面图 , 使其和视频的位置和大小是一致的.

其他代码应该都不需要介绍了, 详细了解可以看看github

猜你喜欢

转载自blog.csdn.net/u011272795/article/details/103276744