Android 如何优化要在GridView中使用的自定义ImageView?

Android 如何优化要在GridView中使用的自定义ImageView?,android,optimization,android-imageview,custom-component,android-gridview,Android,Optimization,Android Imageview,Custom Component,Android Gridview,我开发了这个定制的ImageView类来覆盖一些默认行为以满足我的需要。让我来描述一下这个定制的ImageView的功能 假设您在可绘制mdpi和可绘制hdpi文件夹中有一组图标要显示在GridView中,它们的大小分别为48x48px和72x72px。可绘制xhdpi文件夹中没有可用的图标。GridView属性的设置使得所有图标的大小都是48x48dp(对于mpdi、hdpi和xhdpi密度,这将分别转换为48px、72px和96px) 由于drawable xhdpi文件夹中没有图标,因此当

我开发了这个定制的
ImageView
类来覆盖一些默认行为以满足我的需要。让我来描述一下这个定制的
ImageView
的功能

假设您在
可绘制mdpi
可绘制hdpi
文件夹中有一组图标要显示在
GridView
中,它们的大小分别为48x48px和72x72px。
可绘制xhdpi
文件夹中没有可用的图标。
GridView
属性的设置使得所有图标的大小都是48x48dp(对于mpdi、hdpi和xhdpi密度,这将分别转换为48px、72px和96px)

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

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

如果上面的内容令人困惑,让我们试着用一些图片。下面的示例没有使用
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>
因此,如果在XML布局中使用
UnscaledImageView
视图,或者直接在代码中初始化,那么这个类将完成它的工作。我还提供了两种方法,可以在代码中更改图像,同时防止图像被拉伸。如您所见,这些方法只接受资源ID,到目前为止,我还没有感觉到需要直接使用drawables或位图

现在我遇到的真正问题是

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


这是我的问题,那是我的问题我如何优化它?如果可能的话,至少…

将这些图像放入
res/drawable nodpi/
可以实现您想要的功能(我是说将不同分辨率的图像并排放置)

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

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

在这个nodpi文件夹中,你可以看到带有后缀的图像,以表示预期的屏幕密度

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


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

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

我对本机
ImageView
加载和
UnscaledImageView
GridView
中加载进行了计时,这当然是不科学的,我意识到我的类加载所有图像的速度比本机类快一点。也许本机方法除了解码资源(他们仍然必须这样做,对吗?)之外还有其他一些事情在进行,而我的类只是解码资源(使用
BitmapFactory
),基本上就是这样

我认为是我的类使
GridView
在滚动时有点慢,但在经过几次测试后,使用原始
ImageView
没有任何调整,在滚动
GrivView
时也显示出有点起伏

我发现解决这个问题的解决方案(无论是我的类还是本地类)是在
GridView
中缓存图标,为此我使用
LruCache
,这是在
GridView
中缓存图像的推荐方法

这就是我用来解决问题的解决方案。有关更多详细信息,请参阅官方培训指南:


作为参考,我还发现以下教程很有用:

我真的不认为需要自定义
ImageView
,因为您可以将标准视图与适当的
ScaleType一起使用,这与这个类所做的不同。我开发这个类是因为标准
ImageView
中的两个选项都没有达到我想要的效果(特别是
ScaleType
属性)。也许我没有抓住要点,但是创建一个具有重心的布局XML并包含一个
<?xml version="1.0" encoding="utf-8"?>
<resources>

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

</resources>
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);
    }

}