@date 2020/12/2
*/
public class VideoTouchScaleHandler implements IVideoTouchHandler, ScaleGestureDetector.OnScaleGestureListener {
private static final String TAG = "VideoTouchScaleHandler";
private Context mContext;
public FrameLayout mContainer;
private boolean openScaleTouch = true; // 开启缩放
private boolean mIsScaleTouch;
private Matrix mScaleTransMatrix; // 缓存了上次的矩阵值,所以需要计算每次变化量
private float mStartCenterX, mStartCenterY, mLastCenterX, mLastCenterY, centerX, centerY;
private float mStartSpan, mLastSpan, mCurrentSpan;
private float mScale;
private float[] mMatrixValue = new float[9];
private float mMinScale = 0.1F, mMaxScale = 3F;
private VideoScaleEndAnimator mScaleAnimator;
IVideoTouchAdapter mTouchAdapter;
TouchScaleResetView mScaleRestView;
public VideoTouchScaleHandler(Context context, FrameLayout container,
IVideoTouchAdapter videoTouchAdapter) {
mContext = context;
mContainer = container;
mTouchAdapter = videoTouchAdapter;
initView();
}
private void initView() {
mScaleRestView = new TouchScaleResetView(mContext, mContainer) {
@Override
public void clickResetScale() {
mScaleRestView.setVisibility(View.GONE);
if (isScaled()) {
cancelScale();
}
}
};
}
private Context getContext() {
return mContext;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
TextureView mTextureView = mTouchAdapter.getTextureView();
if (mTextureView != null) {
mIsScaleTouch = true;
if (mScaleTransMatrix == null) {
mScaleTransMatrix = new Matrix(mTextureView.getMatrix());
onScaleMatrixUpdate(mScaleTransMatrix);
}
}
mStartCenterX = detector.getFocusX();
mStartCenterY = detector.getFocusY();
mStartSpan = detector.getCurrentSpan();
mLastCenterX = mStartCenterX;
mLastCenterY = mStartCenterY;
mLastSpan = mStartSpan;
return true;
}
private void updateMatrixToTexture(Matrix newMatrix) {
TextureView mTextureView = mTouchAdapter.getTextureView();
if (mTextureView != null) {
mTextureView.setTransform(newMatrix);
}
onScaleMatrixUpdate(newMatrix);
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (mIsScaleTouch && openScaleTouch) {
mCurrentSpan = detector.getCurrentSpan();
centerX = detector.getFocusX();
centerY = detector.getFocusY();
if (processOnScale(detector)) {
mLastCenterX = centerX;
mLastCenterY = centerY;
mLastSpan = mCurrentSpan;
}
}
return false;
}
private boolean processOnScale(ScaleGestureDetector detector) {
float diffScale = mCurrentSpan / mLastSpan;
if (mTouchAdapter.isFullScreen()) {
if (mScaleTransMatrix != null) {
postScale(mScaleTransMatrix, diffScale, mStartCenterX, mStartCenterY);
mScaleTransMatrix.postTranslate(detector.getFocusX() - mLastCenterX,
detector.getFocusY() - mLastCenterY);
onScaleMatrixUpdate(mScaleTransMatrix);
TextureView mTextureView = mTouchAdapter.getTextureView();
if (mTextureView != null) {
Matrix matrix = new Matrix(mTextureView.getMatrix());
matrix.set(mScaleTransMatrix);
mTextureView.setTransform(matrix);
}
int scaleRatio = (int) (mScale * 100);
Toast.makeText(getContext(), "" + scaleRatio + "%", Toast.LENGTH_SHORT).show();
return true;
}
}
return false;
}
private void postScale(Matrix matrix, float scale, float x, float y) {
matrix.getValues(mMatrixValue);
float curScale = mMatrixValue[Matrix.MSCALE_X];
if (scale < 1 && Math.abs(curScale - mMinScale) < 0.001F) {
scale = 1;
} else if (scale > 1 && Math.abs(curScale - mMaxScale) < 0.001F) {
scale = 1;
} else {
curScale *= scale;
if (scale < 1 && curScale < mMinScale) {
curScale = mMinScale;
scale = curScale / mMatrixValue[Matrix.MSCALE_X];
} else if (scale > 1 && curScale > mMaxScale) {
curScale = mMaxScale;
scale = curScale / mMatrixValue[Matrix.MSCALE_X];
}
matrix.postScale(scale, scale, x, y);
}
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
if (mIsScaleTouch) { // 取消多手势操作
mIsScaleTouch = false;
doScaleEndAnim();
}
}
public void cancelScale() {
TextureView mTextureView = mTouchAdapter.getTextureView();
if (mScaleTransMatrix != null && mTextureView != null) {
mIsScaleTouch = false;
mScaleTransMatrix.reset();
onScaleMatrixUpdate(mScaleTransMatrix);
Matrix matrix = new Matrix(mTextureView.getMatrix());
matrix.reset();
mTextureView.setTransform(matrix);
}
}
/**
计算缩放结束后动画位置:scaleEndAnimMatrix
*/
private void doScaleEndAnim() {
TextureView mTextureView = mTouchAdapter.getTextureView();
if (mTextureView == null) {
return;
}
Matrix scaleEndAnimMatrix = new Matrix();
RectF videoRectF = new RectF(0, 0, mTextureView.getWidth(), mTextureView.getHeight());
if (mScale > 0 && mScale <= 1.0f) { // 缩小居中
scaleEndAnimMatrix.postScale(mScale, mScale, videoRectF.right / 2, videoRectF.bottom / 2);
startTransToAnimEnd(mScaleTransMatrix, scaleEndAnimMatrix);
} else if (mScale > 1.0F) { // 放大,检测4边是否有在屏幕内部,有的话自动吸附到屏幕边缘
RectF rectF = new RectF(0, 0, mTextureView.getWidth(), mTextureView.getHeight());
// 测量经过缩放位移变换后的播放画面位置
mScaleTransMatrix.mapRect(rectF);
float transAnimX = 0f;
float transAnimY = 0f;
scaleEndAnimMatrix.set(mScaleTransMatrix);
if (rectF.left > videoRectF.left
|| rectF.right < videoRectF.right
|| rectF.top > videoRectF.top
|| rectF.bottom < videoRectF.bottom) { // 放大情况下,有一边缩放后在屏幕内部,自动吸附到屏幕边缘
if (rectF.left > videoRectF.left) { // 左移吸边
transAnimX = videoRectF.left - rectF.left;
} else if (rectF.right < videoRectF.right) { // 右移吸边
transAnimX = videoRectF.right - rectF.right;
}
if (rectF.top > videoRectF.top) { // 上移吸边
transAnimY = videoRectF.top - rectF.top;
} else if (rectF.bottom < videoRectF.bottom) { // 下移吸边
transAnimY = videoRectF.bottom - rectF.bottom;
}
scaleEndAnimMatrix.postTranslate(transAnimX, transAnimY);
startTransToAnimEnd(mScaleTransMatrix, scaleEndAnimMatrix);
}
}
}
private void startTransToAnimEnd(Matrix startMatrix, Matrix endMatrix) {
LogUtil.d(TAG, "startTransToAnimEnd \nstart=" + startMatrix + "\nend=" + endMatrix);
// 令 A = startMatrix;B = endMatrix
// 方法1:直接将画面更新为结束矩阵位置B
// updateMatrixToView(endMatrix); //
// 方法2:将画面从现有位置A,移动到结束矩阵位置B,移动的距离T。B = T * A; 根据矩阵乘法的计算规则,反推出:T(x) = B(x) - A(x); T(y) = B(y) - A(y)
// float[] startArray = new float[9];
// float[] endArray = new float[9];
// startMatrix.getValues(startArray);
// endMatrix.getValues(endArray);
// float transX = endArray[Matrix.MTRANS_X] - startArray[Matrix.MTRANS_X];
// float transY = endArray[Matrix.MTRANS_Y] - startArray[Matrix.MTRANS_Y];
// startMatrix.postTranslate(transX, transY);
// LogUtil.d(TAG, "transToCenter1 \nstart=" + startMatrix + "\nend" + endMatrix);
// updateMatrixToView(startMatrix);
// 方法3:在方法2基础上,增加动画移动效果
if (mScaleAnimator != null) {
mScaleAnimator.cancel();
mScaleAnimator = null;
}
if (mScaleAnimator == null) {
mScaleAnimator = new VideoScaleEndAnimator(startMatrix, endMatrix) {
@Override
protected void updateMatrixToView(Matrix transMatrix) {
updateMatrixToTexture(transMatrix);
}
};
mScaleAnimator.start();
}
mScaleTransMatrix = endMatrix;
}
public void showScaleReset() {
if (isScaled() && mTouchAdapter != null && mTouchAdapter.isFullScreen()) {
if (mScaleRestView != null && mScaleRestView.getVisibility() != View.VISIBLE) {
mScaleRestView.setVisibility(View.VISIBLE);
}
}
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// 缩放模式下,是否需要单手滚动
// if (isScaled(mScale) && mScaleTransMatrix != null) {
// TextureView mTextureView = mTouchAdapter.getTextureView();
// if (mTextureView != null) {
// postTranslate(mScaleTransMatrix, -distanceX, -distanceY);
// onScaleMatrixUpdate(mScaleTransMatrix);
// Matrix matrix = new Matrix(mTextureView.getMatrix());
// matrix.set(mScaleTransMatrix);
// mTextureView.setTransform(matrix);
// return true;
// }
// }
return false;
}
private void onScaleMatrixUpdate(Matrix matrix) {
matrix.getValues(mMatrixValue);
mScale = mMatrixValue[Matrix.MSCALE_X];
// 暂停下,实时更新缩放画面
if (!mTouchAdapter.isPlaying()) {
TextureView mTextureView = mTouchAdapter.getTextureView();
if (mTextureView != null) {
mTextureView.invalidate();
}
}
}
/**
是否处于已缩放 or 缩放中
@return
*/
public boolean isInScaleStatus() {
return isScaled(mScale) || mIsScaleTouch;
}
public boolean isScaled() {
return isScaled(mScale);
}
private boolean isScaled(float scale) {
return scale > 0 && scale <= 0.99F || scale >= 1.01F;
}
}