实现查看图片的放大镜,放大镜随着手指在屏幕上的移动,放大镜里面展示手指触点为中心、半径长度的圆形放大后的图片。
剪切出一块圆形Path,然后在圆形Path画放大后的图。因为是clipPath,只会显示Path区域内,区域外不显示。
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
)
}
}
?
?
<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顶部的坐标偏移。
?
?
?