一、播放器常用手势操控包括:
1、单击显示和隐藏播放器控件;
2、双击播放暂停;
3、左侧上下滑动调节亮度;
4、右侧上下滑动调节音量;
5、左右滑动调节进度。
二、手势检测帮助类PlayerGestureHelper。
用于检测区分手势类型。代码如下:
package com.dway.gesture;
import android.content.Context;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
/**
* 播放器常用手势监听:单击、双击、横向滑动、左右两侧纵向滑动(亮度和声音)
*/
public class PlayerGestureHelper extends GestureDetector.SimpleOnGestureListener {
private OnPlayerGestureListener mListener;
//播放器View的长宽
private float mPlayerWidth;
private float mPlayerHeight;
private GestureMode mGestureMode = GestureMode.NONE;
private int mTouchSlop;
public PlayerGestureHelper(Context context, OnPlayerGestureListener listener) {
this.mListener = listener;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public void setPlayerSize(float width, float height){
mPlayerWidth = width;
mPlayerHeight = height;
}
public GestureMode getGestureMode() {
return mGestureMode;
}
@Override
public boolean onDown(MotionEvent e) {
mGestureMode = GestureMode.NONE;
if (mListener != null) {
mListener.onDown(e);
}
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return super.onSingleTapUp(e);
}
@Override
public void onShowPress(MotionEvent e) {
super.onShowPress(e);
}
@Override
public void onLongPress(MotionEvent e) {
super.onLongPress(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (mGestureMode == GestureMode.NONE) {
if (Math.abs(distanceX) > mTouchSlop) {//横向滑动
mGestureMode = GestureMode.PROGRESS;
} else if (Math.abs(distanceY) > mTouchSlop) {//纵向滑动
if (e1.getX() < mPlayerWidth/2) {
mGestureMode = GestureMode.BRIGHTNESS;
} else {
mGestureMode = GestureMode.VOLUME;
}
}
} else if (mGestureMode == GestureMode.PROGRESS) {//快进快退
if (mListener != null) {
mListener.onGestureProgress(e1, e2, distanceX, distanceY);
}
} else if (mGestureMode == GestureMode.BRIGHTNESS) {
if (mListener != null) {
mListener.onGestureBrightness(e1, e2, distanceX, distanceY);
}
} else if (mGestureMode == GestureMode.VOLUME) {
if (mListener != null) {
mListener.onGestureVolume(e1, e2, distanceX, distanceY);
}
}
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
if (mListener != null) {
mListener.onGestureDoubleTap(e);
}
return super.onDoubleTap(e);
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return super.onDoubleTapEvent(e);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (mListener != null) {
mListener.onGestureSingleTap(e);
}
//单击事件,在双击事件发生时不会产生这个事件,所以用这个回调作为播放器单击事件
return super.onSingleTapConfirmed(e);
}
public interface OnPlayerGestureListener {
//亮度手势,手指在左侧上下滑动
void onGestureBrightness(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
//音量手势,手指在右侧上下滑动
void onGestureVolume(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
//进度手势,手指左右滑动
void onGestureProgress(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
//单击手势
void onGestureSingleTap(MotionEvent e);
//双击手势
void onGestureDoubleTap(MotionEvent e);
//按下手势
void onDown(MotionEvent e);
}
/**
* 手势类型
*/
public enum GestureMode {
NONE, //默认值
BRIGHTNESS, //左侧上下滑动(亮度调节)
VOLUME, //右侧上下滑动(声音调节)
PROGRESS, //横向滑动(进度调节)
}
}
三、GestureView说明
GestureView继承于View,使用时直接放在播放器上层即可,该View内部封装了自动调节亮度和音量的功能,对外只保留必要的接口。代码如下:
package com.dway.gesture;
import android.app.Service;
import android.content.Context;
import android.media.AudioManager;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
/**
* 手势操控View
*/
public class GestureView extends View implements View.OnTouchListener {
private boolean mGestureEnable = true;
private PlayerGestureHelper mGestureHelper;
private GestureDetector mGestureDetector;
private GestureListener mGestureListener;
private AudioManager mAudioManager;
private int mMaxVolume = 0;
private int mOldVolume = 0;
private float mOldBrightness = 0;
private Window mWindow;
private int mDuration = 0;
private int mOldProgress = 0;
private int mNewProgress = 0;
public GestureView(Context context) {
super(context);
init();
}
public GestureView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public GestureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mGestureHelper = new PlayerGestureHelper(getContext(), new MyPlayerGestureListener());
mGestureDetector = new GestureDetector(getContext(), mGestureHelper);
post(new Runnable() {
@Override
public void run() {
mGestureHelper.setPlayerSize(getWidth(), getHeight());
}
});
setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if(mGestureEnable) {
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
PlayerGestureHelper.GestureMode gestureMode = mGestureHelper.getGestureMode();
if (gestureMode == PlayerGestureHelper.GestureMode.PROGRESS) {
//手势检测的seekTo操作,因为GestureDetector不能检测手势抬起
if(mGestureListener != null){
mGestureListener.onGestureProgressUp(mNewProgress, mDuration);
}
}
if(gestureMode == PlayerGestureHelper.GestureMode.PROGRESS
|| gestureMode == PlayerGestureHelper.GestureMode.BRIGHTNESS
|| gestureMode == PlayerGestureHelper.GestureMode.VOLUME) {
if (mGestureListener != null) {
mGestureListener.onScrollGestureEnd();
}
}
}
return mGestureDetector.onTouchEvent(event);
}else {
return false;
}
}
public void setGestureEnable(boolean gestureEnable){
mGestureEnable = gestureEnable;
}
public void setGestureListener(GestureListener gestureListener){
mGestureListener = gestureListener;
}
class MyPlayerGestureListener implements PlayerGestureHelper.OnPlayerGestureListener {
@Override
public void onGestureBrightness(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if(mGestureEnable){
if (mGestureListener != null) {
float offset = e1.getY() - e2.getY();
float brightness = mOldBrightness + 2 * offset / getHeight();
if(brightness < 0){
brightness = 0;
}else if(brightness > 1){
brightness = 1;
}
if(mWindow != null){
WindowManager.LayoutParams lp = mWindow.getAttributes();
lp.screenBrightness = brightness;
mWindow.setAttributes(lp);
}
mGestureListener.onGestureBrightness(brightness);
}
}
}
@Override
public void onGestureVolume(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if(mGestureEnable){
if (mGestureListener != null) {
float offset = e1.getY() - e2.getY();
int volume = (int) (mOldVolume + mMaxVolume * 2 * offset / getHeight());
if(volume < 0){
volume = 0;
}else if(volume > mMaxVolume){
volume = mMaxVolume;
}
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, AudioManager.FLAG_PLAY_SOUND);
mGestureListener.onGestureVolume(volume, mMaxVolume);
}
}
}
@Override
public void onGestureProgress(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if(mGestureEnable) {
if (mGestureListener != null) {
if(mDuration == 0){
mDuration = mGestureListener.getDuration();
}
float offset = e2.getX() - e1.getX();
// 视频时长为1小时以上,小屏和全屏的手势滑动最长为视频时长的十分之一
if (mDuration >= 60 * 60 * 1000) {
mNewProgress = (int) (mOldProgress + offset / getWidth() * mDuration / 10);
}// 视频时长为31分钟-60分钟时,小屏和全屏的手势滑动最长为视频时长五分之一
else if (mDuration >= 30 * 60 * 1000) {
mNewProgress = (int) (mOldProgress + offset / getWidth() * mDuration / 5);
}// 视频时长为11分钟-30分钟时,小屏和全屏的手势滑动最长为视频时长三分之一
else if (mDuration >= 10 * 60 * 1000) {
mNewProgress = (int) (mOldProgress + offset / getWidth() * mDuration / 3);
}// 视频时长为4-10分钟时,小屏和全屏的手势滑动最长为视频时长二分之一
else if (mDuration >= 3 * 60 * 1000) {
mNewProgress = (int) (mOldProgress + offset / getWidth() * mDuration / 2);
}// 视频时长为1秒钟至3分钟时,小屏和全屏的手势滑动最长为视频结束
else {
mNewProgress = (int) (mOldProgress + offset / getWidth() * mDuration);
}
if (mNewProgress > mDuration) {
mNewProgress = mDuration;
} else if (mNewProgress < 0) {
mNewProgress = 0;
}
mGestureListener.onGestureProgress(mNewProgress, mDuration);
}
}
}
@Override
public void onGestureSingleTap(MotionEvent e) {
if(mGestureEnable) {
if (mGestureListener != null) {
mGestureListener.onGestureSingleTap();
}
}
}
@Override
public void onGestureDoubleTap(MotionEvent e) {
if(mGestureEnable) {
if (mGestureListener != null) {
mGestureListener.onGestureDoubleTap();
}
}
}
@Override
public void onDown(MotionEvent e) {
if(mGestureEnable){
if(mGestureListener != null) {
mDuration = mGestureListener.getDuration();
mOldProgress = mGestureListener.getCurrent();
mAudioManager = (AudioManager) getContext().getSystemService(Service.AUDIO_SERVICE);
mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mOldVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
mWindow = mGestureListener.getActivityWindow();
if (mWindow != null) {
WindowManager.LayoutParams layoutParams = mWindow.getAttributes();
mOldBrightness = layoutParams.screenBrightness;
if (mOldBrightness == WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE) {
mOldBrightness = 0.5f;
}
}
mGestureListener.onDown();
}
}
}
}
public interface GestureListener{
/**
* 亮度调节回调
* @param brightness [0, 1]
*/
void onGestureBrightness(float brightness);
/**
* 音量调节回调
* @param volume 当前音量
* @param MaxVolume 最大音量
*/
void onGestureVolume(int volume, int MaxVolume);
/**
* 进度调节
* @param progress 进度
* @param duration 总长度
*/
void onGestureProgress(int progress, int duration);
/**
* 进度调节后抬起动作,需要对播放器进行seekTo操作
* @param progress 进度
* @param duration 总长度
*/
void onGestureProgressUp(int progress, int duration);
/**
* 单击
*/
void onGestureSingleTap();
/**
* 双击
*/
void onGestureDoubleTap();
/**
* 按下
*/
void onDown();
/**
* 滑动手势结束(即进度、音量、亮度调节手势结束)
*/
void onScrollGestureEnd();
/**
* 获取当前播放进度
* @return
*/
int getCurrent();
/**
* 获取总长度
* @return
*/
int getDuration();
/**
* 获取Activity的Window,作为调节亮度用
* @return
*/
Window getActivityWindow();
}
}
四、如何使用
1、直接将GestureView置于播放器布局上层。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="#000000">
<SurfaceView
android:id="@+id/player_surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<com.dway.gesture.GestureView
android:id="@+id/player_gestureview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"/>
</FrameLayout>
2、在Activity中使用如下:
//需要打开手势功能则调用该方法,并处理相关手势接口
private void enableGesture(){
GestureView gestureView = findViewById(R.id.player_gestureview);
gestureView.setVisibility(VISIBLE);
gestureView.setGestureEnable(true);
gestureView.setGestureListener(new MyGestureListener());
}
//需要关闭手势功能则调用该方法
private void disableGesture(){
GestureView gestureView = findViewById(R.id.player_gestureview);
gestureView.setVisibility(GONE);
gestureView.setGestureEnable(false);
}
//手势回调处理,其中需要传当前播放进度和视频总长度,Activity的window也需要传进去
class MyGestureListener implements GestureView.GestureListener{
@Override
public void onGestureBrightness(float brightness) {
//此处可以显示亮度调节
}
@Override
public void onGestureVolume(int volume, int MaxVolume) {
//此处可以显示音量调节
}
@Override
public void onGestureProgress(int progress, int duration) {
//此处可以显示进度调节
}
@Override
public void onGestureProgressUp(int progress, int duration) {
//此处可以进行播放器的seekTo操作
}
@Override
public void onGestureSingleTap() {
//单击,可以显示和隐藏播放器上下的控件等
}
@Override
public void onGestureDoubleTap() {
//双击,可以进行播放暂停操作
}
@Override
public void onDown() {
}
@Override
public void onScrollGestureEnd() {
//亮度、音量或进度调节手势结束,可以隐藏调节过程的相关UI
}
@Override
public int getCurrent() {
return mPlayer != null ? (int) mPlayer.getCurrentPosition() : 0;
}
@Override
public int getDuration() {
return mPlayer != null ? (int) mPlayer.getDuration() : 0;
}
@Override
public Window getActivityWindow() {
return getWindow();
}
}
通过封装,使用代码就很简洁了,只需要处理相关提示的UI即可,至于怎么调节亮度和音量等则不需要考虑了。