• Android 双向滑动


    GestureDetector //系统
    GestureDetectorCompat //AndroidX 较旧机型有更好的支持

    code

    package com.example.myapplication.view
    
    import android.animation.ObjectAnimator
    import android.content.Context
    import android.graphics.Canvas
    import android.graphics.Paint
    import android.util.AttributeSet
    import android.view.GestureDetector
    import android.view.MotionEvent
    import android.view.ScaleGestureDetector
    import android.view.View
    import android.widget.OverScroller
    import androidx.core.view.GestureDetectorCompat
    import androidx.core.view.ViewCompat
    import com.example.myapplication.dp
    import com.example.myapplication.getAvatar
    import kotlin.math.max
    import kotlin.math.min
    
    private val IMAGE_SIZE = 300.dp.toInt()
    private const val EXTRA_SCALE_FACTOR = 1.5f
    
    class ScalableImageView(context: Context, attrs: AttributeSet) : View(context, attrs) {
    
        private val runnable = FlingRunner()
        private val bitmap = getAvatar(resources, IMAGE_SIZE)
        private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        private var simpleGenListener = SimpleGenListener()
        private val scaleListener = SimpleScaleListener()
        private val scaleGenstureDetector =
            ScaleGestureDetector(context, scaleListener) //ScaleGestureDetectorCompat是扩展
    
        private var originalOffsetX = 0f
        private var originalOffsetY = 0f
    
    
    
        private var offsetX = 0f //偏移
        private var offsetY = 0f
    
        private var smallScale = 0f //小图
        private var bigScale = 0f //大图
    
        private val scaleAnimation = ObjectAnimator.ofFloat(this, "currentScale", smallScale, bigScale)
        private val gestureDetector = GestureDetectorCompat(context, simpleGenListener)  //gestureDetector.setIsLongpressEnabled(false) //关闭长按
        private var scroller = OverScroller(context) //滑动fling
        private var big = false //双击开关
    
        private var currentScale = 0f
            set(value) {
                field = value
                invalidate()
            }
    
    
        override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
            super.onSizeChanged(w, h, oldw, oldh)
            originalOffsetX = (width - bitmap.width) / 2f
            originalOffsetY = (height - bitmap.height) / 2f
    
            if (bitmap.width / bitmap.height.toFloat() > width / height.toFloat()) {
                smallScale = width / bitmap.width.toFloat()
                bigScale = height / bitmap.height.toFloat() * EXTRA_SCALE_FACTOR
            } else {
                smallScale = height / bitmap.height.toFloat()
                bigScale = width / bitmap.width.toFloat() * EXTRA_SCALE_FACTOR
            }
    
            currentScale = smallScale
            scaleAnimation.setFloatValues(smallScale, bigScale)
        }
    
    
        override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
    
            val scaleFraction = (currentScale - smallScale) / (bigScale - smallScale)
            canvas.translate(offsetX * scaleFraction, offsetY * scaleFraction)
    //        val scale = smallScale + (bigScale - smallScale) * scaleFraction
            //scale 改变后 坐标系也会改变
    //        canvas.scale(scale, scale, width / 2f, height / 2f)
            canvas.scale(currentScale, currentScale, width / 2f, height / 2f)
            canvas.drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint)
        }
    
        private fun fixOffsets() {
            offsetX = min(offsetX, (bitmap.width * bigScale - width) / 2)
            offsetX = max(offsetX, -(bitmap.width * bigScale - width) / 2)
            offsetY = min(offsetY, (bitmap.height * bigScale - height) / 2)
            offsetY = max(offsetY, -(bitmap.height * bigScale - height) / 2)
        }
    
        override fun onTouchEvent(event: MotionEvent?): Boolean {
            scaleGenstureDetector.onTouchEvent(event)
            if (!scaleGenstureDetector.isInProgress){
                gestureDetector.onTouchEvent(event)
            }
            return true
        }
    
        inner class SimpleScaleListener : ScaleGestureDetector.OnScaleGestureListener {
            override fun onScale(detector: ScaleGestureDetector): Boolean {
                val tempCurrentScale =  currentScale * detector.scaleFactor
                if (tempCurrentScale < smallScale || tempCurrentScale > bigScale){
                    return false
                }else{
                    currentScale *= detector.scaleFactor //0 max
                    return true
                }
    
    //            currentScale = currentScale.coerceAtLeast(smallScale).coerceAtMost(bigScale) //最大值
    //            return true //是否统计刚才的值是否拿到
            }
    
            override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
                offsetX = (detector.focusX - width / 2f) * (1 - bigScale / smallScale)
                offsetY = (detector.focusY - height / 2f) * (1 - bigScale / smallScale)
                fixOffsets()
                return true
            }
    
            override fun onScaleEnd(detector: ScaleGestureDetector?) {
    
            }
    
        }
    
        //  GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener,
        inner class SimpleGenListener : GestureDetector.SimpleOnGestureListener() {
            //快速滑动 velocityX 速率 位移 矢量长度
            override fun onFling(
                downEvent: MotionEvent?,
                currentEvent: MotionEvent?,
                velocityX: Float,
                velocityY: Float
            ): Boolean {
                if (big) {
                    scroller.fling(
                        offsetX.toInt(),
                        offsetY.toInt(),
                        velocityX.toInt(),
                        velocityY.toInt(),
                        (-(bitmap.width * bigScale - width) / 2).toInt(),
                        ((bitmap.width * bigScale - width) / 2).toInt(),
                        (-(bitmap.height * bigScale - height) / 2).toInt(),
                        ((bitmap.height * bigScale - height) / 2).toInt()
                    )
    
                    //下一帧调用 刷新
                    ViewCompat.postOnAnimation(this@ScalableImageView, runnable)
                }
    
                return false
            }
    
            //onMove actionMove distanceX旧位置-新位置
            override fun onScroll(
                downEvent: MotionEvent?,
                cuuentEvent: MotionEvent?,
                distanceX: Float,
                distanceY: Float
            ): Boolean {
                if (big) {
                    offsetX -= distanceX
                    offsetY -= distanceY
                    fixOffsets()
                    invalidate()
                }
    
                return false
            }
    
            //双击 判断依据 300ms,40ms以下不处理 防手抖
            override fun onDoubleTap(e: MotionEvent): Boolean {
                big = !big
                if (big) {
                    offsetX = (e.x - width / 2) * (1 - bigScale / smallScale)
                    offsetY = (e.y - height / 2) * (1 - bigScale / smallScale)
                    fixOffsets()
                    scaleAnimation.start()
                } else {
    
                    scaleAnimation.reverse()
                }
    
                return true
            }
    
    //        //双击之后的后续操作
    //        override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
    //            return false
    //        }
    
            //长按
    //        override fun onLongPress(e: MotionEvent?) {}
    //        override fun onShowPress(e: MotionEvent?) {}
    
    //        //不是长按 也不是双击回调 等300ms 没按下 会回调 双击时回调使用准备 时间不够及时
    //        override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
    //            return false
    //        }
    
            override fun onDown(e: MotionEvent?): Boolean {
                return true
            }
    
    //        //单次点击抬起 判断是否是快速抬起给系统做决定 true false 不影响结果 单击
    //        override fun onSingleTapUp(e: MotionEvent?): Boolean {
    //            return false
    //        }
        }
    
        inner class FlingRunner : Runnable {
            override fun run() {
                //当前位置 速度
                if (scroller.computeScrollOffset()) {
                    offsetX = scroller.currX.toFloat()
                    offsetY = scroller.currY.toFloat()
                    invalidate()
                    ViewCompat.postOnAnimation(this@ScalableImageView, this)
                }
    
            }
    
        }
    }
  • 相关阅读:
    【MindSpore】DCGAN生成漫画头像-----利用华为云modelarts云终端实现
    [测试] selenium自动化测试2
    Mybatis核心配置文件中的标签介绍
    java毕业设计——基于java+Eclipse+jsp的网上手机销售系统设计与实现(毕业论文+程序源码)——网上手机销售系统
    2.1 线性表的链式存储--单链表(C语言详细实现)
    利用dom4j组装xml
    认识Dubbo与RPC
    小波变换技术在图像压缩和重建中的应用研究-含Matlab代码
    本地PHP搭建简单Imagewheel私人云图床,在外远程访问
    Delphi TCP服务端监听端口获取客户端RFID网络读卡器上传的刷卡数据
  • 原文地址:https://blog.csdn.net/qq_29769851/article/details/134256485