2

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

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

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

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

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

源文件

这是HDPI设备上的结果:

hdpi-演示

这是XHDPI设备上的结果:

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,到目前为止,我还没有觉得需要直接使用可绘制对象或位图。

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

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

这是我的问题,也是我的问题。我该如何优化呢?如果可能的话,完全...

4

2 回答 2

1

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

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

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

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

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

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

于 2012-10-15T22:29:13.543 回答
0

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

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

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

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

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

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

于 2012-10-17T17:10:50.173 回答