【问题标题】:Dynamic readjustment of image size in ImageView in androidandroid中ImageView中图像大小的动态调整
【发布时间】:2018-01-26 04:52:02
【问题描述】:

我需要知道如何在不同的屏幕尺寸上自动调整图像大小。

我的活动中有一个 ImageView,我已将其 layout_heightlayout_width 分别指定为 50dp。现在我希望图像的大小相对于各种设备的屏幕尺寸/分辨率进行放大或缩小,但它以相同尺寸显示图像,因此在平板电脑中看起来太小而在手机中正好。 此外,如果我使用wrap_content 代替宽度和高度,它在手机上看起来太大而在平板上正好。

那我该如何解决呢?

附:我一直在使用 Android Emulator Nexus 4(用于手机;768x1280 xhdpi)和 Nexus 9(用于平板电脑;2048x1563 xhdpi)。

【问题讨论】:

  • 你试过使用 ImageView 的 ScaleTypes 吗?或者有什么理由你不能使用这些?或者为不同的屏幕尺寸使用@dimen?
  • @Submersed,我确实尝试在这里使用android:scaleType="fitXY",但无济于事。

标签: android


【解决方案1】:

您必须提供不同屏幕密度的图像才能支持所有设备。使用Android Asset StudioImage baker 或任何其他设计工具生成它们。

然后您需要将它们移动到您的资源目录。

res/drawable-mdpi/myimage.png
res/drawable-hdpi/myimage.png
res/drawable-xhdpi/myimage.png
res/drawable-xxhdpi/myimage.png

阅读有关Supporting different screensDesigning for Multiple Screens 的更多信息。

【讨论】:

  • 我已经在使用这种方法,但是正如问题中提到的那样,wrap_content 在单元格上给了我太大的图像,但正好适合选项卡,而将其限制为 50dp 会产生相反的结果.附:尺寸如下:mdpi (130x130), hdpi (195x195), xhdpi (260x260), xxhdpi (390x390) 和 xxxhdpi (520x520)
  • 另外,由于我在 Android 模拟器上对此进行测试,它们(Nexus 4 和 Nexus 9)都具有相同的 dpi,即问题中提到的 xhdpi,因此您提出的解决方案可能不会工作。
【解决方案2】:

我最近写了类似的东西,它可能不是最好的解决方案,因为它使用布局传递来制作动画,但它适用于我的用例。我需要缩放图像以适应容器,同时保持原始纵横比。对此有一个 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 值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-17
    • 2013-02-02
    • 1970-01-01
    • 1970-01-01
    • 2011-10-02
    • 2013-04-26
    • 1970-01-01
    相关资源
    最近更新 更多