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
}
ViewGroup
和View
代码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
区域,缩放区域在这两个区域大小之间变化mDestRect
和Bitmap
宽高的最小宽高比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()
}