I played with loading some (rather large) images stored in the drawable folder in a ListView , and in this post I would like to share the result that I came to. Maybe I hope this saves someone a lot of time. I tested the code that I host on several Android 4+ devices, and I can say that it works quite smoothly, and the amount of RAM used remains relatively low. Some explanations are as follows:
- we are expanding
BaseAdapter - Images will be uploaded in the background using
AsyncTask - as common to adapters of this type, we will use the
ArrayList<> parameter parameterized with the Objects a custom class. In my application, this class is called Weapons. - we will scale the image depending on the screen size
- we will apply the font to the
TextView in each line of the list
Feel free to use this code for any purpose and modify it in any way. The only thing I ask is to check the code correctly before stating that something is not working. It works, believe me.
If you noticed any copy-paste errors (since I deleted some code that doesn't matter for this little tutorial), your feedback is welcome.
Before publishing the code, here is a small state diagram demonstrating the logic of the getView() method:

The code for the Adapter class is given below, I tried to explain everything you need in the comments:
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; static class WeaponHolder { public TextView text; public ImageView image, addFav; public AsyncImageSetter mImageLoader; } 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) { wp = items.get(position); if (convertView == null) { 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(); weaponHolder.mImageLoader.cancel(); } 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) {
Here is our AsyncTask , which will download and install images in the background.
NOTE. my weapon class has a getImage() method that returns resId from the drawable corresponding to the Object Weapon. You can change this part the way it works for you.
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() { 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) { 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() {
You may have noticed that I am using the getScreenWidth() method from the GetSettings class. Its code is pretty simple and returns a dp value representing the width of the device screen:
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; }
Well, thatβs all, and I hope this post really helped someone. Greetings.
PS if you are definitely sure that something is not working, most likely it was caused by the structure of your internal application, which is different from the one I use. In this case, I recommend that you follow these steps:
- Ask a new question so you can add correctly formatted LogCat code and output
- Notify me by adding a comment to my post. I will be happy to help you find out what is wrong