在自己做的一个app中,遇到了一个需求,图片需要支持内容缩放,内容移动预览,选择了PhotoView来实现该功能,但是PhotoView使用centorCrop有一个内容裁剪的缺点,所以尝试使用自定义ImageView来实现,下面直接上代码
* Author: Sean-Shen
* Date: 2021/1/19
* Desc:
*/
import android.content.Context
import android.graphics.Matrix
import android.graphics.RectF
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import androidx.appcompat.widget.AppCompatImageView
import com.blankj.utilcode.util.LogUtils
import kotlin.math.abs
class MyImageView : AppCompatImageView {
private var oldX: Float = 0f
private var oldY: Float = 0f
private var mMatrix: Matrix? = null
private var initScale: Float = 1.0f //初始缩放值
private var totalScale: Float = 1.0f //放大时的缩放值
//缩放的优先级,比拖动的优先级高
private var mScaleGestureDetector: ScaleGestureDetector? = null
constructor(context: Context) : super(context) {
initImageView()
}
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
initImageView()
}
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(
context,
attributeSet,
defStyleAttr
) {
initImageView()
}
/**
* 初始化一些配置
*/
private fun initImageView() {
mScaleGestureDetector =
ScaleGestureDetector(context, object : ScaleGestureDetector.OnScaleGestureListener {
override fun onScale(detector: ScaleGestureDetector?): Boolean {
//要进行边界检查
detector?.scaleFactor?.let {
totalScale += it - initScale
LogUtils.e("Sean--->totalScale$totalScale")
scaleImg(it)
}
return true //返回false,持续计算放大比例,返回true,重置放大比例
}
override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
return true
}
override fun onScaleEnd(detector: ScaleGestureDetector?) {
}
})
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
val tempRectF = getMatrixRectF()
LogUtils.e("Sean--->${tempRectF.right} + ${tempRectF.left} + ${tempRectF.bottom} + ${tempRectF.top} + width:$width + height$height")
centerLayout()
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
mScaleGestureDetector?.onTouchEvent(event)
event?.let {
when (it.action) {
MotionEvent.ACTION_DOWN -> {
oldX = it.x
oldY = it.y
}
MotionEvent.ACTION_MOVE -> {
if (abs((it.x - oldX).toInt()) > 2 || abs((it.y - oldY).toInt()) > 2) {
translateBitmap(it.x - oldX, it.y - oldY)
oldX = it.x
oldY = it.y
cancelLongPress() //如果是移动事件,屏蔽长按事件,解决冲突
}
}
MotionEvent.ACTION_UP -> { //处理边界回弹
handleBorder()
}
else -> return@let
}
}
return super.onTouchEvent(event)
}
/**
* 进行图片缩放
*/
private fun scaleImg(scaleFactor: Float) {
//放大时不用进行边界检查
if (totalScale in 1.0f..2.0f) {
scaleBitmap(scaleFactor, scaleFactor)
} else if (totalScale <= 1.0f) {
//缩小时,需要处理边界问题
handleScaleBorder()
}
}
/**
* 缩放图片
*/
private fun scaleBitmap(xScale: Float, yScale: Float) {
mMatrix?.postScale(xScale, yScale)
imageMatrix = mMatrix
}
/**
* 平移图片
*/
private fun translateBitmap(offsetX: Float, offsetY: Float) {
mMatrix?.postTranslate(offsetX, offsetY)
imageMatrix = mMatrix
}
/**
* 处理边界回弹
*/
private fun handleBorder() {
var tmpOffsetX = 0f
var tmpOffsetY = 0f
val tempRectF = getMatrixRectF()
if (tempRectF.left > 0) { //左侧向右,左侧存在空白
tmpOffsetX = -tempRectF.left
}
if (tempRectF.right < this.width) { //右侧向左,右侧存在白边
tmpOffsetX = this.width - tempRectF.right
}
if (tempRectF.top > 0) { //上侧向下,上侧存在白边
tmpOffsetY = -tempRectF.top
}
if (tempRectF.bottom < this.height) { //下侧向上,下侧存在白边
tmpOffsetY = this.height - tempRectF.bottom
}
translateBitmap(tmpOffsetX, tmpOffsetY)
}
/**
* 处理缩小时的边界问题
*/
private fun handleScaleBorder() {
val tempRectF = getMatrixRectF()
if (width > height && tempRectF.left <= 0) {
val tempScaleFactor = width.toFloat() / (tempRectF.right - tempRectF.left)
scaleBitmap(tempScaleFactor, tempScaleFactor)
} else if (height >= width && tempRectF.top <= 0) {
val tempScaleFactor = height.toFloat() / (tempRectF.bottom - tempRectF.top)
scaleBitmap(tempScaleFactor, tempScaleFactor)
}
}
/**
* 获取变化之后的图片尺寸
*/
private fun getMatrixRectF(): RectF {
val rectF = RectF()
val mDrawable = drawable
mDrawable?.let {
rectF.set(0f, 0f, it.intrinsicWidth.toFloat(), it.intrinsicHeight.toFloat())
mMatrix?.mapRect(rectF)
}
return rectF
}
/**
* 将图片中间区域显示在View中
*/
private fun centerLayout() {
val tempRectF = getMatrixRectF()
val offsetX = -(tempRectF.right - tempRectF.left - width) / 2
val offsetY = -(tempRectF.bottom - tempRectF.top - height) / 2
val matrix = Matrix()
matrix.postTranslate(offsetX, offsetY)
mMatrix = matrix //要更新这个mMatrix
imageMatrix = matrix
}
/**
* 获取当前的缩放值
*/
private fun getScale(): Float {
val matrixValues = FloatArray(9)
matrix.getValues(matrixValues)
return matrixValues[Matrix.MSCALE_X]
}
}