美文网首页
自定义可缩放、滑动、双击操作的图片查看工具

自定义可缩放、滑动、双击操作的图片查看工具

作者: 壹元伍角叁分 | 来源:发表于2021-10-12 21:30 被阅读0次
class PhotoView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
    View(context, attrs, defStyleAttr) {
    private val TAG = PhotoView::class.java.simpleName

    constructor(context: Context?) : this(context, null, 0)
    constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)

    private var bitmap: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.drawable_photo)
    private val OVER_SCALE_FACTOR = 1.5f
    private var smallScaleSize = 1.0f//最小缩放比。一边撑满屏幕,另一边留白
    private var bigScaleSize = 1.0f//最大缩放比
    private var currScaleSize = 1.0f
    private val gestureDetector: GestureDetector = GestureDetector(context, PhotoGestureListener())
    private val scaleGestureDetector: ScaleGestureDetector =
        ScaleGestureDetector(context, PhotoScaleGestureDetectorListener())
    private var offsetX = 0f
    private var offsetY = 0f
    private var bitmapLeft = 0f
    private var bitmapTop = 0f
    private val overScroller = OverScroller(context)

    private val filingRunnable = object : Runnable {
        override fun run() {
            if (overScroller.computeScrollOffset()) {
                offsetX = overScroller.currX.toFloat()
                offsetY = overScroller.currY.toFloat()
                invalidate()
                //postOnAnimation是每帧动画执行一次,性能更好
                postOnAnimation(this)
            }
        }
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        val scaleValue = (currScaleSize - smallScaleSize) / (bigScaleSize - smallScaleSize)
        canvas?.translate(offsetX * scaleValue, offsetY * scaleValue)
        canvas?.scale(currScaleSize, currScaleSize, width / 2f, height / 2f)
        //在这里进行图片的绘制
        canvas?.drawBitmap(
            bitmap,
            bitmapLeft,
            bitmapTop,
            null
        )
    }

    //measure后调用,执行onLayout后才执行onDraw
    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)

        bitmapLeft = (width - bitmap.width) / 2f
        bitmapTop = (height - bitmap.height) / 2f

        val bitmapRatio = bitmap.width * 1f / bitmap.height
        val screenRatio = width * 1f / height

        if (bitmapRatio > screenRatio) {
            //说明宽要长很多。宽先撑满屏幕,高留白。等高撑满的时候,宽超过屏幕了
            smallScaleSize = width * 1f / bitmap.width
            //最大size乘以一个系数
            bigScaleSize = height * 1f / bitmap.height * OVER_SCALE_FACTOR
        } else {
            //说明高要高很多。高先撑满屏幕,宽留白。等宽撑满的时候,高超过屏幕了
            smallScaleSize = height * 1f / bitmap.height
            bigScaleSize = width * 1f / bitmap.width * OVER_SCALE_FACTOR
        }
        currScaleSize = smallScaleSize
        Log.d(TAG, "onLayout: smallScaleSize=$smallScaleSize,bigScaleSize=$bigScaleSize")
        invalidate()
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        //双指缩放优先处理
        var onTouchEvent = scaleGestureDetector.onTouchEvent(event)
        if (!scaleGestureDetector.isInProgress) {
            onTouchEvent = gestureDetector.onTouchEvent(event)
        }
        return onTouchEvent
    }

   //双指缩放处理
    inner class PhotoScaleGestureDetectorListener :
        ScaleGestureDetector.SimpleOnScaleGestureListener() {
        var currScaleScaleSize = 0f
        override fun onScale(detector: ScaleGestureDetector?): Boolean {
            detector?.run {
                currScaleSize =
                    (currScaleScaleSize * scaleFactor).coerceAtLeast(smallScaleSize)
                        .coerceAtMost(bigScaleSize)//限制最大、最小缩放比
                invalidate()
            }
            return super.onScale(detector)
        }

        override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
            currScaleScaleSize = currScaleSize
            return true//返回true,表示消费事件
        }

        override fun onScaleEnd(detector: ScaleGestureDetector?) {
            super.onScaleEnd(detector)
        }
    }

   //单指操作处理
    inner class PhotoGestureListener : GestureDetector.SimpleOnGestureListener() {
        override fun onDown(e: MotionEvent?): Boolean {
            // 返回true才会被消费
            // return super.onDown(e)
            return true
        }

        // 延时触发 100ms -- 点击效果,设置水波纹效果
        override fun onShowPress(e: MotionEvent?) {
            super.onShowPress(e)
        }

        // 单击事件抬起时触发。双击事件第二次抬起时触发
        override fun onSingleTapUp(e: MotionEvent?): Boolean {
            return super.onSingleTapUp(e)
        }

        override fun onScroll(
            e1: MotionEvent?,
            e2: MotionEvent?,
            distanceX: Float,//单位时间内滑动的距离:旧值-新值
            distanceY: Float
        ): Boolean {
            //滑动
            if (currScaleSize > smallScaleSize) {
                offsetX -= distanceX
                offsetY -= distanceY
                checkOffsetXAndOffsetY()
                invalidate()
            }
            return super.onScroll(e1, e2, distanceX, distanceY)
        }

        override fun onLongPress(e: MotionEvent?) {
            super.onLongPress(e)
        }

        //抛掷。只会开始时执行一次,接下来不会执行了
        override fun onFling(
            e1: MotionEvent?,
            e2: MotionEvent?,
            velocityX: Float,
            velocityY: Float
        ): Boolean {
            if (currScaleSize > smallScaleSize) {
                //都可滑动
                overScroller.fling(
                    offsetX.toInt(),
                    offsetY.toInt(),
                    velocityX.toInt(),//x轴速度
                    velocityY.toInt(),//y轴速度
                    -(bitmap.width * bigScaleSize - width).toInt() / 2,
                    (bitmap.width * bigScaleSize - width).toInt() / 2,
                    -(bitmap.height * bigScaleSize - height).toInt() / 2,
                    (bitmap.height * bigScaleSize - height).toInt() / 2,
                    100,//x轴回弹距离
                    100//y轴回弹距离
                )
                postOnAnimation(filingRunnable)
            }

            return super.onFling(e1, e2, velocityX, velocityY)
        }

        //单击事件触发,双击不触发。在单击up后300ms后回调
        override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
            return super.onSingleTapConfirmed(e)
        }

        //双击事件up时触发
        override fun onDoubleTap(e: MotionEvent): Boolean {
            if (currScaleSize == smallScaleSize) {
                offsetX = e.x - width / 2 - (e.x - width / 2) * bigScaleSize / currScaleSize
                offsetY = e.y - height / 2 - (e.y - height / 2) * bigScaleSize / currScaleSize
                checkOffsetXAndOffsetY()

                //当前是最小缩放状态,双击,放大到最大缩放比
                getObjectAnimator(currScaleSize, bigScaleSize).start()
            } else if (currScaleSize == bigScaleSize) {
                //当前是最大缩放状态,双击,缩小到最小缩放比
                getObjectAnimator(bigScaleSize, smallScaleSize).start()
            } else{
               //双击,放大到最大缩放比
                getObjectAnimator(currScaleSize, bigScaleSize).start()
            }
            return super.onDoubleTap(e)
        }

        //双击事件,down、move、up都会触发
        override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
            return super.onDoubleTapEvent(e)
        }

        override fun onContextClick(e: MotionEvent?): Boolean {
            return super.onContextClick(e)
        }
    }

    public fun setCurrScaleSize(currScaleSize: Float) {
        this.currScaleSize = currScaleSize
        invalidate()
    }

    private var objectAnimator: ObjectAnimator? = null
    private fun getObjectAnimator(startScaleSize: Float, endScaleSize: Float): ObjectAnimator {
        if (objectAnimator == null) {
            objectAnimator = ObjectAnimator.ofFloat(this, "currScaleSize", 0f)//必须定义setCurrScaleSize()方法,通过反射调用
        }
        objectAnimator!!.setFloatValues(startScaleSize, endScaleSize)
        objectAnimator!!.duration = 400//默认300ms
        return objectAnimator!!
    }

    private fun checkOffsetXAndOffsetY() {
        offsetX = offsetX.coerceAtMost((bitmap.width * bigScaleSize - width) / 2f)
            .coerceAtLeast(-(bitmap.width * bigScaleSize - width) / 2f)
        offsetY = offsetY.coerceAtMost((bitmap.height * bigScaleSize - height) / 2f)
            .coerceAtLeast(-(bitmap.height * bigScaleSize - height) / 2f)
    }
}

相关文章

网友评论

      本文标题:自定义可缩放、滑动、双击操作的图片查看工具

      本文链接:https://www.haomeiwen.com/subject/nvujoltx.html