Android自定义view---手势识别,双击,惯性滑动,相册图片效果

本文介绍了如何在Android中使用GestureDetectorCompat实现手势识别,包括双击、惯性滑动等效果。通过定义实例、重写方法,详细讲解了双击放大缩小图片及实现惯性滑动的步骤,提供了代码优化和进阶功能,如手指点哪里放大到哪里、捏撑效果,并展示了如何使捏撑和双击手势协同工作。
摘要由CSDN通过智能技术生成


前言

在view中我们可以重写onTouchEvent来自定义点击事件,但是MotionEvent给我们的选择太少,无法满足一些个性化的需求,比如双击,惯性滑动等等,所以我们引入GestureDetectorCompat监听器来实现一些额外的功能


一、GestureDetectorCompat是什么?

GestureDetectorCompat,翻译系为手势检测器,类似于外挂,钩子,把你在屏幕上的触摸和点击截取到,去替代默认的super.onTouchEvent(event),而是走我们在手势检测器中自定义的触摸效果

二、使用步骤

1.定义一个GestureDetectorCompat的实例

代码如下:

private val gestureDetectorCompat = GestureDetectorCompat(context, this)

第二个参数为listener,让view实现GestureDetector.OnGestureListener接口,我们就可以直接填入this,但是要去重写抽象方法。

    override fun onDown(e: MotionEvent?): Boolean {
   
        TODO("Not yet implemented")
    }

    override fun onShowPress(e: MotionEvent?) {
   
        TODO("Not yet implemented")
    }

    override fun onSingleTapUp(e: MotionEvent?): Boolean {
   
        TODO("Not yet implemented")
    }

    override fun onScroll(
        e1: MotionEvent?,
        e2: MotionEvent?,
        distanceX: Float,
        distanceY: Float
    ): Boolean {
   
        TODO("Not yet implemented")
    }

    override fun onLongPress(e: MotionEvent?) {
   
        TODO("Not yet implemented")
    }

    override fun onFling(
        e1: MotionEvent?,
        e2: MotionEvent?,
        velocityX: Float,
        velocityY: Float
    ): Boolean {
   
        TODO("Not yet implemented")
    }

2.重写方法,实现自定义效果

①想让检测器消费一系列的触摸事件,那么就要在重写方法onDown中去返回true,那么后续的一系列触摸过程才能让手势检测器获取

代码如下:

    override fun onDown(e: MotionEvent?): Boolean {
   
        return true
    }

②既然我们要去实现双击效果,那么就得再给手势检测器设置一个监听

    private val gestureDetectorCompat = GestureDetectorCompat(context, this).apply {
   
        setOnDoubleTapListener(this@ScalableImageView)
    }

setOnDoubleTapListener的参数是传入一个listener,那么我们还是填入view,让view去重写接口GestureDetector.OnDoubleTapListener的方法即可
重写的方法:

    override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
   
        TODO("Not yet implemented")
    }

    override fun onDoubleTap(e: MotionEvent?): Boolean {
   
        TODO("Not yet implemented")
    }

    override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
   
        TODO("Not yet implemented")
    }

简化:
在这里插入图片描述
追踪GestureDetectorCompat的构造函数的源码我们可以看到,内部帮我们判断了listener的类型,如果是GestureDetector.OnDoubleTapListene的实现类,那么就会帮我们去执行setOnDoubleTapListener方法,不用我们再去配置,所以代码可以简化为:

    private val gestureDetectorCompat = GestureDetectorCompat(context, this)

③实现双击变大变小

效果:
在这里插入图片描述

④重写onDoubleTap

目的是双击实现图片的放大缩小,所以我们做一个动画,控制图片的大小

    private var scaleFraction = 0f
        set(value) {
   
            field = value
            invalidate()
        }
    private val animator by lazy {
    ObjectAnimator.ofFloat(this, "scaleFraction", 0f, 1f) }

ondraw中,拿到要缩放的比例系数,实现从小图片到大图片

            val scale = smallScale + (bigScale - smallScale) * scaleFraction
            scale(scale, scale, width / 2f, height / 2f)
            drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint)

所以在实现双击的方法中,从小到大就是正常播放动画,从大到小就是反向播放动画

    override fun onDoubleTap(e: MotionEvent?): Boolean {
   
        if (isBig) {
   
            //本来是大图片,那么就从小到大
            animator.reverse()
        } else {
   
            animator.start()
        }
        isBig = !isBig
        return true
    }

④实现惯性滑动

核心是重写onFling方法
创建一个OverScroller对象

private val overScroller = OverScroller(context)

OverScroller的作用:控制一个点在一定范围内的惯性滑动
如图所示
在这里插入图片描述
那么如何控制一张图片的惯性移动呢?
在这里插入图片描述
把图片在大框中的移动等价为触摸点在小框中的移动,触摸点在x,y轴上移动的位移,同步到图片在xy轴上的偏移,那么就可以实现图片的惯性
说白了就是把你的触摸点控制在一个小框的范围内,就可以把图片控制在大框内
实现步骤:
①:定义两个变量作为小圆在x轴上移动的偏移,y轴上移动的偏移

    private var offsetX = 0f
    private var offsetY = 0f

②:重写onFling方法

    override fun onFling(
        e1: MotionEvent?,
        e2: MotionEvent?,
        velocityX: Float,
        velocityY: Float
    ): Boolean {
   
        overScroller.fling(
            offsetX.toInt(),
            offsetY.toInt(),
            velocityX.toInt(),
            velocityY.toInt(),
            (-(bitmap.width * bigScale - width) / 2f).toInt(),
            ((bitmap.width * bigScale - width) / 2f).toInt(),
            (-(bitmap.height * bigScale - height) / 2f).toInt(),
            ((bitmap.height * bigScale - height) / 2f).toInt(), 40.dp.toInt(), 40.dp.toInt()
        )
        postOnAnimation(this)
        return false
    }

第一个参数和第二个参数:手指点击下去时的位置
第三第四个参数:手指用力滑动时,在两个方向上的速度
第五第六个参数:包围触摸点的小框的范围
最后两个参数指滑出小框边界时,超出又恢复的范围
如下图所示的效果
在这里插入图片描述
总结:把触摸点的位移同步给图片
③实现流畅的惯性滑动
让view实现Runnable接口,重写run方法,按帧去更新滑动

    override fun run() {
   
        //只要惯性还没结束,就递归去更新图片位置
        if (overScroller.computeScrollOffset()) {
   
            offsetX = overScroller.currX.toFloat()
            offsetY = overScroller.currY.toFloat()
            invalidate()
            postOnAnimation(this)
        }
    }

④在onFling中使用run方法

postOnAnimation
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值