文章目录
前言
在与后端初步联调,测试完毕视频的上传功能后,由于算法部分尚未完成,因此先暂缓了需要进行联调的部分功能。这段时间内,我将精力主要放在了视频播放功能的实现上。
安卓端有一些开源的视频播放器项目,由于当前尚未对视频播放功能有特殊的需求,这里直接使用了开源项目 dkplayer 的视频播放器组件,而自己编写控制视频播放的相关代码。
一、Dkplayer 自定义控制组件
首先引用开源项目
implementation 'com.github.dueeeke.dkplayer:dkplayer-java:3.2.6'
网上有关 Dkplayer 的使用说明的资料比较少,在初步阅读开源项目源码后,这里进行一下总结:
Dkplayer 真正的视频播放器类是 VideoView
,同时 VideoViewManager
作为视频播放器管理类,可以管理并配置视频播放器。但是仅仅使用 VideoView,只能实现简单的视频播放,而不能进行一些调节进度条、调整亮度等更自由的操作。而 Dkplayer 提供了一个类 ControlWrapper,可以通过自定义控制组件,来调用封装好的视频播放控制类的 api ,根据需求完善视频播放功能。
这里我主要定义了5个控制组件:
控制组件类 | 作用 |
---|---|
PrepareView | 播放器的初始状态,准备播放界面 |
TitleView | 视频播放器的顶部控制栏,主要包括标题、时间、电量以及返回操作 |
VodControlView | 视频播放器的底部控制栏,包括进度条、播放时间、全屏控制 |
GestureView | 视频播放器的手势控制,包括拖动快进快退、亮度、音量等控制 |
CompleteView | 视频播放完毕后的界面 |
PrepareView
PrepareView 对应的页面显示了视频的封面图片,以及播放图标,点击图标后,便调用接口,播放视频
public class PrepareView extends FrameLayout implements IControlComponent {
private ControlWrapper mControlWrapper;
private ImageView mThumb;
private ImageView mStartPlay;
private ProgressBar mLoading;
public PrepareView(@NonNull Context context) {
super(context);
}
public PrepareView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public PrepareView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
LayoutInflater.from(getContext()).inflate(R.layout.player_layout_prepare_view, this, true);
mThumb = findViewById(R.id.thumb);
mStartPlay = findViewById(R.id.start_play);
mLoading = findViewById(R.id.loading);
findViewById(R.id.status_btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 设置是否在移动网络下直接播放视频
VideoViewManager.instance().setPlayOnMobileNetwork(true);
mControlWrapper.start();
}
});
}
/**
* 设置点击此界面开始播放
*/
public void setClickStart() {
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mControlWrapper.start();
}
});
}
@Override
public void attach(@NonNull ControlWrapper controlWrapper) {
mControlWrapper = controlWrapper;
}
@Override
public View getView() {
return this;
}
@Override
public void onVisibilityChanged(boolean isVisible, Animation anim) {
}
@Override
public void onPlayStateChanged(int playState) {
switch (playState) {
case VideoView.STATE_PREPARING:
bringToFront();
setVisibility(VISIBLE);
mStartPlay.setVisibility(View.GONE);
mLoading.setVisibility(View.VISIBLE);
break;
case VideoView.STATE_PLAYING:
case VideoView.STATE_PAUSED:
case VideoView.STATE_ERROR:
case VideoView.STATE_BUFFERING:
case VideoView.STATE_BUFFERED:
case VideoView.STATE_PLAYBACK_COMPLETED:
setVisibility(GONE);
break;
case VideoView.STATE_IDLE:
setVisibility(VISIBLE);
bringToFront();
mLoading.setVisibility(View.GONE);
mStartPlay.setVisibility(View.VISIBLE);
mThumb.setVisibility(View.VISIBLE);
break;
case VideoView.STATE_START_ABORT:
setVisibility(VISIBLE);
break;
}
}
@Override
public void onPlayerStateChanged(int playerState) {
}
@Override
public void setProgress(int duration, int position) {
}
@Override
public void onLockStateChanged(boolean isLocked) {
}
}
TitleView
TitleView 实现的功能主要如下:
- 在页面初始注册 BatteryReceiver,获取手机电量信息,显示在顶部栏中,在离开页面时取消注册
- 获取系统时间、视频标题等信息,显示在顶部栏中
- 点击返回箭头后,退出全屏
- 点击锁定图标后,锁定屏幕,并隐藏顶部底部栏
public class TitleView extends FrameLayout implements IControlComponent {
private ControlWrapper mControlWrapper;
private LinearLayout mTitleContainer;
private TextView mTitle;
private TextView mSysTime;//系统当前时间
private BatteryReceiver mBatteryReceiver;
private boolean mIsRegister;//是否注册BatteryReceiver
public TitleView(@NonNull Context context) {
super(context);
}
public TitleView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TitleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
setVisibility(GONE);
LayoutInflater.from(getContext()).inflate(R.layout.player_layout_title_view, this, true);
mTitleContainer = findViewById(R.id.title_container);
ImageView back = findViewById(R.id.back);
// 点击返回后,退出全屏
back.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Activity activity = PlayerUtils.scanForActivity(getContext());
if (activity != null && mControlWrapper.isFullScreen()) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mControlWrapper.stopFullScreen();
}
}
});
mTitle = findViewById(R.id.title);
mSysTime = findViewById(R.id.sys_time);
//电量
ImageView batteryLevel = findViewById(R.id.iv_battery);
mBatteryReceiver = new BatteryReceiver(batteryLevel);
}
public void setTitle(String title) {
mTitle.setText(title);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// View 与 Window 分离的时候要取消注册 BatteryReceiver
if (mIsRegister) {
getContext().unregisterReceiver(mBatteryReceiver);
mIsRegister = false;
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!mIsRegister) {
getContext().registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
mIsRegister = true;
}
}
@Override
public void attach(@NonNull ControlWrapper controlWrapper) {
mControlWrapper = controlWrapper;
}
@Override
public View getView() {
return this;
}
@Override
public void onVisibilityChanged(boolean isVisible, Animation anim) {
//只在全屏时才有效
if (!mControlWrapper.isFullScreen()) return;
if (isVisible) {
if (getVisibility() == GONE) {
mSysTime.setText(PlayerUtils.getCurrentSystemTime());
setVisibility(VISIBLE);
if (anim != null) {
startAnimation(anim);
}
}
} else {
if (getVisibility() == VISIBLE) {
setVisibility(GONE);
if (anim != null) {
startAnimation(anim);
}
}
}
}
@Override
public void onPlayStateChanged(int playState) {
switch (playState) {
case VideoView.STATE_IDLE:
case VideoView.STATE_START_ABORT:
case VideoView.STATE_PREPARING:
case VideoView.STATE_PREPARED:
case VideoView.STATE_ERROR: