Android 视频手势缩放与回弹动效实现(一)

本文介绍了Android视频双指缩放和平移的实现,包括缩放吸附动效、暂停状态下缩放及动画效果。通过ScaleGestureDetector检测手势,结合Matrix进行画面缩放和平移。缩放结束时,根据缩放状态执行居中或吸附屏幕边缘的动画。


文章索引

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

Android 视频手势缩放与回弹动效实现(一)

1. 功能需求

在这里插入图片描述

  1. 双指缩放视频播放画面,支持设定最小、最大缩放范围
  2. 双指拖动画面可任意方向移动
  3. 如果是缩小画面,最后需要在屏幕居中显示,并且需要有动画效果
  4. 如果是放大画面,有画面边缘在屏幕内的,需要自动吸附到屏幕边缘
  5. 视频暂停状态下也能缩放

2. 实现原理

  1. 先进行缩放平移。
    通过View.getMatrix()获取当前播放画面的Matrix,进行矩阵变换:缩放、平移,改变画面位置和大小,实现播放画面缩放功能。
  2. 缩放结束后,进行属性动画。
    当前画面对应的矩阵变换为mScaleTransMatrix,计算动画结束应该移动的位scaleEndAnimMatrix,进行属性动画从mScaleTransMatrix变化为scaleEndAnimMatrix

2.1 如何检测手势缩放?

  1. View.onTouchEvent。分别监听手指按下(MotionEvent.ACTION_POINTER_DOWN)、抬起(MotionEvent.ACTION_POINTER_UP)、移动(MotionEvent.ACTION_MOVE
  2. 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中的手势触摸操作,得到onScaleBeginonScaleonScaleEnd三种回调,在回调里面通过VideoTouchScaleHandler对视频进行缩放、平移操作。

  1. 添加手势触摸层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) {
         
         
                return true;
            }
            return false;
        }
    
    ...
    }
    
  2. ScaleGestureDetector.OnScaleGestureListener 手势缩放回调处理

    /**
     * 手势缩放 播放画面
     */
    public class 
Android 系统中,可通过以下几种方式实现缩放图片的回弹果: ### 双指缩放、单指移回弹 实现图片双指缩放、单指移回弹果时,可参考 Android 图片双指缩放、单指移实现的方法。初始化缩放位置可使用 `mInitializationScalePoint.set( SwZoomDragImageView.this.getWidth() / 2, viewY + SwZoomDragImageView.this.getHeight() / 2)` ,以确保缩放的基准点合适 [^4]。在移时,若图片左移后离开控件右边缘,可将图片移对齐到右边缘,代码如下: ```java else if(imgWidth*values[Matrix.MSCALE_X]+values[Matrix.MTRANS_X]+ dx < getWidth()){ //图片左移后若离开控件右边缘,那么将图片移对齐到右边缘 } ``` 这里通过判断图片左移后的位置,对图片进行相应的调整,实现时的边界处理,为回弹果做铺垫 [^3]。 ### 阻尼回弹果 阻尼回弹种常用的物理果,可提供更自然的用户交互体验。它模拟物理世界中物体的弹性阻力回弹过程,常用于用户滚或拖视图达到边界后产生类似橡皮筋拉伸回弹画反馈。在实现图片缩放回弹果时,可借鉴这种思想,当图片移到边界时,模拟这种弹性阻力回弹 [^2]。 ### 自定义实现回弹 可使用 `ValueAnimator` 来实现回弹画。如 Android 自定义 ScrollView 实现放大回弹果实例代码中,通过以下代码创建个从 `distance` 到 `0.0F` 的画,并设置持续时间: ```java ValueAnimator anim = ObjectAnimator.ofFloat(distance, 0.0F).setDuration((long) (distance * mReplyRatio)); ``` 这里的 `distance` 表示图片超出边界的距离,`mReplyRatio` 是回弹比例,通过这种方式可以控制回弹的速度和时间,实现图片缩放后的回弹画 [^5]。 ### 视频手势操作的借鉴 在 Android 视频手势缩放回弹实现中,有关于视频双指缩放、平移、回弹以及旋转、缩放、平移、回弹实现方法。虽然是针对视频的操作,但其中的手势处理和回弹逻辑可以借鉴到图片处理中。例如在处理双指缩放和平移时的逻辑,以及回弹实现思路,都可以为图片的缩放回弹果提供参考 [^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值