1. 功能需求
- 双指缩放视频播放画面,支持设定最小、最大缩放范围
- 双指拖动画面可任意方向移动
- 如果是缩小画面,最后需要在屏幕居中显示,并且需要有动画效果
- 如果是放大画面,有画面边缘在屏幕内的,需要自动吸附到屏幕边缘
- 视频暂停状态下也能缩放
2. 实现原理
- 先进行缩放平移。 通过
View.getMatrix()
获取当前播放画面的Matrix,进行矩阵变换:缩放、平移,改变画面位置和大小,实现播放画面缩放功能。 - 缩放结束后,进行属性动画。 当前画面对应的矩阵变换为
mScaleTransMatrix
,计算动画结束应该移动的位scaleEndAnimMatrix
,进行属性动画从mScaleTransMatrix
变化为scaleEndAnimMatrix
。
2.1 如何检测手势缩放?
View.onTouchEvent
。分别监听手指按下(MotionEvent.ACTION_POINTER_DOWN
)、抬起(MotionEvent.ACTION_POINTER_UP
)、移动(MotionEvent.ACTION_MOVE
)ScaleGestureDetector
。直接使用手势缩放检测ScaleGestureDetector
对View#onTouchEvent中的手势变化进行识别,通过ScaleGestureDetector.OnScaleGestureListener
得到onScaleBegin-onScale-onScale … -onScaleEnd的缩放回调,在回调中处理响应的缩放逻辑。
1. View.onTouchEvent关键代码
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_POINTER_DOWN:
onScaleBegin(event);
break;
case MotionEvent.ACTION_POINTER_UP:
onScaleEnd(event);
break;
case MotionEvent.ACTION_MOVE:
onScale(event);
break;
case MotionEvent.ACTION_CANCEL:
cancelScale(event);
break;
}
return true;
}
2. ScaleGestureDetector
使用ScaleGestureDetector
来识别onTouchEvent中的手势触摸操作,得到onScaleBegin
、onScale
、onScaleEnd
三种回调,在回调里面通过VideoTouchScaleHandler
对视频进行缩放、平移操作。
-
添加手势触摸层
GestureLayer
,使用ScaleGestureDetector
识别手势/** * 手势处理layer层 */ public final class GestureLayer implements IGestureLayer, GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { private static final String TAG = "GestureLayer"; private Context mContext; private FrameLayout mContainer; /** 手势检测 */ private GestureDetector mGestureDetector; /** 手势缩放 检测 */ private ScaleGestureDetector mScaleGestureDetector; /** 手势缩放 监听 */ private VideoScaleGestureListener mScaleGestureListener; /** 手势缩放 处理 */ private VideoTouchScaleHandler mScaleHandler; private IVideoTouchAdapter mVideoTouchAdapter; public GestureLayer(Context context, IVideoTouchAdapter videoTouchAdapter) { mContext = context; mVideoTouchAdapter = videoTouchAdapter; initContainer(); initTouchHandler(); } private void initContainer() { mContainer = new FrameLayout(mContext) { @Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { boolean isConsume = onGestureTouchEvent(event); if (isConsume) { return true; } else { return super.onTouchEvent(event); } } }; } public void initTouchHandler() { mGestureDetector = new GestureDetector(mContext, this); mGestureDetector.setOnDoubleTapListener(this); // 手势缩放 mScaleGestureListener = new VideoScaleGestureListener(this); mScaleGestureDetector = new ScaleGestureDetector(getContext(), mScaleGestureListener); // 缩放 处理 mScaleHandler = new VideoTouchScaleHandler(getContext(), mContainer, mVideoTouchAdapter); mScaleGestureListener.mScaleHandler = mScaleHandler; } @Override public void onLayerRelease() { if (mGestureDetector != null) { mGestureDetector.setOnDoubleTapListener(null); } } @Override public boolean onGestureTouchEvent(MotionEvent event) { try { int pointCount = event.getPointerCount(); if (pointCount == 1 && event.getAction() == MotionEvent.ACTION_UP) { if (mScaleHandler.isScaled()) { mScaleHandler.showScaleReset(); } } if (pointCount > 1) { boolean isConsume = mScaleGestureDetector.onTouchEvent(event); if (isConsume) { return true; } } } catch (Exception e) { Log.e(TAG, "", e); } if (event.getAction() == MotionEvent.ACTION_DOWN) { retu