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