如何有效地将可绘制文件夹中的位图加载到ListView中?

时间:2013-10-19 17:44:37

标签: android android-listview

如何将drawable文件夹中的图像加载到ListView中,这种方式会快速发生并且不会占用大量内存?

2 个答案:

答案 0 :(得分:15)

注意:此帖子已弃用,请使用RecyclerView创建列表


我一直在努力将存储在drawable文件夹中的一些(非常大的)图像加载到ListView中,在这篇文章中,我想分享我来到的结果。也许(我希望如此)这会节省一些时间。我已经测试了我在几个Android 4+设备上发布的代码,我可以说它运行得相当顺利,并且使用的内存量相对较低。一些解释如下:

  • 我们正在扩展BaseAdapter
  • 图片将使用AsyncTask
  • 在后台加载
  • 对于这种适配器来说很常见,我们将使用ArrayList<>参数化一些自定义类的Objects。在我的应用程序中,此类称为武器
  • 我们将根据屏幕尺寸缩放图像
  • 我们会将字体应用于每个列表行中的TextView

可以将此代码用于任何目的,并以任何方式对其进行修改。我唯一要求的是在声称某些内容不起作用之前正确测试代码。它相信,相信我。

如果您发现任何复制粘贴编辑错误(因为我删除了一些与此小教程无关的代码),欢迎您提供反馈。

在发布代码之前,这是一个展示getView()方法逻辑的小状态图:

enter image description here

Adapter课程的代码如下:我试图在评论中解释您需要的所有内容:

public class WeaponAdapter extends BaseAdapter implements View.OnClickListener {

private ArrayList<Weapon> items;
private LayoutInflater inflater = null;
private WeaponHolder weaponHolder;
private Weapon wp;
private Context c;
private Bitmap bmp;

/*--- a simple View Holder class ---*/
static class WeaponHolder {
    public TextView text;
    public ImageView image, addFav;
    public AsyncImageSetter mImageLoader;

}

/*--- Context and all weapons of specified class are passed here ---*/

public WeaponAdapter(ArrayList<Weapon> items, Context c) {
    this.items = (ArrayList<Weapon>) items;
    inflater = LayoutInflater.from(c);
    this.c = c;
}

@Override
public int getCount() {
    return items.size();

}

@Override
public Weapon getItem(int position) {
    return items.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    /*--- initialize our Weapon Object ---*/
    wp = items.get(position);

    if (convertView == null) {
        /*--- no View is available. Inflate our list item layout and init the Views we need ---*/
        convertView = inflater.inflate(R.layout.category_row, null);
        weaponHolder = new WeaponHolder();
        weaponHolder.text = (TextView) convertView
                .findViewById(R.id.tvCatText);
        weaponHolder.image = (ImageView) convertView
                .findViewById(R.id.imgCatImage);
        weaponHolder.addFav = (ImageView) convertView
                .findViewById(R.id.imgAddFav);
        convertView.setTag(weaponHolder);
    } else {
        weaponHolder = (WeaponHolder) convertView.getTag();
        /*--- if convertView is not null, cancel the current loading operation to 
         * improve performance and decrease RAM usage ---*/
        weaponHolder.mImageLoader.cancel();
    }
    /*--- load the image in background ---*/
    weaponHolder.mImageLoader = new AsyncImageSetter(c, weaponHolder.image,
            wp.getImage(), bmp, weaponHolder.text);
    weaponHolder.mImageLoader.execute();
    weaponHolder.text.setText(wp.getName());
    weaponHolder.addFav.setOnClickListener(this);
    return convertView;

}

@Override
public void onClick(View v) {


        // do any stuff here



}
   }

这是我们的AsyncTask,它将在后台加载和设置图像。

注意:我的武器类有getImage()方法,该方法返回与武器drawable对应的Object的resId。您可以按照适合自己的方式修改此部件。

public class AsyncImageSetter extends AsyncTask<Void, Void, Bitmap> {

private ImageView img;
private int image_resId;
private Bitmap bmp;
private Context c;
private boolean cancel = false;
private int sampleSize;
private TextView txtGunName;
private Typeface font;


public AsyncImageSetter(Context c, ImageView img, int image_ResId,
        Bitmap bmp, TextView txtGunName) {

    this.img = img;
    this.image_resId = image_ResId;
    this.bmp = bmp;
    this.c = c;
    this.txtGunName = txtGunName;

}

public void cancel() {
    cancel = true;
}

@Override
protected void onPreExecute() {
    /*--- we hide the Views from the user until the content is ready. This will prevent
     * the user from seeing an image being "transformed" into the next one (as a result of
     * View recycling) on slow devices. 
     */
    img.setVisibility(View.GONE);
    txtGunName.setVisibility(View.GONE);
    font = Typeface.createFromAsset(c.getAssets(), "b_reg.otf");
    super.onPreExecute();
}

@Override
protected Bitmap doInBackground(Void... params) {

    if (!cancel) {
        try {
            return decodeAndScale(bmp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return null;
}

@Override
protected void onPostExecute(Bitmap result) {

    img.setVisibility(View.VISIBLE);
    try {
        img.setImageBitmap(result);
    } catch (Exception e) {
        /*--- show an error icon in case something went wrong ---*/
        img.setImageResource(R.drawable.ic_warn);
    }

    txtGunName.setVisibility(View.VISIBLE);
    txtGunName.setTypeface(font);
    super.onPostExecute(result);
}

private Bitmap decodeAndScale(Bitmap bmp) {

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = setSampleSize();

    return BitmapFactory.decodeResource(c.getResources(), image_resId,
            options);

}

private int setSampleSize() {

    // TODO add multiple screens check
/*--- modify this method to match your needs ---*/
    if (GetSettings.getScreenWidth((Activity) c) >= 320) {
        /*--- physical width >= 480px ---*/
        sampleSize = 2;
    }

    return sampleSize;
}}

您可能已经注意到我使用了getScreenWidth()类中的GetSettings方法。它的代码非常简单,并返回表示设备屏幕宽度的dp值:

    public static int getScreenWidth(Activity a) {

    Display display = a.getWindowManager().getDefaultDisplay();
    DisplayMetrics outMetrics = new DisplayMetrics();
    display.getMetrics(outMetrics);

    float density = a.getResources().getDisplayMetrics().density;
    float dpWidth = outMetrics.widthPixels / density;

    return (int) dpWidth;

}

嗯,这就是全部,我希望这篇文章对某人有所帮助。欢呼声。

P.S。如果你肯定确定某些东西不起作用,很可能是因为你的内部应用程序结构与我使用的不同。在这种情况下,我建议您执行以下步骤:

  1. 提出一个新问题,这样您就可以添加格式正确的代码和LogCat输出
  2. 通过在我的帖子中添加评论来通知我。我很乐意帮你弄清楚什么是错的

答案 1 :(得分:1)

使用listview滚动状态相应地设置图像。

  1. 当列表视图被剔除时,不要同时正确设置图像,一旦状态空闲,请使用代码设置图像。 通过这种方式,它将避免为不可见的视图分配内存。如果在滚动列表视图时尝试设置图像,则可能导致内存不足错误。
  2. 此外,请确保您的drawable具有良好的尺寸以适合视野。从drawable获取图像一直很快,这里主要关注的是内存使用情况。
  3. 同时创建一个drawables数组以设置为列表项,因此在设置适配器时,您已经拥有一个可用于列表项的drawables,并且不会根据条件使用drawable填充列表视图