Android 视频旋转、缩放与回弹动效实现(二)

本文详细介绍了在Android中实现视频双指旋转、缩放和平移的动效,包括旋转识别、旋转处理和回弹动效的触发与计算。通过自定义的RotateGestureDetector和VideoTouchRotateHandler类,结合手势识别和变换矩阵,实现了视频画面的平滑旋转和回弹效果。同时,文章探讨了回弹动效的计算方法,确保在动画执行过程中保持画面稳定。
摘要由CSDN通过智能技术生成


文章索引

  1. Android 视频手势缩放与回弹动效实现(一):主要是实现视频双指:缩放、平移、回弹动效
  2. Android 视频旋转、缩放与回弹动效实现(二):主要是实现视频双指:旋转、缩放、平移、回弹动效

Android 视频旋转、缩放与回弹动效实现(二)

Android 视频手势缩放与回弹动效实现(一)中我们实现了下列1-5部分的需求,事实上对于双指手势触摸,不仅可以缩放、平移,还可以进行旋转,现在我们就在原有基础上进行改造,添加视频手势旋转回弹动效,来实现需求6。

功能需求

在这里插入图片描述

  1. 双指缩放视频播放画面,支持设定最小、最大缩放范围
  2. 双指拖动画面可任意方向移动
  3. 如果是缩小画面,最后需要在屏幕居中显示,并且需要有动画效果
  4. 如果是放大画面,有画面边缘在屏幕内的,需要自动吸附到屏幕边缘
  5. 视频暂停状态下也能缩放
  6. 双指旋转画面,旋转角度以45度为分界线,进行自动校正(超过45度旋转90度;不超过45度旋转0度)

实现思路

实现主要思路

  1. 手势旋转识别。
    接收onTouchEvent事件,识别双指旋转:旋转角度、旋转中心
  2. 手势旋转处理。
    识别到手势旋转后,通过Matrix.postRotate进行画面旋转变换
  3. 缩放倍数逻辑改造。
    由于旋转也会引起Matrix#MSCALE_X值发生变化,缩放倍数不能再通过直接获取矩阵该分量值来计算,需要将计算后的缩放倍数独立保存。参考:Android Matrix 带你掌控雷电,旋转部分对Matrix的影响
  4. 回弹动效触发。
    原有回弹动效是在缩放结束后onScaleEnd触发,加入旋转后,需要保证在缩放结束onScaleEnd和旋转结束onRotateEnd后才触发,这里选择ACTION_UP来触发回弹动效。
  5. 回弹动效的计算。
    加入旋转后,回弹动效在原有2个数据:transAnimXtransAnimY(x、y轴上的平移补偿)基础上增加了旋转补偿角度rotateEndFixDegrees

1. 旋转识别

在缩放、平移中我们使用系统自带缩放识别类ScaleGestureDetector.onTouchEvent(event)来识别当前触摸事件,得到onScaleBeginonScaleonScaleEnd的缩放回调,然后用VideoTouchScaleHandler来对缩放、平移进行相应处理。

加入旋转功能后,由于系统没有自带旋转识别类,我们需要自定义实现手势识别旋转:RotateGestureDetector。下面介绍RotateGestureDetector里面的主要逻辑,即如何识别旋转中心旋转角度

  1. 旋转中心
    旋转中心计算比较简单,通过获取双指间连线的中心点即可。
    但是实际我们在视频画面旋转中,不会使用双指中心来进行旋转,而是直接使用画面中心来旋转,为什么呢?

    如果采用双指中心来旋转,因为每次旋转的中心点都不固定,会造成旋转时,变换矩阵中的位移分量也受到了影响,这样后续在计算回弹动效的时候,需要补偿的位移transAnimXtransAnimY会因为旋转中心点变化,而无法准确计算出。

     // 计算双指旋转中心点
     float pivotX = (event.getX(0) + event.getX(1)) / 2;
     float pivotY = (event.getY(0) + event.getY(1)) / 2;
    
     // 实际播放画面的旋转中心为画面中心点
     mRotateCenter = new PointF(mTouchAdapter.getTextureView().getWidth() / 2,
                     mTouchAdapter.getTextureView().getHeight() / 2);
    
  2. 旋转角度
    旋转角度的计算稍微比较复杂,我们需要先计算当前双指连线的夹角degrees, 来减去上一次我们记录的手指连线夹角lastDegrees,才能得到本次旋转的角度diffDegree = degrees - lastDegrees。如图所示:

在这里插入图片描述

双指连线夹角degrees如何计算呢?

这里我们需要获取到双指分别在x、y轴上的分量 Δ x , Δ y \Delta x, \Delta y Δx,Δy,组成的三角形,由于 t a n ( θ ) = Δ y Δ x tan(\theta)=\frac{\Delta y}{\Delta x} tan(θ)=ΔxΔy通过计算夹角的反正切三角函数arctangent得到偏移角度 θ = a r c t a n Δ y Δ x \theta=arctan \frac{\Delta y}{\Delta x} θ=arctanΔxΔy。几个注意的小点:

  1. Math.atan2(y, x)Math.atan(v)差异。

    都能计算反正切,为了避免除数为0的情况(当然还有其它差别,如结果范围不同),我们一般选用Math.atan2(y, x)

  2. Math.atan2(y, x)结果。

    计算结果值为弧度,取值范围**[-Math.PI, Math.PI],由于旋转操作Matrix.postRotate传入的参数为具体角度,所以我们需要将弧度通过Math.toDegrees转换为角度** [-180, 180]。

  3. 分母微小抖动带来的角度剧烈变化

    试想一下 Δ y Δ x \frac{\Delta y}{\Delta x} ΔxΔy,当 Δ y \Delta y Δy一定情况下, Δ x \Delta x Δx轴偏移量稍微变化点点,都会引起角度剧烈变化,显然不符合用户的真实意图,所以对于超过45度的变化,我们可以认为只改变少许角度,这里取值正负5度。

  4. 旋转角度diffDegree>0,则通过Matrix.postRotate传入角度进行旋转,结果为正数是顺时针旋转,否则是逆时针旋转

 // 计算双指旋转中心,使手势识别工具类具备识别旋转中心能力,但本次需求实际并不会使用,而是始终用画面中心来旋转
 float pivotX = (event.getX(0) + event.getX(1)) / 2;
 float pivotY = (event.getY(0) + event.getY(1)) / 2;
float deltaX = event.getX(0) - event.getX(1);
 float deltaY = event.getY(0) - event.getY(1);
 float degrees = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));

 // 计算本次旋转角度
 float diffDegree = degrees - mLastDegrees;  // 旋转角度 = 当前双指夹角 - 上次双指夹角。结果大于0 顺时针旋转;小于0逆时针旋转
 if (diffDegree > 45) {
     // y/x 分母微小抖动带来的角度剧烈变化,修正
     diffDegree = -5;
 } else if (diffDegree < -45) {
   
  diffDegree = 5;
 }

下面是旋转识别类RotateGestureDetector的所有源码

旋转识别:RotateGestureDetector

/**
 * 手势旋转识别
 * <p>
 *
 * @author yinxuming
 * @date 2020/12/22
 */
public class RotateGestureDetector {
   

    private OnRotateGestureListener mRotateGestureListener;
    private boolean mIsRotate = false;
    private float lastDegrees;


    public boolean onTouchEvent(MotionEvent event) {
   
        if (event.getPointerCount() != 2) {
   
            mIsRotate = false;
            return false;
        }
        float pivotX = (event.getX(0) + event.getX
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值