Android:自定义View实现图片缩放及坐标的计算(上)

575 阅读3分钟

大家好,我是时曾相识2022。不喜欢唱跳篮球,但对杰伦的Rap却情有独钟。

最近项目中有一个需求:需要在一张图片上描两个点,手动输入这两点的距离,再将两点的坐标上传即可。话不多说首先直接上手撸效果:

1.gif

分析功能点:

既然是在图片上描点那就避免不了图片的放大缩小拖拽及滑动等处理,另外还涉及到放大缩小后点坐标的变化。由于需要用户描点,自定义肯定也是必不可少。还有就是描点后拖拽点的功能。

咱们一步一步来,实现一个自定义View,为了方便这里直接继承自AppCompatImageView。要想实现描点我们得监听用户的点击事件并记录两次描点的坐标。再通过onDraw()回调去绘制mark图标,最后将这两个点用虚线串联起来。

private var pointCount = 0//点的数量
private val firstPoint by lazy { PointF() }
private val lastPoint by lazy { PointF() }

//获取点信息
override fun onTouchEvent(event: MotionEvent): Boolean {
   ......
   firstPoint.apply {
       x = e.x
       y = e.y
  }
  ......
  lastPoint.apply {
       x = e.x
       y = e.y
  }  
}

//绘制两个点的mark和中间虚线
if (pointCount != 0) {
    if (firstPoint.x != 0f && firstPoint.y != 0f) {
       canvas?.drawBitmap(bitmap,(firstPoint.x - bitmap.width / 2),(firstPoint.y - bitmap.height),paint)
    }
    if (lastPoint.x != 0f && lastPoint.y != 0f) {
       canvas?.drawBitmap(bitmap,(lastPoint.x - bitmap.width / 2),(lastPoint.y - bitmap.height),paint)
    }
        }
    if (pointCount == 2) {
       canvas?.drawLine(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y, linePaint)
    }
}

对于绘制mark和线条代码相对很简单,接下来需要实现图片的缩放以及点坐标的还原。

这里咱们还得先介绍下手势识别的这两个类:ScaleGestureDetectorGestureDetector

  • ScaleGestureDetector: 缩放手势监测
  • GestureDetector:各类手势操作,包含单击、双击、滑动、长按、快速滑动等操作

其中ScaleGestureDetector自然是用来处理缩放,而GestureDetector则用来处理描点和拖拽已描的点使用,也可用于放大图片的图片移动等效果。

//缩放监听
ScaleGestureDetector(context, object : SimpleOnScaleGestureListener() {
    override fun onScale(detector: ScaleGestureDetector): Boolean {
         scale(detector)//处理缩放效果
        return true
    }
   override fun onScaleEnd(detector: ScaleGestureDetector) {
         scaleEnd(detector)//缩放结束后一些约束限制
   }
})
//手势监听
GestureDetector(context, object : SimpleOnGestureListener() {
    ......
    //按下手势
    override fun onDown(e: MotionEvent): Boolean {
       return true
    }
    //滑动手势
    override fun onScroll(e1: MotionEvent,e2: MotionEvent,distanceX: Float,distanceY: Float): Boolean {
       return true
    }
    //双击手势
    override fun onDoubleTap(e: MotionEvent): Boolean {
       //双击监听
       onDoubleDrowScale(e.x, e.y)
       return true
    }
    //快速滑动
    override fun onFling(e1: MotionEvent,e2: MotionEvent,velocityX: Float,velocityY: Float): Boolean {              
    return super.onFling(e1, e2, velocityX, velocityY)
     }
})

知道了这两种手势监听器该如何触发呢,很简单。重写onTouchEvent方法,将返回值交给这两个监听器:

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

各类手势的监听咱们有了,现在就需要对图片进行缩放功能了。如何对图片缩放呢?相信大家都知道Matrix,也就是矩阵。图片可通过它设置中心点和缩放比例:

matrix.postScale(scale, scale, x, y)

那一切都变得简单起来,我们只需要再ScaleGestureDetectoronScale方法中调用缩放效果即可。其返回的ScaleGestureDetector参数可直接获取当前坐标中心点和缩放比例。需要注意的是我们要给缩放一个上下限,在onScaleEnd方法中进行限制即可。到这里基础的缩放功能基本就做完了,但还有一个情况是快速滑动这么一个情况,即onFling回调,咱们需要创建一个动画让其平缓滑动,之前有写过关于动画的相关文章,这里就不做赘述。

至此,图片的缩放基本完成。我们还差最后一步,也就是描点的坐标在缩放后坐标点计算以及放大后再描点缩放后实际坐标的的计算。由于篇幅过长,咱们下篇继续讲解。