Android Matrix剪切clipPath缩放scale图片postTranslate圆形放大镜,Kotlin(1)
实现查看图片的放大镜,放大镜随着手指在屏幕上的移动,放大镜里面展示手指触点为中心、半径长度的圆形放大后的图片。
剪切出一块圆形Path,然后在圆形Path画放大后的图。因为是clipPath,只会显示Path区域内,区域外不显示。
Kotlin
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Path
import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
class MainActivity : AppCompatActivity() {
private var image: MyImage? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
image = findViewById(R.id.image)
}
}
class MyImage : AppCompatImageView {
private val TAG = "fly"
private var mIsDraw = false
private var curX: Float = 0f
private var curY: Float = 0f
private var originBmp: Bitmap = (drawable as BitmapDrawable).bitmap
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event?.actionMasked) {
MotionEvent.ACTION_DOWN -> {
mIsDraw = true
curX = event.x
curY = event.y
invalidate()
}
MotionEvent.ACTION_MOVE -> {
curX = event.x
curY = event.y
invalidate()
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
mIsDraw = false
invalidate()
}
}
return true
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
if (canDraw()) {
myDraw(canvas)
}
}
private fun canDraw(): Boolean {
return mIsDraw
}
/**
* Matrix放大矩阵,然后通过clipPath剪切一块圆形区域作为放大镜区域,然后直接在clipPath的区域上绘制放大后的图。
*/
private fun myDraw(canvas: Canvas) {
Log.d(TAG, "ImageView w=${width} h=${height} Bitmap w=${originBmp.width} h=${originBmp.height}")
val path = Path()
val radius = 250f
val factor = 2f
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.color = Color.RED
paint.style = Paint.Style.STROKE
paint.strokeWidth = 10f
path.addCircle(curX, curY, radius, Path.Direction.CW)
canvas.drawPath(path, paint) //画红色的圆圈,圆心是手指在屏幕上的触点。
canvas.clipPath(path) //剪切出来一片圆形区域。
//特别注意,这里固定以手机屏幕的宽度为基准等比例放大Bitmap。
val scaleW: Float = resources.displayMetrics.widthPixels.toFloat()
val scaleH: Float = (originBmp.height.toFloat() / originBmp.width.toFloat()) * scaleW
Log.d(TAG, "display w=${scaleW} h=${scaleH}")
val matrix = Matrix()
matrix.setScale(factor, factor)
//canvas.concat(matrix)
val dx = -curX * (factor - 1)
val dy = -curY * (factor - 1)
//上下左右移动矩阵,使得放大后的矩阵处于圆圈的内容刚好是手指触点为中心的圆图。
matrix.postTranslate(dx, dy)
canvas.drawBitmap(
Bitmap.createScaledBitmap(originBmp, scaleW.toInt(), scaleH.toInt(), true),
matrix,
paint
)
}
}
XML
<com.pkg.MyImage
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@mipmap/p2" />
需要注意的,因为在xml布局里面特别设置了ImageView的高度为wrap_content,手指在屏幕触点的位置是放大镜里面放大图片后准确圆心位置,但是,如果ImageView设置成match_parent,则因为ImageView里面的Bitmap被缩放(此处Bitmap其实小于ImageView,被拉伸了),拉伸后的Bitmap水平方向坐标与ImageView一直重合,但竖直方向,Bitmap坐标与ImageView不一致,会造成一种现象,手指触点放大镜放大后,水平方向是正确的,但竖直方向有偏移量。如果想要纠正竖直方向的偏移量,可以考虑调校Bitmap竖直方向顶部与ImageView顶部的坐标偏移。