View->Bitmap缩放到自定义ViewGroup的任意区域(Matrix方式绘制Bitmap)

Bitmap缩放和平移

  • 加载一张Bitmap可能为宽高相同的正方形,也可能为宽高不同的矩形
  • 缩放方向可以为中心缩放,左上角缩放,右上角缩放,左下角缩放,右下角缩放
  • Bitmap中心缩放,包含了缩放和平移两个操作,不可拆开
  • Bitmap其余四个方向的缩放,可以单独缩放不带平移,也可以缩放带平移

XML文件

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.yang.app.MyRelativeLayout
        android:id="@+id/real_rl"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_marginTop="30dp"
        android:layout_marginBottom="30dp"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:background="@color/gray">
        <com.yang.app.MyImageView
            android:id="@+id/real_iv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="30dp"
            android:layout_marginBottom="30dp"
            android:layout_marginLeft="30dp"
            android:layout_marginRight="30dp" />
    </com.yang.app.MyRelativeLayout>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_weight="0"
        android:background="#00ff00" />
</LinearLayout>

Activity代码

kotlin 复制代码
const val TAG = "Yang"
class MainActivity : AppCompatActivity() {
    var mRealView : MyImageView ?= null
    var mRelativeLayout : MyRelativeLayout ?= null
    var tempBitmap : Bitmap ?= null
    var mHandler = Handler(Looper.getMainLooper())
    var screenWidth = 0
    var screenHeight = 0
    var srcRect = RectF()
    var destRect = RectF()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mRealView = findViewById(R.id.real_iv)
        mRelativeLayout = findViewById(R.id.real_rl)
        // 屏幕宽高的一半作为临时RectF, 用于压缩Bitmap
        screenWidth = resources.displayMetrics.widthPixels
        screenHeight = resources.displayMetrics.heightPixels
        val tempRect = RectF(0f, 0f, screenWidth.toFloat() / 2, screenHeight.toFloat() / 2)
        
        
        CoroutineScope(Dispatchers.IO).launch {
            tempBitmap = getBitmap(resources, tempRect, R.drawable.fake)
            withContext(Dispatchers.Main) {
                mRelativeLayout?.post {
                    // 获取初始区域的RectF
                    srcRect.set(
                        0f,
                        0f,
                        mRelativeLayout?.width?.toFloat()!!,
                        mRelativeLayout?.height?.toFloat()!!
                    )
                    // 获取结束区域的RectF
                    mRelativeLayout?.forEach { childView ->
                        if (childView is MyImageView) {
                            destRect.set(
                                0f,
                                0f,
                                 childView.width.toFloat(),
                                childView.height.toFloat()
                            )
                        }
                    }
                    scaleRectFun(tempBitmap, srcRect, destRect)
                }
            }
        }
    }
    
    fun scaleRectFun(tempBitmap: Bitmap?, srcRect : RectF, destRect: RectF){
        tempBitmap?.let { bitmap->
            mRelativeLayout?.setBitmap(bitmap)
            val animator = ValueAnimator.ofFloat(0f, 1f)
            animator.duration = 5000L
            animator.interpolator = DecelerateInterpolator()
            animator.addUpdateListener {
                val value = it.animatedValue as Float
                // 中心缩放
                mRelativeLayout?.setDestRectCenterWithTranslate(srcRect, destRect, value)
                // 左上角不带平移缩放
                // mRelativeLayout?.setDestRectLeftTopNoTranslate(srcRect, destRect, value)
                // 左上角平移缩放
                // mRelativeLayout?.setDestRectLeftTopWithTranslate(srcRect, destRect, value)
                // 右上角不带平移缩放
                // mRelativeLayout?.setDestRectRightTopNoTranslate(srcRect, destRect, value)
                // 右上角平移缩放
                // mRelativeLayout?.setDestRectRightTopWithTranslate(srcRect, destRect, value)
                // 左下角不带平移缩放
                // mRelativeLayout?.setDestRectLeftBottomNoTranslate(srcRect, destRect, value)
                // 左下角平移缩放
                // mRelativeLayout?.setDestRectLeftBottomWithTranslate(srcRect, destRect, value)
                // 右下角不带平移缩放
                // mRelativeLayout?.setDestRectRightBottomNoTranslate(srcRect, destRect, value)
                // 右下角平移缩放
                // mRelativeLayout?.setDestRectRightBottomWithTranslate(srcRect, destRect, value)
            }
            animator.start()
        }
    }
}
fun getBitmap(resources : Resources, destRect : RectF, imageId: Int): Bitmap? {
    var imageWidth = -1
    var imageHeight = -1
    val preOption = BitmapFactory.Options().apply {
        // 只获取图片的宽高
        inJustDecodeBounds = true
        BitmapFactory.decodeResource(resources, imageId, this)
    }
    imageWidth = preOption.outWidth
    imageHeight = preOption.outHeight
    // 计算缩放比例
    val scaleMatrix = Matrix()
    // 确定未缩放Bitmap的RectF
    var srcRect = RectF(0f, 0f, imageWidth.toFloat(), imageHeight.toFloat())
    // 通过目标RectF, 确定缩放数值,存储在scaleMatrix中
    scaleMatrix.setRectToRect(srcRect, destRect, Matrix.ScaleToFit.CENTER)
    // 缩放数值再映射到原始Bitmap上,得到缩放后的RectF
    scaleMatrix.mapRect(srcRect)

    val finalOption = BitmapFactory.Options().apply {
        if (imageHeight > 0 && imageWidth > 0) {
            inPreferredConfig = Bitmap.Config.RGB_565
            inSampleSize = calculateInSampleSize(
                imageWidth,
                imageHeight,
                srcRect.width().toInt(),
                srcRect.height().toInt()
            )
        }
    }
    return BitmapFactory.decodeResource(resources, imageId, finalOption)
}

fun calculateInSampleSize(fromWidth: Int, fromHeight: Int, toWidth: Int, toHeight: Int): Int {
    var bitmapWidth = fromWidth
    var bitmapHeight = fromHeight
    if (fromWidth > toWidth|| fromHeight > toHeight) {
        var inSampleSize = 2
        // 计算最大的inSampleSize值,该值是2的幂,并保持原始宽高大于目标宽高
        while (bitmapWidth >= toWidth && bitmapHeight >= toHeight) {
            bitmapWidth /= 2
            bitmapHeight /= 2
            inSampleSize *= 2
        }
        return inSampleSize
    }
    return 1
}

自定义ViewGroupView代码

kotlin 复制代码
class MyImageView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr)


class MyRelativeLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr) {
    var mBitmap : Bitmap ?= null
    var mScaleMatrix = Matrix()
    var mDestRect = RectF()

    fun setBitmap(bitmap: Bitmap?) {
        mBitmap = bitmap
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        mBitmap?.let {
            canvas?.drawBitmap(it, mScaleMatrix, Paint().apply {
                isAntiAlias = true
            })
        }
    }
}

中心缩放

  • ValueAnimator动画的初始值为0,开始区域为外层MyRelativeLayout区域,结束区域为内层MyImageView区域,缩放区域在这两个区域大小之间变化
  • 缩放比例取决于当前缩放区域mDestRectBitmap宽高的最小宽高比
kotlin 复制代码
fun setDestRectCenterWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = (srcRect.width() - mBitmap?.width!! * scale) / 2
    val dy = (srcRect.height() - mBitmap?.height!! * scale) / 2
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 中心缩放效果图

左上角缩放

  • 左上角不带平移缩放
kotlin 复制代码
fun setDestRectLeftTopNoTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    mScaleMatrix.setScale(scale, scale)
    invalidate()
}
  • 左上角不带平移缩放效果图

  • 左上角带平移缩放

kotlin 复制代码
fun setDestRectLeftTopWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = ((srcRect.width() - destRect.width())/2) * value + (destRect.left - srcRect.left) * value
    val dy = ((srcRect.height() - destRect.height())/2) * value + (destRect.top - srcRect.top) * value
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 左上角带平移缩放效果图

右上角缩放

  • 右上角不带平移缩放
kotlin 复制代码
fun setDestRectRightTopNoTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left  + (destRect.left - srcRect.left) * value,
        srcRect.top + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = srcRect.width() - mBitmap!!.width * scale
    val dy = 0f
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 右上角不带平移缩放效果图
  • 右上角带平移缩放
kotlin 复制代码
fun setDestRectRightTopWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left  + (destRect.left - srcRect.left) * value,
        srcRect.top + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = ((srcRect.width() - destRect.width())/2) * value + (destRect.left - srcRect.left) * value
    val dy = ((srcRect.height() - destRect.height())/2) * value + (destRect.top - srcRect.top) * value
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 右上角不带平移缩放效果图

左下角缩放

  • 左下角不带平移缩放
kotlin 复制代码
fun setDestRectLeftBottomNoTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = 0f
    val dy = srcRect.height() - mBitmap?.height!! * scale
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 左下角不带平移缩放效果图
  • 左下角平移缩放
kotlin 复制代码
fun setDestRectLeftBottomWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = ((srcRect.width() - destRect.width())/2 )* value
    val dy = ((srcRect.height() - destRect.height())/2 )* value + (destRect.bottom - srcRect.bottom) * value + (srcRect.height()- mBitmap?.height!! * scale)
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 左下角平移缩放效果图

右下角缩放

  • 右下角不带平移缩放
kotlin 复制代码
fun setDestRectRightBottomNoTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = srcRect.width() - mBitmap!!.width * scale
    val dy = srcRect.height() - mBitmap?.height!! * scale
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 右下角不带平移缩放效果图
  • 右下角平移缩放
kotlin 复制代码
fun setDestRectRightBottomWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = ((srcRect.width() - destRect.width())/2 )* value
    val dy = ((srcRect.height() - destRect.height())/2 )* value + (destRect.bottom - srcRect.bottom) * value + (srcRect.height()- mBitmap?.height!! * scale)
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 右下角平移缩放效果图
相关推荐
q5673152313 分钟前
在 Bash 中获取 Python 模块变量列
开发语言·python·bash
许野平38 分钟前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
也无晴也无风雨41 分钟前
在JS中, 0 == [0] 吗
开发语言·javascript
无极程序员1 小时前
PHP常量
android·ide·android studio
狂奔solar1 小时前
yelp数据集上识别潜在的热门商家
开发语言·python
blammmp1 小时前
Java:数据结构-枚举
java·开发语言·数据结构
何曾参静谧2 小时前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++
暗黑起源喵2 小时前
设计模式-工厂设计模式
java·开发语言·设计模式
WaaTong2 小时前
Java反射
java·开发语言·反射
Troc_wangpeng2 小时前
R language 关于二维平面直角坐标系的制作
开发语言·机器学习