ListView - Images shuffle while scrolling

2019-04-29 08:45发布

I have a ListView with two TextViews and one ImageView. The images are loaded from the Internet and are cached by LruCache. While scrolling through the ListView the images are getting shuffled for a few seconds. Ther just shouldn't be any image until the right image is fully loaded. I found a few questions with the same problem but no one helped me :/. Here's my code:

public class NewsAdapter extends BaseAdapter {
    private static LayoutInflater inflater;
    private List<Item> items = new LinkedList<Item>();
    private LruCache<String, Bitmap> mMemoryCache;

    public NewsAdapter(Context context) {
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        // Bitmap Cache
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return getSizeInBytes(bitmap) / 1024;
            }
        };
    }

    public void add(Item item) {
        items.add(item);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = new ViewHolder();
        if(convertView == null) {
            convertView = inflater.inflate(R.layout.list_item_item, null);
            viewHolder.ivPic = (ImageView) convertView.findViewById(R.id.ivPic);
            viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tvTitle);
            viewHolder.tvShortDesc = (TextView) convertView.findViewById(R.id.tvShortDesc);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        final Item item = items.get(position);
        viewHolder.tvTitle.setText(item.getTitle());
        viewHolder.tvShortDesc.setText(Html.fromHtml(item.getShortDesc()));

        Bitmap bitmap = mMemoryCache.get(item.getPicUrl());
        if (bitmap != null) {
            viewHolder.ivPic.setImageBitmap(bitmap);
        } else {
            GetBitmap gb = new GetBitmap(item.getPicUrl(), viewHolder.ivPic);
            gb.execute();
        }

        return convertView;
    }

    static class ViewHolder {
        ImageView ivPic;
        TextView tvTitle;
        TextView tvShortDesc;
    }

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

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

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

    @SuppressLint("NewApi")
    public static int getSizeInBytes(Bitmap bitmap) {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            return bitmap.getByteCount();
        } else {
            return bitmap.getRowBytes() * bitmap.getHeight();
        }
    }

    private class GetBitmap extends AsyncTask<Void, Bitmap, Bitmap> {
        private String url;
        private ImageView ivPic;

        public GetBitmap(String url, ImageView ivPic) {
            this.url = url;
            this.ivPic = ivPic;
        }

        @Override
        protected Bitmap doInBackground(Void... params) {
            Bitmap bitmap = null;
            try {
                URL url = new URL(this.url);
                bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
            } catch (Exception e) {
                e.printStackTrace();
            }

            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            if (bitmap != null) ivPic.setImageBitmap(bitmap); 
        }

    }

}

It would be nice if anyone has an idea... Thanks in advance!

P.S.: I forgot something, please do not suggest any libraries, I want to do it without any external library.

2条回答
男人必须洒脱
2楼-- · 2019-04-29 09:03

It could possibly be this codesnippet

 ViewHolder viewHolder = new ViewHolder();

That is everytime getView is called a new instance of viewHolder is created.

Try this instead:

   final ViewHolder viewHolder;

and then in the if-structure

    viewHolder = new ViewHolder();

Implemented in your code:

  @Override
public View getView(int position, View convertView, ViewGroup parent) {
   final ViewHolder viewHolder; 

    if(convertView == null) {
        convertView = inflater.inflate(R.layout.list_item_item, null);
        viewHolder = new ViewHolder();
        viewHolder.ivPic = (ImageView) convertView.findViewById(R.id.ivPic);
        viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tvTitle);
        viewHolder.tvShortDesc = (TextView) convertView.findViewById(R.id.tvShortDesc);
        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
    }

    final Item item = items.get(position);
    viewHolder.tvTitle.setText(item.getTitle());
    viewHolder.tvShortDesc.setText(Html.fromHtml(item.getShortDesc()));

    Bitmap bitmap = mMemoryCache.get(item.getPicUrl());
    if (bitmap != null) {
        viewHolder.ivPic.setImageBitmap(bitmap);
    } else {
        GetBitmap gb = new GetBitmap(item.getPicUrl(), viewHolder.ivPic);
        gb.execute();
    }

    return convertView;
}
查看更多
放我归山
3楼-- · 2019-04-29 09:09
public GetBitmap(String url, ImageView ivPic, int position) {
    this.url = url;
    this.ivPic = ivPic;
    this.position = position;
    ivPic.setTag(position);
    ivPic.setImageBitmap(null);
}

@Override
protected void onPostExecute(Bitmap bitmap) {
     super.onPostExecute(bitmap);
     if(bitmap != null && ((Integer)getTag) == this.position)
         ivPic.setImageBitmap(bitmap); 
}

Issue is you are not checking if the image is in the same position or not. Try the above code hope it will help.

查看更多
登录 后发表回答