我最近写了类似的东西,它可能不是最好的解决方案,因为它使用布局传递来制作动画,但它适用于我的用例。我需要缩放图像以适应容器,同时保持原始纵横比。对此有一个 ScaleType,但我还需要跟踪图像的缩放程度,以便我可以转换其他点,使其相对于原始图像正确显示在放大的图像上。根据您的用例,这可能有点矫枉过正,但我是这样处理的:
用至少 2 个视图定义您的 XML,一个将作为您的 ImageView 可以填充的最大范围,另一个将是您的 ImageView 以进行缩放——我很懒,用一个不可见的视图做到了这一点(请注意,我删除了一些视图,因为它们特定于我的项目):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp">
<!-- This is the ImageView we're setting the bitmap on, that we'll be scaling -->
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:layout_centerHorizontal="true"/>
<!-- Being lazy here and using an invisible view for measuring our max bounds, use RelativeLayouts layout params to bound this appropriately to your use case i.e. layout_above, layout_below, etc -->
<View
android:id="@+id/image_max_scale"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>
在定义视图后定义 3 个矩形(我需要这些是全局的,你可能不需要——另外,我将在 Kotlin 中提供示例):
private val originalBounds = RectF()
private val maxContainerBounds = RectF()
private val finalBounds = RectF()
创建视图后,加载位图并将其设置在 ImageView 上。在 ImageView 上设置之前,将 OnPreDrawListener 添加到 View 的 ViewTreeObserver。这将允许您获取最大边界框的测量边界,以及初始设置时 ImageView 的大小(因为我们最初使用的是 wrap_content):
image.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
image.getLocalVisibleRect(originalBounds)
image_max_scale.getLocalVisibleRect(maxContainerBounds)
image.viewTreeObserver.removeOnPreDrawListener(this)
image.post {
animateImageContainerScale()
}
return true
}
})
image.setImageBitmap(it)
所以,现在我们的边界有 2 个 Rect,我们可以为 ImageView 设置动画以适应这些边界:
//Provides getWidth(), setWidth(), getHeight(), setHeight() for property animation
class BoundListener(w: Int, h: Int) {
var width: Int = w
var height: Int = h
}
private fun animateImageContainerScale() {
finalBounds.set(originalBounds)
val scaleMatrix = Matrix()
scaleMatrix.setRectToRect(originalBounds, maxContainerBounds, Matrix.ScaleToFit.CENTER)
scaleMatrix.mapRect(finalBounds, originalBounds)
val valueReceiver = BoundListener(originalBounds.width().toInt(), originalBounds.height().toInt())
val animator = ObjectAnimator.ofPropertyValuesHolder(valueReceiver,
PropertyValuesHolder.ofInt("width", finalBounds.width().toInt()),
PropertyValuesHolder.ofInt("height", finalBounds.height().toInt()))
animator.addUpdateListener {
image.layoutParams.width = valueReceiver.width
image.layoutParams.height = valueReceiver.height
image.requestLayout()
}
animator.setAutoCancel(true)
animator.start()
}
这应该为 ImageView 设置动画以适应最大边界,或者如果您想跳过动画,只需将宽度和高度设置为适当的 finalBounds 值。