您的位置:首页 > 移动开发 > Android开发

Android VideoView 视频播放器 仿抖音

2020-07-12 16:21 351 查看

前言

最近项目有个需求 , 做个类似抖音的视频效果. 又因为包大小的问题不使用第三方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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: