【问题标题】:How to optimize a custom ImageView to be used in a GridView?如何优化要在 GridView 中使用的自定义 ImageView?
【发布时间】:2012-10-18 01:10:50
【问题描述】:

我开发了这个自定义的ImageView 类来覆盖一些默认行为以满足我的需要。让我描述一下这个自定义 ImageView 的作用......

假设你在GridViewdrawable-mdpidrawable-hdpi 文件夹中有一堆图标要显示,它们的大小分别为48x48px 和72x72px。 drawable-xhdpi 文件夹中没有可用的图标。 GridView 属性使所有图标大小都为 48x48dp(对于 mpdi、hdpi 和 xhdpi 密度,这将分别转换为 48px、72px 和 96px)。

由于drawable-xhdpi 文件夹中没有图标,因此当此应用程序在如此密度的设备上运行时,图标将从drawable-hdpi 文件夹中拉出。 由于它们只有 72 像素,而 xhdpi 设备需要 96 像素的图像,因此图标将被拉伸以填充剩余的像素。

这是我的自定义 ImageView 尝试覆盖的行为。使用我的自定义组件,将发生的情况是图像根本不会被拉伸。例如,在上面使用我的类的示例中,GridView 中的每个 ImageView 仍将是 96x96px(因为定义了 48x48dp 大小),但使用的图像来自 drawable-hdpi 文件夹,大小为 72x72px。 将会发生的情况是,drawable-hdpi 文件夹中的这些图像将被放置在尺寸为 96x96 像素的 ImageView 的中心,而不会拉伸图像以适应整个视图大小。

如果以上内容令人困惑,让我们尝试几张图片。下面的示例不使用GridView,我试图简化我的自定义类背后的想法。这些是我用于此示例的源图片:

这是 HDPI 设备上的结果:

这是 XHDPI 设备上的结果:

上面截图中的布局代码是这样的:

<?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:layout_margin="10dp"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Standard ImageView:"
        android:textAppearance="?android:attr/textAppearanceLarge"/>

    <ImageView
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_margin="10dp"
        android:scaleType="center"
        android:background="#FFEEEE"
        android:src="@drawable/ic_female"/>
    <ImageView
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_margin="10dp"
        android:scaleType="center"
        android:background="#FFEEEE"
        android:src="@drawable/ic_male"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Custom UnscaledImageView:"
        android:textAppearance="?android:attr/textAppearanceLarge"/>

    <com.sampleapp.widget.UnscaledImageView
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_margin="10dp"
        android:scaleType="center"
        android:background="#FFEEEE"
        android:src="@drawable/ic_female"/>
    <com.sampleapp.widget.UnscaledImageView
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_margin="10dp"
        android:scaleType="center"
        android:background="#FFEEEE"
        android:src="@drawable/ic_male"/>

</LinearLayout>

现在更清楚了吗?这就是我想要做的,而且效果很好,除了一个小的性能问题......现在让我发布我用于此类自定义组件的代码:

attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="UnscaledImageView">
        <attr name="android:src" />
    </declare-styleable>

</resources>

UnscaledImageView.java:

public class UnscaledImageView extends ImageView {

    private int mDeviceDensityDpi;

    public UnscaledImageView(Context context) {
        super(context);

        mDeviceDensityDpi = getResources().getDisplayMetrics().densityDpi;
    }

    public UnscaledImageView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mDeviceDensityDpi = getResources().getDisplayMetrics().densityDpi;

        TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.UnscaledImageView);
        int resourceId = styledAttrs.getResourceId(R.styleable.UnscaledImageView_android_src, 0);

        if(resourceId != 0) {
            setUnscaledImageResource(resourceId);
        }

        styledAttrs.recycle();
    }

    public void setUnscaledImageResource(int resId) {
        setImageBitmap(decodeBitmapResource(resId));
    }

    @SuppressWarnings("deprecation")
    public void setUnscaledBackgroundResource(int resId) {
        BitmapDrawable drawable = new BitmapDrawable(null, decodeBitmapResource(resId));
        drawable.setTargetDensity(mDeviceDensityDpi);
        drawable.setGravity(Gravity.CENTER);

        setBackgroundDrawable(drawable);
    }

    private Bitmap decodeBitmapResource(int resId) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inDensity = mDeviceDensityDpi;

        return BitmapFactory.decodeResource(getResources(), resId, options);
    }

}

所以,如果 UnscaledImageView 视图用于 XML 布局或直接在代码中初始化,则此类将执行此操作。我还提供了 2 种方法,因此可以在代码中更改图像,同时防止其被拉伸。如您所见,这些方法只获取资源id,到目前为止我还没有觉得需要直接使用drawables或bitmaps。

现在我遇到的真正问题...

如果这个类在某些布局中用作单个图像视图,没问题,它只解码一个图像。但是,如果它在GridView 中使用,其中可能有 40 个图标(我从我的 xhdpi 设备上运行的应用程序实际发生的情况中获取这个值)同时可见,滚动GridView 将非常很慢,因为 decodeBitmapResource() 正在为每张图片调用 BitmapFactory.decodeResource()

这是我的问题,也是我的问题。 我怎样才能优化它?如果可能的话……

【问题讨论】:

  • 我真的认为不需要自定义ImageView,因为您可以使用标准的ScaleType
  • @Rajesh 不,这与此类所做的不同。我开发了这个类,因为标准 ImageView 中的任何一个选项都没有达到我想要的效果(尤其是 ScaleType 属性)。
  • 也许我没有抓住重点,但是如何创建一个重心的布局 XML 并包含一个将 scaleType 设置为 center 的 ImageView?您能否分享您的 GridView 布局和适配器的 getView 代码?
  • @Rajesh 同样,这与我在这门课上所做的不一样。 GridView 布局和适配器无关。 GridView 是我在我的应用程序上使用的,但我还不如在 LinearLayout 上有一堆单独的 ImageView,问题会是一样的。我真的不知道如何比我在这个问题上所做的更好地解释这个问题。我花时间把它写好……如果你还是不明白,我不知道该怎么说。
  • 我刚记得的一件事我错过了解释......我举了一个例子,在 mdpi/hdpi 文件夹中有一些图像,但在 xhdpi 上没有。但在某些其他情况/图像中,特定图像也可能存在于 xhdpi 文件夹中。这个自定义 ImageView 适应了这一点。仅使用 XML 和标准 ImageView 是无法做到这一点的。

标签: android optimization android-imageview custom-component android-gridview


【解决方案1】:

将这些图像放入res/drawable-nodpi/ 可以做你想做的事(我是说将不同分辨率的图像并排放置)。

这会有点棘手,因为您可能必须遵循命名约定才能为您尝试绘制的给定图像找到最佳资源。可能这将要求您尝试按名称查找图像,这不是一种非常有效的资源检索方式。

我想象的方式是:在布局(或其他任何地方)上,指定要使用的图像资源的名称(字符串,而不是 id!)。

在该 nodpi 文件夹中,您将获得带有预期屏幕密度后缀的图像。

然后,在 setter 方法中,您必须尝试不同的组合才能找到最佳可用资源。

您要思考的问题是:如果您要缩小图像怎么办?该资源将比您绘制它的视图大!

【讨论】:

  • +1 按名称查找图像可能不是检索资源的最有效方法,但它比在滚动时尝试全部解码要好得多。
  • 这是一个想法...我将不得不考虑并测试它。至于您的最后一段,在这种特定情况下这不是问题,图像永远不会按比例缩小。
【解决方案2】:

虽然 Pedro Loureiro 的回答可能是一个可能的解决方案,但我在意识到一些事情后决定采取不同的方法......

我已经将原生 ImageView 加载和我的 UnscaledImageView 加载到 GridView 定时,当然这是非科学的,我意识到我的类加载所有图像的速度比原生快一点一。也许原生方法除了解码资源(他们仍然必须这样做,对吗?)之外还有其他事情发生,而我的课程只是解码资源(使用BitmapFactory),基本上就是这样。

我认为是我的班级让GridView 在滚动时有点慢,但经过几次测试后,使用原始的ImageView 没有任何调整,在滚动@987654329 时也显示有点不稳定@。

我发现解决此问题的解决方案(使用我的班级或本机班级)是将图标缓存在 GridView 中,为此我使用了 LruCache,这是在GridView.

这就是我将用来解决我的问题的解决方案。更多详情请参考官方培训指南:http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

作为参考,我还发现以下教程很有用:http://andrewbrobinson.com/2012/03/05/image-caching-in-android/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    • 2011-10-20
    相关资源
    最近更新 更多