Demo:双向滑动的 ScalableImageView

初始页面

1.实现图片放大

两种情况,通过view和图片的宽高比来判断

public void scale (float sx, float sy) ;//以(0,0)为中心点,将画布长宽分别变为原来的sx/sy倍
public final void scale (float sx, float sy, float px, float py); //以(px,py)为中心点,将画布长宽分别变为原来的sx/sy倍


代码

smallScale效果

2.实现双击放大

2.1使用GestureDetector

private gestureDetector = GestureDetectorCompat(context,gestureListener)

⽤于在点击和⻓按之外,增加其他⼿势的监听,例如双击、滑动。通过在View.onTouchEvent() ⾥调⽤ GestureDetector.onTouchEvent() ,以代理的形式来实现:

override fun onTouchEvent(event: MotionEvent): Boolean {
 return gestureDetector.onTouchEvent(event)
}

GestureDetector的几个回调方法


\

2.2通过GestureDetector.setOnDoubleTapListener(OnDoubleTapListener)来配置双击功能:

gestureDetector.setOnDoubleTapListener(doubleTapListener);

(但其实因为GestureDetector的内部实现导致不用实现该接口)
所以总体代码为:

  private val gestureDetector: GestureDetector = GestureDetector(
    context, object :GestureDetector.OnGestureListener {
      override fun onDown(e: MotionEvent?): Boolean {
        return true
      }

      override fun onShowPress(e: MotionEvent?) {

      }

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


// distanceX: Float为当前事件的点 - 上次事件的点
      override fun onScroll(
        e1: MotionEvent?,
        e2: MotionEvent?,
        distanceX: Float,
        distanceY: Float
      ): Boolean {
        return true
      }

      override fun onLongPress(e: MotionEvent?) {

      }

      override fun onFling(
        e1: MotionEvent?,
        e2: MotionEvent?,
        velocityX: Float,
        velocityY: Float
      ): Boolean {
        return true
      }

    }).apply {
      setOnDoubleTapListener(object :GestureDetector.OnDoubleTapListener{
        override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
          
        }

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

        override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
           
        }
      })
  }

3.实现放大时可以移动

先定义手指滑动时的偏移量

  //大图模式下移动时的偏移量
  private var extraOffsetX = 0f
  private var extraOffsetY = 0f

onDraw中实现偏移效果



onScroll中修改偏移量


偏移量 = 当前偏移量 + 滑动偏移量,但应该写成-

4.实现惯性移动

OverScroller⽤于⾃动计算滑动的偏移。常⽤于 onFling() ⽅法中,调⽤ OverScroller.fling() ⽅法来启动惯性滑动的计算:

//模板
val overScroller = OverScroller(context);
override fun onFling(downEvent: MotionEvent,
currentEvent: MotionEvent, velocityX: Float, velocityY:
Float) {
 // 初始化滑动
   scroller.fling(startX, startY, velocityX, velocityY,
    minX, maxX, minY, maxY)
     // 下⼀帧刷新
     ViewCompat.postOnAnimation(this, this)
     return false
}
...
override fun run() {
   // 计算此时的位置,并且如果滑动已经结束,就停⽌
   if (scroller.computeScrollOffset()) {
   // 把此时的位置应⽤于界⾯
   offsetX = scroller.currX.toFloat()
   offsetY = scroller.currY.toFloat()
   invalidate()
   // 下⼀帧刷新
   ViewCompat.postOnAnimation(this, this)
 }
}
  override fun onFling(
    e1: MotionEvent?,
    e2: MotionEvent?,
    velocityX: Float,
    velocityY: Float
  ): Boolean {
    if (big){
      overScroller.fling(extraOffsetX.toInt(), extraOffsetY.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(),
              40.dp.toInt(), 40.dp.toInt())
      //下一帧到来时执行Runnable
      ViewCompat.postOnAnimation(this,this)
    }

    return false
  }

  override fun run() {
    //计算当前的位置,返回值是惯性运动是否还在进行
    if(overScroller.computeScrollOffset()){
      //取当前位置
      extraOffsetX = overScroller.currX.toFloat()
      extraOffsetY = overScroller.currY.toFloat()
      invalidate()
      ViewCompat.postOnAnimation(this,this)
    }
  }

目前效果

5.实现放大移动后缩小恢复到原位置

      override fun onDoubleTap(e: MotionEvent): Boolean {
        big = !big
        if (big){

          //实现放大移动后缩小恢复到原位置
          extraOffsetX = (1- bigScale / smallScale) * (e.x - width / 2)
          extraOffsetY = (1- bigScale / smallScale) * (e.y - height / 2)

          extraOffsetX = min(extraOffsetX,(bitmap.width * bigScale - width)/2)
          extraOffsetX = max(extraOffsetX,- (bitmap.width * bigScale - width)/2)
          extraOffsetY = min(extraOffsetY,(bitmap.height * bigScale - height)/2)
          extraOffsetY = max(extraOffsetY,- (bitmap.height * bigScale - height)/2)
          
          scaleAnimator.start()
        }else{
          scaleAnimator.reverse()
        }

        return true
      }
当前效果

6.实现双指缩放

ScaleGestureDetector 和ScaleGestureListener

private scaleGestureDetector =ScaleGestureDetector(context, scaleGestureListener)
class HenScaleGestureListener : OnScaleGestureListener {
 override fun onScaleBegin(detector:
ScaleGestureDetector): Boolean {
 // 捏撑开始
 return true
 }

 override fun onScaleEnd(detector:
ScaleGestureDetector) {
 // 捏撑结束
 }

 override fun onScale(detector: ScaleGestureDetector):Boolean {
   // 新的捏撑事件
   currentScale *= detector.scaleFactor
    
   // 这个返回值表示「事件是否消耗」,即「这个事件算不算数」
    //return true 则scaleFactor表示当前状态放缩系数和前一个状态的比值
  //return false则scaleFactor表示当前状态放缩系数和初始状态的比值
   return true
     }
}
  private val scaleGestureDetector = ScaleGestureDetector(context,object : ScaleGestureDetector.OnScaleGestureListener{
    override fun onScale(detector: ScaleGestureDetector): Boolean {
      val tempCurrentScale = currentScale * detector.scaleFactor
      if (tempCurrentScale < smallScale || tempCurrentScale > bigScale){
        return false
      }else{
        currentScale *= detector.scaleFactor
        return true
      }

      // 这个返回值表示「事件是否消耗」,即「这个事件算不算数」
      //return true 则scaleFactor表示当前状态放缩系数和前一个状态的比值
      //return false则scaleFactor表示当前状态放缩系数和初始状态的比值
      return true
    }

    override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
      //实现放大移动后缩小恢复到原位置
      extraOffsetX = (1- bigScale / smallScale) * (detector.focusX - width / 2)
      extraOffsetY = (1- bigScale / smallScale) * (detector.focusY - height / 2)
      return true
    }

    override fun onScaleEnd(detector: ScaleGestureDetector?) {

    }

  })

  override fun onTouchEvent(event: MotionEvent?): Boolean {
    scaleGestureDetector.onTouchEvent(event)
    if (!scaleGestureDetector.isInProgress){
      gestureDetector.onTouchEvent(event)
    }
    return true
  }

7.最终效果:

你可能感兴趣的:(Demo:双向滑动的 ScalableImageView)