如何将drawable
文件夹中的图像加载到ListView
中,这种方式会快速发生并且不会占用大量内存?
答案 0 :(得分:15)
注意:此帖子已弃用,请使用RecyclerView创建列表
我一直在努力将存储在drawable
文件夹中的一些(非常大的)图像加载到ListView
中,在这篇文章中,我想分享我来到的结果。也许(我希望如此)这会节省一些时间。我已经测试了我在几个Android 4+设备上发布的代码,我可以说它运行得相当顺利,并且使用的内存量相对较低。一些解释如下:
BaseAdapter
AsyncTask
ArrayList<>
参数化一些自定义类的Objects
。在我的应用程序中,此类称为武器 TextView
可以将此代码用于任何目的,并以任何方式对其进行修改。我唯一要求的是在声称某些内容不起作用之前正确测试代码。它相信,相信我。
如果您发现任何复制粘贴编辑错误(因为我删除了一些与此小教程无关的代码),欢迎您提供反馈。
在发布代码之前,这是一个展示getView()
方法逻辑的小状态图:
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 :(得分:1)
使用listview滚动状态相应地设置图像。