package com.android.app_base.widget;
|
|
import android.animation.ValueAnimator;
|
import android.annotation.SuppressLint;
|
import android.app.Activity;
|
import android.content.Context;
|
import android.media.AudioManager;
|
import android.media.MediaPlayer;
|
import android.net.Uri;
|
import android.provider.Settings;
|
import android.text.TextUtils;
|
import android.util.AttributeSet;
|
import android.view.LayoutInflater;
|
import android.view.MotionEvent;
|
import android.view.View;
|
import android.view.ViewConfiguration;
|
import android.view.ViewGroup;
|
import android.view.Window;
|
import android.view.WindowManager;
|
import android.widget.FrameLayout;
|
import android.widget.ImageView;
|
import android.widget.LinearLayout;
|
import android.widget.SeekBar;
|
import android.widget.TextView;
|
import android.widget.VideoView;
|
|
import androidx.annotation.DrawableRes;
|
import androidx.annotation.NonNull;
|
import androidx.annotation.Nullable;
|
import androidx.core.content.ContextCompat;
|
import androidx.lifecycle.Lifecycle;
|
import androidx.lifecycle.LifecycleEventObserver;
|
import androidx.lifecycle.LifecycleOwner;
|
|
import com.airbnb.lottie.LottieAnimationView;
|
import com.android.app_base.R;
|
import com.android.app_base.base.action.ActivityAction;
|
import com.android.app_base.base.dialog.UIDialog;
|
|
import java.io.File;
|
import java.util.Formatter;
|
import java.util.Locale;
|
|
/**
|
* 视频播放控件
|
*/
|
public final class VideoPlayerView extends FrameLayout
|
implements LifecycleEventObserver,
|
SeekBar.OnSeekBarChangeListener,
|
View.OnClickListener, ActivityAction,
|
MediaPlayer.OnPreparedListener,
|
MediaPlayer.OnInfoListener,
|
MediaPlayer.OnCompletionListener,
|
MediaPlayer.OnErrorListener {
|
|
/** 刷新间隔 */
|
private static final int REFRESH_TIME = 1000;
|
/** 面板隐藏间隔 */
|
private static final int CONTROLLER_TIME = 3000;
|
/** 提示对话框隐藏间隔 */
|
private static final int DIALOG_TIME = 500;
|
/** 动画执行时间 */
|
private static final int ANIM_TIME = 500;
|
|
private final ViewGroup mTopLayout;
|
private final TextView mTitleView;
|
private final View mLeftView;
|
|
private final ViewGroup mBottomLayout;
|
private final TextView mPlayTime;
|
private final TextView mTotalTime;
|
private final SeekBar mProgressView;
|
|
private final VideoView mVideoView;
|
private final PlayButton mControlView;
|
private final ImageView mLockView;
|
|
private ViewGroup mMessageLayout;
|
private final LottieAnimationView mLottieView;
|
private final TextView mMessageView;
|
|
/** 视频宽度 */
|
private int mVideoWidth;
|
/** 视频高度 */
|
private int mVideoHeight;
|
|
/** 锁定面板 */
|
private boolean mLockMode;
|
/** 显示面板 */
|
private boolean mControllerShow = false;
|
|
/** 触摸按下的 X 坐标 */
|
private float mViewDownX;
|
/** 触摸按下的 Y 坐标 */
|
private float mViewDownY;
|
/** 手势开关 */
|
private boolean mGestureEnabled;
|
/** 当前播放进度 */
|
private int mCurrentProgress;
|
/** 返回监听器 */
|
@Nullable
|
private OnPlayListener mListener;
|
|
/** 音量管理器 */
|
private final AudioManager mAudioManager;
|
/** 最大音量值 */
|
private int mMaxVoice;
|
/** 当前音量值 */
|
private int mCurrentVolume;
|
/** 当前亮度值 */
|
private float mCurrentBrightness;
|
/** 当前窗口对象 */
|
private Window mWindow;
|
/** 调整秒数 */
|
private int mAdjustSecond;
|
/** 触摸方向 */
|
private int mTouchOrientation = -1;
|
|
public VideoPlayerView(@NonNull Context context) {
|
this(context, null);
|
}
|
|
public VideoPlayerView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
this(context, attrs, 0);
|
}
|
|
public VideoPlayerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
this(context, attrs, defStyleAttr, 0);
|
}
|
|
public VideoPlayerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
super(context, attrs, defStyleAttr, defStyleRes);
|
|
LayoutInflater.from(getContext()).inflate(R.layout.widget_player_view, this, true);
|
mTopLayout = findViewById(R.id.ll_player_view_top);
|
mLeftView = findViewById(R.id.iv_player_view_left);
|
mTitleView = findViewById(R.id.tv_player_view_title);
|
|
mBottomLayout = findViewById(R.id.ll_player_view_bottom);
|
mPlayTime = findViewById(R.id.tv_player_view_play_time);
|
mTotalTime = findViewById(R.id.tv_player_view_total_time);
|
mProgressView = findViewById(R.id.sb_player_view_progress);
|
|
mVideoView = findViewById(R.id.vv_player_view_video);
|
mLockView = findViewById(R.id.iv_player_view_lock);
|
mControlView = findViewById(R.id.iv_player_view_control);
|
|
mMessageLayout = findViewById(R.id.cv_player_view_message);
|
mLottieView = findViewById(R.id.lav_player_view_lottie);
|
mMessageView = findViewById(R.id.tv_player_view_message);
|
|
mLeftView.setOnClickListener(this);
|
mControlView.setOnClickListener(this);
|
mLockView.setOnClickListener(this);
|
this.setOnClickListener(this);
|
|
mProgressView.setOnSeekBarChangeListener(this);
|
|
mVideoView.setOnPreparedListener(this);
|
mVideoView.setOnCompletionListener(this);
|
mVideoView.setOnInfoListener(this);
|
mVideoView.setOnErrorListener(this);
|
|
mAudioManager = ContextCompat.getSystemService(getContext(), AudioManager.class);
|
}
|
|
/**
|
* 设置播放器生命管控(自动回调生命周期方法)
|
*/
|
public void setLifecycleOwner(LifecycleOwner owner) {
|
owner.getLifecycle().addObserver(this);
|
}
|
|
/**
|
* {@link LifecycleEventObserver}
|
*/
|
|
@Override
|
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
|
switch (event) {
|
case ON_RESUME:
|
onResume();
|
break;
|
case ON_PAUSE:
|
onPause();
|
break;
|
case ON_DESTROY:
|
onDestroy();
|
break;
|
default:
|
break;
|
}
|
}
|
|
/**
|
* 设置视频标题
|
*/
|
public void setVideoTitle(CharSequence title) {
|
if (TextUtils.isEmpty(title)) {
|
return;
|
}
|
mTitleView.setText(title);
|
}
|
|
/**
|
* 设置视频源
|
*/
|
public void setVideoSource(File file) {
|
if (file == null || !file.isFile()) {
|
return;
|
}
|
mVideoView.setVideoPath(file.getPath());
|
}
|
|
public void setVideoSource(String url) {
|
if (TextUtils.isEmpty(url)) {
|
return;
|
}
|
mVideoView.setVideoURI(Uri.parse(url));
|
}
|
|
/**
|
* 开始播放
|
*/
|
public void start() {
|
mVideoView.start();
|
mControlView.play();
|
// 延迟隐藏控制面板
|
removeCallbacks(mHideControllerRunnable);
|
postDelayed(mHideControllerRunnable, CONTROLLER_TIME);
|
}
|
|
/**
|
* 暂停播放
|
*/
|
public void pause() {
|
mVideoView.pause();
|
mControlView.pause();
|
// 延迟隐藏控制面板
|
removeCallbacks(mHideControllerRunnable);
|
postDelayed(mHideControllerRunnable, CONTROLLER_TIME);
|
}
|
|
/**
|
* 锁定控制面板
|
*/
|
public void lock() {
|
mLockMode = true;
|
mLockView.setImageResource(R.drawable.video_lock_close_ic);
|
mTopLayout.setVisibility(GONE);
|
mBottomLayout.setVisibility(GONE);
|
mControlView.setVisibility(GONE);
|
// 延迟隐藏控制面板
|
removeCallbacks(mHideControllerRunnable);
|
postDelayed(mHideControllerRunnable, CONTROLLER_TIME);
|
}
|
|
/**
|
* 解锁控制面板
|
*/
|
public void unlock() {
|
mLockMode = false;
|
mLockView.setImageResource(R.drawable.video_lock_open_ic);
|
mTopLayout.setVisibility(VISIBLE);
|
if (mVideoView.isPlaying()) {
|
mBottomLayout.setVisibility(VISIBLE);
|
}
|
mControlView.setVisibility(VISIBLE);
|
// 延迟隐藏控制面板
|
removeCallbacks(mHideControllerRunnable);
|
postDelayed(mHideControllerRunnable, CONTROLLER_TIME);
|
}
|
|
/**
|
* 是否正在播放
|
*/
|
public boolean isPlaying() {
|
return mVideoView.isPlaying();
|
}
|
|
/**
|
* 设置视频播放进度
|
*/
|
public void setProgress(int progress) {
|
if (progress > mVideoView.getDuration()) {
|
progress = mVideoView.getDuration();
|
}
|
// 要跳转的进度必须和当前播放进度相差 1 秒以上
|
if (Math.abs(progress - mVideoView.getCurrentPosition()) > 1000) {
|
mVideoView.seekTo(progress);
|
mProgressView.setProgress(progress);
|
}
|
}
|
|
/**
|
* 获取视频播放进度
|
*/
|
public int getProgress() {
|
return mVideoView.getCurrentPosition();
|
}
|
|
/**
|
* 获取视频的总进度
|
*/
|
public int getDuration() {
|
return mVideoView.getDuration();
|
}
|
|
/**
|
* 设置手势开关
|
*/
|
public void setGestureEnabled(boolean enabled) {
|
mGestureEnabled = enabled;
|
}
|
|
/**
|
* 设置返回监听
|
*/
|
public void setOnPlayListener(@Nullable OnPlayListener listener) {
|
mListener = listener;
|
mLeftView.setVisibility(mListener != null ? VISIBLE : INVISIBLE);
|
}
|
|
/**
|
* 显示面板
|
*/
|
public void showController() {
|
if (mControllerShow) {
|
return;
|
}
|
|
mControllerShow = true;
|
|
ValueAnimator topAnimator = ValueAnimator.ofInt(-mTopLayout.getHeight(), 0);
|
topAnimator.setDuration(ANIM_TIME);
|
topAnimator.addUpdateListener(animation -> {
|
int translationY = (int) animation.getAnimatedValue();
|
mTopLayout.setTranslationY(translationY);
|
if (translationY != -mTopLayout.getHeight()) {
|
return;
|
}
|
|
if (mTopLayout.getVisibility() == INVISIBLE) {
|
mTopLayout.setVisibility(VISIBLE);
|
}
|
});
|
topAnimator.start();
|
|
ValueAnimator bottomAnimator = ValueAnimator.ofInt(mBottomLayout.getHeight(), 0);
|
bottomAnimator.setDuration(ANIM_TIME);
|
bottomAnimator.addUpdateListener(animation -> {
|
int translationY = (int) animation.getAnimatedValue();
|
mBottomLayout.setTranslationY(translationY);
|
if (translationY != mBottomLayout.getHeight()) {
|
return;
|
}
|
|
if (mBottomLayout.getVisibility() == INVISIBLE) {
|
mBottomLayout.setVisibility(VISIBLE);
|
}
|
});
|
bottomAnimator.start();
|
|
ValueAnimator alphaAnimator = ValueAnimator.ofFloat(0f, 1f);
|
alphaAnimator.setDuration(ANIM_TIME);
|
alphaAnimator.addUpdateListener(animation -> {
|
float alpha = (float) animation.getAnimatedValue();
|
mLockView.setAlpha(alpha);
|
mControlView.setAlpha(alpha);
|
if (alpha != 0) {
|
return;
|
}
|
|
if (mLockView.getVisibility() == INVISIBLE) {
|
mLockView.setVisibility(VISIBLE);
|
}
|
if (mControlView.getVisibility() == INVISIBLE) {
|
mControlView.setVisibility(VISIBLE);
|
}
|
});
|
alphaAnimator.start();
|
}
|
|
/**
|
* 隐藏面板
|
*/
|
public void hideController() {
|
if (!mControllerShow) {
|
return;
|
}
|
|
mControllerShow = false;
|
|
ValueAnimator topAnimator = ValueAnimator.ofInt(0, -mTopLayout.getHeight());
|
topAnimator.setDuration(ANIM_TIME);
|
topAnimator.addUpdateListener(animation -> {
|
int translationY = (int) animation.getAnimatedValue();
|
mTopLayout.setTranslationY(translationY);
|
if (translationY != -mTopLayout.getHeight()) {
|
return;
|
}
|
|
if (mTopLayout.getVisibility() == VISIBLE) {
|
mTopLayout.setVisibility(INVISIBLE);
|
}
|
});
|
topAnimator.start();
|
|
ValueAnimator bottomAnimator = ValueAnimator.ofInt(0, mBottomLayout.getHeight());
|
bottomAnimator.setDuration(ANIM_TIME);
|
bottomAnimator.addUpdateListener(animation -> {
|
int translationY = (int) animation.getAnimatedValue();
|
mBottomLayout.setTranslationY(translationY);
|
if (translationY != mBottomLayout.getHeight()) {
|
return;
|
}
|
|
if (mBottomLayout.getVisibility() == VISIBLE) {
|
mBottomLayout.setVisibility(INVISIBLE);
|
}
|
});
|
bottomAnimator.start();
|
|
ValueAnimator alphaAnimator = ValueAnimator.ofFloat(1f, 0f);
|
alphaAnimator.setDuration(ANIM_TIME);
|
alphaAnimator.addUpdateListener(animation -> {
|
float alpha = (float) animation.getAnimatedValue();
|
mLockView.setAlpha(alpha);
|
mControlView.setAlpha(alpha);
|
if (alpha != 0) {
|
return;
|
}
|
|
if (mLockView.getVisibility() == VISIBLE) {
|
mLockView.setVisibility(INVISIBLE);
|
}
|
if (mControlView.getVisibility() == VISIBLE) {
|
mControlView.setVisibility(INVISIBLE);
|
}
|
});
|
alphaAnimator.start();
|
}
|
|
public void onResume() {
|
mVideoView.resume();
|
}
|
|
public void onPause() {
|
mVideoView.suspend();
|
pause();
|
}
|
|
public void onDestroy() {
|
mVideoView.stopPlayback();
|
removeCallbacks(mRefreshRunnable);
|
removeCallbacks(mShowControllerRunnable);
|
removeCallbacks(mHideControllerRunnable);
|
removeCallbacks(mShowMessageRunnable);
|
removeCallbacks(mHideMessageRunnable);
|
removeAllViews();
|
}
|
|
@Override
|
protected void onWindowVisibilityChanged(int visibility) {
|
// 这里解释一下 onWindowVisibilityChanged 方法调用的时机
|
// 从前台返回到后台:先调用 onWindowVisibilityChanged(View.INVISIBLE) 后调用 onWindowVisibilityChanged(View.GONE)
|
// 从后台返回到前台:先调用 onWindowVisibilityChanged(View.INVISIBLE) 后调用 onWindowVisibilityChanged(View.VISIBLE)
|
super.onWindowVisibilityChanged(visibility);
|
// 这里修复了 Activity 从后台返回到前台时 VideoView 从头开始播放的问题
|
if (visibility != VISIBLE) {
|
return;
|
}
|
mVideoView.seekTo(mCurrentProgress);
|
mProgressView.setProgress(mCurrentProgress);
|
}
|
|
/**
|
* {@link SeekBar.OnSeekBarChangeListener}
|
*/
|
@Override
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
if (fromUser) {
|
mPlayTime.setText(conversionTime(progress));
|
return;
|
}
|
|
if (progress != 0) {
|
// 记录当前播放进度
|
mCurrentProgress = progress;
|
} else {
|
// 如果 Activity 返回到后台,progress 会等于 0,而 mVideoView.getDuration 会等于 -1
|
// 所以要避免在这种情况下记录当前的播放进度,以便用户从后台返回到前台的时候恢复正确的播放进度
|
if (mVideoView.getDuration() > 0) {
|
mCurrentProgress = progress;
|
}
|
}
|
}
|
|
@Override
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
removeCallbacks(mRefreshRunnable);
|
removeCallbacks(mHideControllerRunnable);
|
}
|
|
@Override
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
postDelayed(mRefreshRunnable, REFRESH_TIME);
|
postDelayed(mHideControllerRunnable, CONTROLLER_TIME);
|
// 设置选择的播放进度
|
setProgress(seekBar.getProgress());
|
}
|
|
/**
|
* {@link MediaPlayer.OnPreparedListener}
|
*/
|
@Override
|
public void onPrepared(MediaPlayer player) {
|
mPlayTime.setText(conversionTime(0));
|
mTotalTime.setText(conversionTime(player.getDuration()));
|
mProgressView.setMax(mVideoView.getDuration());
|
|
// 获取视频的宽高
|
mVideoWidth = player.getVideoWidth();
|
mVideoHeight = player.getVideoHeight();
|
|
// VideoView 的宽高
|
int viewWidth = getWidth();
|
int viewHeight = getHeight();
|
|
// 基于比例调整大小
|
if (mVideoWidth * viewHeight < viewWidth * mVideoHeight) {
|
// 视频宽度过大,进行纠正
|
viewWidth = viewHeight * mVideoWidth / mVideoHeight;
|
} else if (mVideoWidth * viewHeight > viewWidth * mVideoHeight) {
|
// 视频高度过大,进行纠正
|
viewHeight = viewWidth * mVideoHeight / mVideoWidth;
|
}
|
|
// 重新设置 VideoView 的宽高
|
ViewGroup.LayoutParams params = mVideoView.getLayoutParams();
|
params.width = viewWidth;
|
params.height = viewHeight;
|
mVideoView.setLayoutParams(params);
|
|
post(mShowControllerRunnable);
|
postDelayed(mRefreshRunnable, REFRESH_TIME / 2);
|
|
if (mListener == null) {
|
return;
|
}
|
mListener.onPlayStart(this);
|
}
|
|
/**
|
* {@link MediaPlayer.OnCompletionListener}
|
*/
|
@Override
|
public void onCompletion(MediaPlayer player) {
|
pause();
|
if (mListener == null) {
|
return;
|
}
|
mListener.onPlayEnd(this);
|
}
|
|
/**
|
* {@link MediaPlayer.OnInfoListener}
|
*/
|
@Override
|
public boolean onInfo(MediaPlayer player, int what, int extra) {
|
switch (what) {
|
// 视频播放卡顿开始
|
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
|
mLottieView.setAnimation(R.raw.progress);
|
mLottieView.playAnimation();
|
mMessageView.setText("加载中…");
|
post(mShowMessageRunnable);
|
return true;
|
// 视频播放卡顿结束
|
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
|
mLottieView.cancelAnimation();
|
mMessageView.setText("加载中…");
|
postDelayed(mHideMessageRunnable, DIALOG_TIME);
|
return true;
|
default:
|
break;
|
}
|
return false;
|
}
|
|
/**
|
* {@link MediaPlayer.OnErrorListener}
|
*/
|
|
@Override
|
public boolean onError(MediaPlayer player, int what, int extra) {
|
Activity activity = getActivity();
|
if (activity == null) {
|
return false;
|
}
|
|
String message;
|
if (what == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
|
message = "该视频不适合在此设备上播放";
|
} else {
|
message = "无法播放此视频";
|
}
|
message += "\n" + String.format("错误类型:%d,错误代码:%d", what, extra);
|
new UIDialog.Builder(getActivity())
|
.setTitle("")
|
.setContent(message)
|
.setConfirm("确定")
|
.setCancel(null)
|
.setListener(dialog -> onCompletion(player))
|
.setCancelable(false)
|
.show();
|
return true;
|
}
|
|
/**
|
* {@link View.OnClickListener}
|
*/
|
|
@Override
|
public void onClick(View view) {
|
if (view == this) {
|
|
// 先移除之前发送的
|
removeCallbacks(mShowControllerRunnable);
|
removeCallbacks(mHideControllerRunnable);
|
|
if (mControllerShow) {
|
// 隐藏控制面板
|
post(mHideControllerRunnable);
|
return;
|
}
|
|
// 显示控制面板
|
post(mShowControllerRunnable);
|
postDelayed(mHideControllerRunnable, CONTROLLER_TIME);
|
|
} else if (view == mLeftView) {
|
|
if (mListener == null) {
|
return;
|
}
|
mListener.onClickBack(this);
|
|
} else if (view == mControlView) {
|
|
if (mControlView.getVisibility() != VISIBLE) {
|
return;
|
}
|
|
if (isPlaying()) {
|
pause();
|
} else {
|
start();
|
}
|
// 先移除之前发送的
|
removeCallbacks(mShowControllerRunnable);
|
removeCallbacks(mHideControllerRunnable);
|
// 重置显示隐藏面板任务
|
if (!mControllerShow) {
|
post(mShowControllerRunnable);
|
}
|
postDelayed(mHideControllerRunnable, CONTROLLER_TIME);
|
if (mListener == null) {
|
return;
|
}
|
mListener.onClickPlay(this);
|
|
} else if (view == mLockView) {
|
|
if (mLockMode) {
|
unlock();
|
} else {
|
lock();
|
}
|
if (mListener == null) {
|
return;
|
}
|
mListener.onClickLock(this);
|
}
|
}
|
|
public int getVideoWidth() {
|
return mVideoWidth;
|
}
|
|
public int getVideoHeight() {
|
return mVideoHeight;
|
}
|
|
@SuppressLint("ClickableViewAccessibility")
|
@Override
|
public boolean onTouchEvent(MotionEvent event) {
|
// 满足任一条件:关闭手势控制、处于锁定状态、处于缓冲状态
|
if (!mGestureEnabled || mLockMode || mLottieView.isAnimating()) {
|
return super.onTouchEvent(event);
|
}
|
|
switch (event.getAction()) {
|
case MotionEvent.ACTION_DOWN:
|
mMaxVoice = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
|
mCurrentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
|
|
mWindow = getActivity().getWindow();
|
mCurrentBrightness = mWindow.getAttributes().screenBrightness;
|
// 如果当前亮度是默认的,那么就获取系统当前的屏幕亮度
|
if (mCurrentBrightness == WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE) {
|
try {
|
// 这里需要注意,Settings.System.SCREEN_BRIGHTNESS 获取到的值在小米手机上面会超过 255
|
mCurrentBrightness = Math.min(Settings.System.getInt(
|
getContext().getContentResolver(),
|
Settings.System.SCREEN_BRIGHTNESS), 255) / 255f;
|
} catch (Settings.SettingNotFoundException ignored) {
|
mCurrentBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF;
|
}
|
}
|
|
mViewDownX = event.getX();
|
mViewDownY = event.getY();
|
removeCallbacks(mHideControllerRunnable);
|
break;
|
case MotionEvent.ACTION_MOVE:
|
// 计算偏移的距离(按下的位置 - 当前触摸的位置)
|
float distanceX = mViewDownX - event.getX();
|
float distanceY = mViewDownY - event.getY();
|
// 手指偏移的距离一定不能太短,这个是前提条件
|
if (Math.abs(distanceY) < ViewConfiguration.get(getContext()).getScaledTouchSlop()) {
|
break;
|
}
|
if (mTouchOrientation == -1) {
|
// 判断滚动方向是垂直的还是水平的
|
if (Math.abs(distanceY) > Math.abs(distanceX)) {
|
mTouchOrientation = LinearLayout.VERTICAL;
|
} else if (Math.abs(distanceY) < Math.abs(distanceX)) {
|
mTouchOrientation = LinearLayout.HORIZONTAL;
|
}
|
}
|
|
// 如果手指触摸方向是水平的
|
if (mTouchOrientation == LinearLayout.HORIZONTAL) {
|
int second = -(int) (distanceX / (float) getWidth() * 60f);
|
int progress = getProgress() + second * 1000;
|
if (progress >= 0 && progress <= getDuration()) {
|
mAdjustSecond = second;
|
mLottieView.setImageResource(mAdjustSecond < 0 ?
|
R.drawable.video_schedule_rewind_ic :
|
R.drawable.video_schedule_forward_ic);
|
mMessageView.setText(String.format("%s s", Math.abs(mAdjustSecond)));
|
post(mShowMessageRunnable);
|
}
|
break;
|
}
|
|
// 如果手指触摸方向是垂直的
|
if (mTouchOrientation == LinearLayout.VERTICAL) {
|
// 判断触摸点是在屏幕左边还是右边
|
if ((int) event.getX() < getWidth() / 2) {
|
// 手指在屏幕左边
|
float delta = (distanceY / getHeight()) * WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL;
|
if (delta == 0) {
|
break;
|
}
|
|
// 更新系统亮度
|
float brightness = Math.min(Math.max(mCurrentBrightness + delta,
|
WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF),
|
WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL);
|
WindowManager.LayoutParams attributes = mWindow.getAttributes();
|
attributes.screenBrightness = brightness;
|
mWindow.setAttributes(attributes);
|
|
int percent = (int) (brightness * 100);
|
|
@DrawableRes int iconId;
|
if (percent > 100 / 3 * 2) {
|
iconId = R.drawable.video_brightness_high_ic;
|
} else if (percent > 100 / 3) {
|
iconId = R.drawable.video_brightness_medium_ic;
|
} else {
|
iconId = R.drawable.video_brightness_low_ic;
|
}
|
mLottieView.setImageResource(iconId);
|
mMessageView.setText(String.format("%s %%", percent));
|
post(mShowMessageRunnable);
|
break;
|
}
|
|
// 手指在屏幕右边
|
float delta = (distanceY / getHeight()) * mMaxVoice;
|
if (delta == 0) {
|
break;
|
}
|
|
// 更新系统音量
|
int voice = (int) Math.min(Math.max(mCurrentVolume + delta, 0), mMaxVoice);
|
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, voice, 0);
|
|
int percent = voice * 100 / mMaxVoice;
|
|
@DrawableRes int iconId;
|
if (percent > 100 / 3 * 2) {
|
iconId = R.drawable.video_volume_high_ic;
|
} else if (percent > 100 / 3) {
|
iconId = R.drawable.video_volume_medium_ic;
|
} else if (percent != 0) {
|
iconId = R.drawable.video_volume_low_ic;
|
} else {
|
iconId = R.drawable.video_volume_mute_ic;
|
}
|
mLottieView.setImageResource(iconId);
|
mMessageView.setText(String.format("%s %%", percent));
|
post(mShowMessageRunnable);
|
break;
|
}
|
break;
|
case MotionEvent.ACTION_UP:
|
if (Math.abs(mViewDownX - event.getX()) <= ViewConfiguration.get(getContext()).getScaledTouchSlop() &&
|
Math.abs(mViewDownY - event.getY()) <= ViewConfiguration.get(getContext()).getScaledTouchSlop()) {
|
// 如果整个视频播放区域太大,触摸移动会导致触发点击事件,所以这里换成手动派发点击事件
|
if (isEnabled() && isClickable()) {
|
performClick();
|
}
|
}
|
case MotionEvent.ACTION_CANCEL:
|
mTouchOrientation = -1;
|
mCurrentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
|
if (mAdjustSecond != 0) {
|
// 调整播放进度
|
setProgress(getProgress() + mAdjustSecond * 1000);
|
mAdjustSecond = 0;
|
}
|
postDelayed(mHideControllerRunnable, CONTROLLER_TIME);
|
postDelayed(mHideMessageRunnable, DIALOG_TIME);
|
break;
|
default:
|
break;
|
}
|
return true;
|
}
|
|
/**
|
* 刷新任务
|
*/
|
private final Runnable mRefreshRunnable = new Runnable() {
|
|
@Override
|
public void run() {
|
int progress = mVideoView.getCurrentPosition();
|
// 这里优化了播放的秒数计算,将 800 毫秒估算成 1 秒
|
if (progress + 1000 < mVideoView.getDuration()) {
|
// 进行四舍五入计算
|
progress = Math.round(progress / 1000f) * 1000;
|
}
|
mPlayTime.setText(conversionTime(progress));
|
mProgressView.setProgress(progress);
|
mProgressView.setSecondaryProgress((int) (mVideoView.getBufferPercentage() / 100f * mVideoView.getDuration()));
|
if (mVideoView.isPlaying()) {
|
if (!mLockMode && mBottomLayout.getVisibility() == GONE) {
|
mBottomLayout.setVisibility(VISIBLE);
|
}
|
} else {
|
if (mBottomLayout.getVisibility() == VISIBLE) {
|
mBottomLayout.setVisibility(GONE);
|
}
|
}
|
postDelayed(this, REFRESH_TIME);
|
|
if (mListener == null) {
|
return;
|
}
|
mListener.onPlayProgress(VideoPlayerView.this);
|
}
|
};
|
|
/**
|
* 显示控制面板
|
*/
|
private final Runnable mShowControllerRunnable = () -> {
|
if (!mControllerShow) {
|
showController();
|
}
|
};
|
|
/**
|
* 隐藏控制面板
|
*/
|
private final Runnable mHideControllerRunnable = () -> {
|
if (mControllerShow) {
|
hideController();
|
}
|
};
|
|
/**
|
* 显示提示
|
*/
|
private final Runnable mShowMessageRunnable = () -> {
|
hideController();
|
mMessageLayout.setVisibility(VISIBLE);
|
};
|
|
/**
|
* 隐藏提示
|
*/
|
private final Runnable mHideMessageRunnable = () -> {
|
mMessageLayout.setVisibility(GONE);
|
};
|
|
/**
|
* 时间转换
|
*/
|
public static String conversionTime(int time) {
|
Formatter formatter = new Formatter(Locale.getDefault());
|
// 总秒数
|
int totalSeconds = time / 1000;
|
// 小时数
|
int hours = totalSeconds / 3600;
|
// 分钟数
|
int minutes = (totalSeconds / 60) % 60;
|
// 秒数
|
int seconds = totalSeconds % 60;
|
if (hours > 0) {
|
return formatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
|
} else {
|
return formatter.format("%02d:%02d", minutes, seconds).toString();
|
}
|
}
|
|
/**
|
* 点击返回监听器
|
*/
|
public interface OnPlayListener {
|
|
/**
|
* 点击了返回按钮(可在此处处理返回事件)
|
*/
|
default void onClickBack(VideoPlayerView view) {}
|
|
/**
|
* 点击了锁定按钮
|
*/
|
default void onClickLock(VideoPlayerView view) {}
|
|
/**
|
* 点击了播放按钮
|
*/
|
default void onClickPlay(VideoPlayerView view) {}
|
|
/**
|
* 播放开始(可在此处设置播放进度)
|
*/
|
default void onPlayStart(VideoPlayerView view) {}
|
|
/**
|
* 播放进度发生改变
|
*/
|
default void onPlayProgress(VideoPlayerView view) {}
|
|
/**
|
* 播放结束(可在此处结束播放或者循环播放)
|
*/
|
default void onPlayEnd(VideoPlayerView view) {}
|
}
|
}
|