前言
在与后端初步联调,测试完毕视频的上传功能后,由于算法部分尚未完成,因此先暂缓了需要进行联调的部分功能。这段时间内,我将精力主要放在了视频播放功能的实现上。
安卓端有一些开源的视频播放器项目,由于当前尚未对视频播放功能有特殊的需求,这里直接使用了开源项目 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;
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();
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:
case VideoView.STATE_PLAYBACK_COMPLETED:
setVisibility(GONE);
break;
}
}
@Override
public void onPlayerStateChanged(int playerState) {
if (playerState == VideoView.PLAYER_FULL_SCREEN) {
if (mControlWrapper.isShowing() && !mControlWrapper.isLocked()) {
setVisibility(VISIBLE);
mSysTime.setText(PlayerUtils.getCurrentSystemTime());
}
mTitle.setSelected(true);
} else {
setVisibility(GONE);
mTitle.setSelected(false);
}
Activity activity = PlayerUtils.scanForActivity(getContext());
if (activity != null && mControlWrapper.hasCutout()) {
int orientation = activity.getRequestedOrientation();
int cutoutHeight = mControlWrapper.getCutoutHeight();
if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
mTitleContainer.setPadding(0, 0, 0, 0);
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
mTitleContainer.setPadding(cutoutHeight, 0, 0, 0);
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
mTitleContainer.setPadding(0, 0, cutoutHeight, 0);
}
}
}
@Override
public void setProgress(int duration, int position) {
}
@Override
public void onLockStateChanged(boolean isLocked) {
if (isLocked) {
setVisibility(GONE);
} else {
setVisibility(VISIBLE);
mSysTime.setText(PlayerUtils.getCurrentSystemTime());
}
}
private static class BatteryReceiver extends BroadcastReceiver {
private ImageView pow;
public BatteryReceiver(ImageView pow) {
this.pow = pow;
}
@Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if (extras == null) return;
int current = extras.getInt("level");
int total = extras.getInt("scale");
int percent = current * 100 / total;
pow.getDrawable().setLevel(percent);
}
}
}
VodControlView
VodControlView 实现的功能主要如下:
- 在底部设置进度条和拖动条,随视频播放进行变化,并允许拖动改变视频播放位置
- 点击视频播放暂停按钮,控制视频播放
- 提供全屏按钮,控制视频全屏播放
public class VodControlView extends FrameLayout implements IControlComponent, View.OnClickListener, SeekBar.OnSeekBarChangeListener {
protected ControlWrapper mControlWrapper;
private TextView mTotalTime, mCurrTime;
private ImageView mFullScreen;
private LinearLayout mBottomContainer;
private SeekBar mVideoProgress;
private ProgressBar mBottomProgress;
private ImageView mPlayButton;
private boolean mIsDragging;
private boolean mIsShowBottomProgress = true;
public VodControlView(@NonNull Context context) {
super(context);
}
public VodControlView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public VodControlView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
setVisibility(GONE);
LayoutInflater.from(getContext()).inflate(getLayoutId(), this, true);
mFullScreen = findViewById(R.id.fullscreen);
mFullScreen.setOnClickListener(this);
mBottomContainer = findViewById(R.id.bottom_container);
mVideoProgress = findViewById(R.id.seekBar);
mVideoProgress.setOnSeekBarChangeListener(this);
mTotalTime = findViewById(R.id.total_time);
mCurrTime = findViewById(R.id.curr_time);
mPlayButton = findViewById(R.id.iv_play);
mPlayButton.setOnClickListener(this);
mBottomProgress = findViewById(R.id.bottom_progress);
}
protected int getLayoutId() {
return R.layout.player_layout_vod_control_view;
}
public void showBottomProgress(boolean isShow) {
mIsShowBottomProgress = isShow;
}
@Override
public void attach(@NonNull ControlWrapper controlWrapper) {
mControlWrapper = controlWrapper;
}
@Override
public View getView() {
return this;
}
@Override
public void onVisibilityChanged(boolean isVisible, Animation anim) {
if (isVisible) {
mBottomContainer.setVisibility(VISIBLE);
if (anim != null) {
mBottomContainer.startAnimation(anim);
}
if (mIsShowBottomProgress) {
mBottomProgress.setVisibility(GONE);
}
} else {
mBottomContainer.setVisibility(GONE);
if (anim != null) {
mBottomContainer.startAnimation(anim);
}
if (mIsShowBottomProgress) {
mBottomProgress.setVisibility(VISIBLE);
AlphaAnimation animation = new AlphaAnimation(0f, 1f);
animation.setDuration(300);
mBottomProgress.startAnimation(animation);
}
}
}
@Override
public void onPlayStateChanged(int playState) {
switch (playState) {
case VideoView.STATE_IDLE:
case VideoView.STATE_PLAYBACK_COMPLETED:
setVisibility(GONE);
mBottomProgress.setProgress(0);
mBottomProgress.setSecondaryProgress(0);
mVideoProgress.setProgress(0);
mVideoProgress.setSecondaryProgress(0);
break;
case VideoView.STATE_START_ABORT:
case VideoView.STATE_PREPARING:
case VideoView.STATE_PREPARED:
case VideoView.STATE_ERROR:
setVisibility(GONE);
break;
case VideoView.STATE_PLAYING:
mPlayButton.setSelected(true);
if (mIsShowBottomProgress) {
if (mControlWrapper.isShowing()) {
mBottomProgress.setVisibility(GONE);
mBottomContainer.setVisibility(VISIBLE);
} else {
mBottomContainer.setVisibility(GONE);
mBottomProgress.setVisibility(VISIBLE);
}
} else {
mBottomContainer.setVisibility(GONE);
}
setVisibility(VISIBLE);
mControlWrapper.startProgress();
break;
case VideoView.STATE_PAUSED:
mPlayButton.setSelected(false);
break;
case VideoView.STATE_BUFFERING:
case VideoView.STATE_BUFFERED:
mPlayButton.setSelected(mControlWrapper.isPlaying());
break;
}
}
@Override
public void onPlayerStateChanged(int playerState) {
switch (playerState) {
case VideoView.PLAYER_NORMAL:
mFullScreen.setSelected(false);
break;
case VideoView.PLAYER_FULL_SCREEN:
mFullScreen.setSelected(true);
break;
}
Activity activity = PlayerUtils.scanForActivity(getContext());
if (activity != null && mControlWrapper.hasCutout()) {
int orientation = activity.getRequestedOrientation();
int cutoutHeight = mControlWrapper.getCutoutHeight();
if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
mBottomContainer.setPadding(0, 0, 0, 0);
mBottomProgress.setPadding(0, 0, 0, 0);
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
mBottomContainer.setPadding(cutoutHeight, 0, 0, 0);
mBottomProgress.setPadding(cutoutHeight, 0, 0, 0);
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
mBottomContainer.setPadding(0, 0, cutoutHeight, 0);
mBottomProgress.setPadding(0, 0, cutoutHeight, 0);
}
}
}
@Override
public void setProgress(int duration, int position) {
if (mIsDragging) {
return;
}
if (mVideoProgress != null) {
if (duration > 0) {
mVideoProgress.setEnabled(true);
int pos = (int) (position * 1.0 / duration * mVideoProgress.getMax());
mVideoProgress.setProgress(pos);
mBottomProgress.setProgress(pos);
} else {
mVideoProgress.setEnabled(false);
}
int percent = mControlWrapper.getBufferedPercentage();
if (percent >= 95) {
mVideoProgress.setSecondaryProgress(mVideoProgress.getMax());
mBottomProgress.setSecondaryProgress(mBottomProgress.getMax());
} else {
mVideoProgress.setSecondaryProgress(percent * 10);
mBottomProgress.setSecondaryProgress(percent * 10);
}
}
if (mTotalTime != null)
mTotalTime.setText(stringForTime(duration));
if (mCurrTime != null)
mCurrTime.setText(stringForTime(position));
}
@Override
public void onLockStateChanged(boolean isLocked) {
onVisibilityChanged(!isLocked, null);
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.fullscreen) {
toggleFullScreen();
} else if (id == R.id.iv_play) {
mControlWrapper.togglePlay();
}
}
private void toggleFullScreen() {
Activity activity = PlayerUtils.scanForActivity(getContext());
mControlWrapper.toggleFullScreen(activity);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mIsDragging = true;
mControlWrapper.stopProgress();
mControlWrapper.stopFadeOut();
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
long duration = mControlWrapper.getDuration();
long newPosition = (duration * seekBar.getProgress()) / mVideoProgress.getMax();
mControlWrapper.seekTo((int) newPosition);
mIsDragging = false;
mControlWrapper.startProgress();
mControlWrapper.startFadeOut();
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (!fromUser) {
return;
}
long duration = mControlWrapper.getDuration();
long newPosition = (duration * progress) / mVideoProgress.getMax();
if (mCurrTime != null)
mCurrTime.setText(stringForTime((int) newPosition));
}
}
GestureView
GestureView 所提供的主要功能有:
- 屏幕中央前后滑动,快速调整视频播放进度
- 屏幕左侧上下滑动,可以调节亮度
- 屏幕右侧上下滑动,可以调节音量
public class GestureView extends FrameLayout implements IGestureComponent {
public GestureView(@NonNull Context context) {
super(context);
}
public GestureView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public GestureView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private ControlWrapper mControlWrapper;
private ImageView mIcon;
private ProgressBar mProgressPercent;
private TextView mTextPercent;
private LinearLayout mCenterContainer;
{
setVisibility(GONE);
LayoutInflater.from(getContext()).inflate(R.layout.player_layout_gesture_control_view, this, true);
mIcon = findViewById(R.id.iv_icon);
mProgressPercent = findViewById(R.id.pro_percent);
mTextPercent = findViewById(R.id.tv_percent);
mCenterContainer = findViewById(R.id.center_container);
}
@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 onPlayerStateChanged(int playerState) {
}
@Override
public void onStartSlide() {
mControlWrapper.hide();
mCenterContainer.setVisibility(VISIBLE);
mCenterContainer.setAlpha(1f);
}
@Override
public void onStopSlide() {
mCenterContainer.animate()
.alpha(0f)
.setDuration(300)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mCenterContainer.setVisibility(GONE);
}
})
.start();
}
@Override
public void onPositionChange(int slidePosition, int currentPosition, int duration) {
mProgressPercent.setVisibility(GONE);
if (slidePosition > currentPosition) {
mIcon.setImageResource(R.drawable.dkplayer_ic_action_fast_forward);
} else {
mIcon.setImageResource(R.drawable.dkplayer_ic_action_fast_rewind);
}
mTextPercent.setText(String.format("%s/%s", stringForTime(slidePosition), stringForTime(duration)));
}
@Override
public void onBrightnessChange(int percent) {
mProgressPercent.setVisibility(VISIBLE);
mIcon.setImageResource(R.drawable.dkplayer_ic_action_brightness);
mTextPercent.setText(percent + "%");
mProgressPercent.setProgress(percent);
}
@Override
public void onVolumeChange(int percent) {
mProgressPercent.setVisibility(VISIBLE);
if (percent <= 0) {
mIcon.setImageResource(R.drawable.dkplayer_ic_action_volume_off);
} else {
mIcon.setImageResource(R.drawable.dkplayer_ic_action_volume_up);
}
mTextPercent.setText(percent + "%");
mProgressPercent.setProgress(percent);
}
@Override
public void onPlayStateChanged(int playState) {
if (playState == VideoView.STATE_IDLE
|| playState == VideoView.STATE_START_ABORT
|| playState == VideoView.STATE_PREPARING
|| playState == VideoView.STATE_PREPARED
|| playState == VideoView.STATE_ERROR
|| playState == VideoView.STATE_PLAYBACK_COMPLETED) {
setVisibility(GONE);
} else {
setVisibility(VISIBLE);
}
}
@Override
public void setProgress(int duration, int position) {
}
@Override
public void onLockStateChanged(boolean isLock) {
}
}
CompleteView
在播放完成后,会出现重新播放的按钮,如果用户点击,则将重新播放视频。此外,如果当前处于全屏状态,也会出现返回按钮,以退出全屏
public class CompleteView extends FrameLayout implements IControlComponent {
private ControlWrapper mControlWrapper;
private ImageView mStopFullscreen;
public CompleteView(@NonNull Context context) {
super(context);
}
public CompleteView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CompleteView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
setVisibility(GONE);
LayoutInflater.from(getContext()).inflate(R.layout.player_layout_complete_view, this, true);
findViewById(R.id.iv_replay).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mControlWrapper.replay(true);
}
});
mStopFullscreen = findViewById(R.id.stop_fullscreen);
mStopFullscreen.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mControlWrapper.isFullScreen()) {
Activity activity = PlayerUtils.scanForActivity(getContext());
if (activity != null && !activity.isFinishing()) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mControlWrapper.stopFullScreen();
}
}
}
});
setClickable(true);
}
@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) {
if (playState == VideoView.STATE_PLAYBACK_COMPLETED) {
setVisibility(VISIBLE);
mStopFullscreen.setVisibility(mControlWrapper.isFullScreen() ? VISIBLE : GONE);
bringToFront();
} else {
setVisibility(GONE);
}
}
@Override
public void onPlayerStateChanged(int playerState) {
if (playerState == VideoView.PLAYER_FULL_SCREEN) {
mStopFullscreen.setVisibility(VISIBLE);
} else if (playerState == VideoView.PLAYER_NORMAL) {
mStopFullscreen.setVisibility(GONE);
}
Activity activity = PlayerUtils.scanForActivity(getContext());
if (activity != null && mControlWrapper.hasCutout()) {
int orientation = activity.getRequestedOrientation();
int cutoutHeight = mControlWrapper.getCutoutHeight();
LayoutParams sflp = (LayoutParams) mStopFullscreen.getLayoutParams();
if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
sflp.setMargins(0, 0, 0, 0);
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
sflp.setMargins(cutoutHeight, 0, 0, 0);
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
sflp.setMargins(0, 0, 0, 0);
}
}
}
@Override
public void setProgress(int duration, int position) {
}
@Override
public void onLockStateChanged(boolean isLock) {
}
}
二、视频播放器的使用
StandardVideoController
在定义好各个播放控制组件后,仍需要一个总控制器,将各个控制组件组合,来对视频播放进行管理,并且监听 VideoView 的状态变化、调整视图方向、控制锁定状态等。
public class StandardVideoController extends GestureVideoController implements View.OnClickListener {
protected ImageView mLockButton;
protected ProgressBar mLoadingProgress;
public StandardVideoController(@NonNull Context context) {
this(context, null);
}
public StandardVideoController(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public StandardVideoController(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected int getLayoutId() {
return R.layout.player_layout_standard_controller;
}
@Override
protected void initView() {
super.initView();
mLockButton = findViewById(R.id.lock);
mLockButton.setOnClickListener(this);
mLoadingProgress = findViewById(R.id.loading);
}
public void addDefaultControlComponent(String title) {
CompleteView completeView = new CompleteView(getContext());
PrepareView prepareView = new PrepareView(getContext());
prepareView.setClickStart();
TitleView titleView = new TitleView(getContext());
titleView.setTitle(title);
addControlComponent(completeView, prepareView, titleView);
addControlComponent(new VodControlView(getContext()));
addControlComponent(new GestureView(getContext()));
}
@Override
public void onClick(View v) {
int i = v.getId();
if (i == R.id.lock) {
mControlWrapper.toggleLockState();
}
}
@Override
protected void onLockStateChanged(boolean isLocked) {
if (isLocked) {
mLockButton.setSelected(true);
Toast.makeText(getContext(), R.string.dkplayer_locked, Toast.LENGTH_SHORT).show();
} else {
mLockButton.setSelected(false);
Toast.makeText(getContext(), R.string.dkplayer_unlocked, Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onVisibilityChanged(boolean isVisible, Animation anim) {
if (mControlWrapper.isFullScreen()) {
if (isVisible) {
if (mLockButton.getVisibility() == GONE) {
mLockButton.setVisibility(VISIBLE);
if (anim != null) {
mLockButton.startAnimation(anim);
}
}
} else {
mLockButton.setVisibility(GONE);
if (anim != null) {
mLockButton.startAnimation(anim);
}
}
}
}
@Override
protected void onPlayerStateChanged(int playerState) {
super.onPlayerStateChanged(playerState);
switch (playerState) {
case VideoView.PLAYER_NORMAL:
setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mLockButton.setVisibility(GONE);
break;
case VideoView.PLAYER_FULL_SCREEN:
if (isShowing()) {
mLockButton.setVisibility(VISIBLE);
} else {
mLockButton.setVisibility(GONE);
}
break;
}
if (mActivity != null && hasCutout()) {
int orientation = mActivity.getRequestedOrientation();
int dp24 = PlayerUtils.dp2px(getContext(), 24);
int cutoutHeight = getCutoutHeight();
if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
FrameLayout.LayoutParams lblp = (FrameLayout.LayoutParams) mLockButton.getLayoutParams();
lblp.setMargins(dp24, 0, dp24, 0);
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mLockButton.getLayoutParams();
layoutParams.setMargins(dp24 + cutoutHeight, 0, dp24 + cutoutHeight, 0);
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mLockButton.getLayoutParams();
layoutParams.setMargins(dp24, 0, dp24, 0);
}
}
}
@Override
protected void onPlayStateChanged(int playState) {
super.onPlayStateChanged(playState);
switch (playState) {
case VideoView.STATE_IDLE:
mLockButton.setSelected(false);
mLoadingProgress.setVisibility(GONE);
break;
case VideoView.STATE_PLAYING:
case VideoView.STATE_PAUSED:
case VideoView.STATE_PREPARED:
case VideoView.STATE_ERROR:
case VideoView.STATE_BUFFERED:
mLoadingProgress.setVisibility(GONE);
break;
case VideoView.STATE_PREPARING:
case VideoView.STATE_BUFFERING:
mLoadingProgress.setVisibility(VISIBLE);
break;
case VideoView.STATE_PLAYBACK_COMPLETED:
mLoadingProgress.setVisibility(GONE);
mLockButton.setVisibility(GONE);
mLockButton.setSelected(false);
break;
}
}
@Override
public boolean onBackPressed() {
if (isLocked()) {
show();
Toast.makeText(getContext(), R.string.dkplayer_lock_tip, Toast.LENGTH_SHORT).show();
return true;
}
if (mControlWrapper.isFullScreen()) {
return stopFullScreen();
}
return super.onBackPressed();
}
}
VideoView 的使用
在控制组件都编程结束后,就可以在项目中使用 VideoView 了。 首先要在对应的 xml 文件中对其定义
<com.dueeeke.videoplayer.player.VideoView
android:id="@+id/player"
android:layout_width="match_parent"
android:layout_height="300dp"
app:layout_constraintDimensionRatio="16:10"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
而后定义视频播放控制组件,以及总控制器
StandardVideoController controller = new StandardVideoController(this.getContext());
controller.setEnableOrientation(true);
PrepareView prepareView = new PrepareView(this.getContext());
prepareView.setClickStart();
ImageView thumb = prepareView.findViewById(R.id.thumb);
Glide.with(this).load(THUMB).into(thumb);
controller.addControlComponent(prepareView);
controller.addControlComponent(new CompleteView(this.getContext()));
controller.addControlComponent(new ErrorView(this.getContext()));
TitleView titleView = new TitleView(this.getContext());
controller.addControlComponent(titleView);
VodControlView vodControlView = new VodControlView(this.getContext());
controller.addControlComponent(vodControlView);
GestureView gestureControlView = new GestureView(this.getContext());
controller.addControlComponent(gestureControlView);
此外要定义一个监听器,监听视频播放器的状态
private VideoView.OnStateChangeListener mOnStateChangeListener = new VideoView.SimpleOnStateChangeListener() {
@Override
public void onPlayerStateChanged(int playerState) {
switch (playerState) {
case VideoView.PLAYER_NORMAL://小屏
break;
case VideoView.PLAYER_FULL_SCREEN://全屏
break;
}
}
@Override
public void onPlayStateChanged(int playState) {
switch (playState) {
case VideoView.STATE_IDLE:
break;
case VideoView.STATE_PREPARING:
break;
case VideoView.STATE_PREPARED:
break;
case VideoView.STATE_PLAYING:
int[] videoSize = mVideoView.getVideoSize();
break;
case VideoView.STATE_PAUSED:
break;
case VideoView.STATE_BUFFERING:
break;
case VideoView.STATE_BUFFERED:
break;
case VideoView.STATE_PLAYBACK_COMPLETED:
break;
case VideoView.STATE_ERROR:
break;
}
}
};
为 VideoView 设置对应的控制器与监听器,开始播放视频
mVideoView.setVideoController(controller);
mVideoView.setUrl(VideoUrl);
mVideoView.addOnStateChangeListener(mOnStateChangeListener);
mVideoView.start();
注意在离开页面时,对 VideoView 进行资源释放
@Override
public void onPause() {
super.onPause();
if (mVideoView.getCurrentPlayState() == VideoView.STATE_PREPARING) {
mVideoView.release();
}
}
|