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


    Bitmap缩放和平移

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

    XML文件

    
    <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代码

    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代码

    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宽高的最小宽高比
    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()
    }
    
    • 中心缩放效果图
      在这里插入图片描述

    左上角缩放

    • 左上角不带平移缩放
    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()
    }
    
    • 左上角不带平移缩放效果图
      在这里插入图片描述

    • 左上角带平移缩放

    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()
    }
    
    • 左上角带平移缩放效果图
      在这里插入图片描述

    右上角缩放

    • 右上角不带平移缩放
    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()
    }
    
    • 右上角不带平移缩放效果图
      在这里插入图片描述
    • 右上角带平移缩放
    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()
    }
    
    • 右上角不带平移缩放效果图
      在这里插入图片描述

    左下角缩放

    • 左下角不带平移缩放
    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()
    }
    
    • 左下角不带平移缩放效果图
      在这里插入图片描述
    • 左下角平移缩放
    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()
    }
    
    • 左下角平移缩放效果图
      在这里插入图片描述

    右下角缩放

    • 右下角不带平移缩放
    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()
    }
    
    • 右下角不带平移缩放效果图

    在这里插入图片描述

    • 右下角平移缩放
    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()
    }
    
    • 右下角平移缩放效果图
      在这里插入图片描述
  • 相关阅读:
    【Java】运算符(算术运算符、赋值运算符、自增自减运算符、关系运算符、逻辑运算符、三元运算符)
    计算机图形学:纹理综述
    【Android】View 与 ViewGroup
    【0102】【内存上下文】计算AllocSet内存消耗统计信息
    关于RecyclerView的瀑布流 分割线左右间距问题
    文件操作——IO(代码演示)
    创作纪念日 && 编程练习 - 斐波那契数列
    Java代码隐患之——Arrays.asList
    vue模板语法(上)
    Web自动化测试进阶:网页中难点之等待机制 —— 强制等待,隐式等待
  • 原文地址:https://blog.csdn.net/sunshine_guo/article/details/139268651