How to avoid image flickering in a listview

て烟熏妆下的殇ゞ 提交于 2019-12-01 06:49:41

The reason for this flicker is that, in listview list items are reused. When re-used, the imageviews in the list item retains the old image reference which is displayed first. Later on once new image is downloaded, it starts to show. this causes the flickering behavior. To avoid this flickering issue, always clear the old image reference from the imageview when it is getting reused.

In your case, add holder.image.setImageBitmap(null); after holder = (ViewHolder) convertView.getTag();

So, your getView() method will look like:

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

    ...

    if (convertView == null) {
        LayoutInflater inflater = getLayoutInflater();
        convertView = inflater.inflate(viewResourceId, null);

        holder = new ViewHolder(convertView);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
        holder.image.setImageBitmap(null)
    }

    ...

    return convertView;
}

After hours of research, i was able to know the method that i can use to calculate new imageview height while maintaining image aspect ratio.

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;

//Returns null, sizes are in the options variable
BitmapFactory.decodeFile("/sdcard/image.png", options);
int width = options.outWidth;
int height = options.outHeight;

//calculating image aspect ratio
float ratio =(float) height/(float) width;

//calculating my image height since i want it to be 360px wide
int newHeight = Math.round(ratio*360);

//setting the new dimentions
 imageview.getLayoutParams().width = 360;
 imageview.getLayoutParams().height = newHeight;

 //i'm using universal image loader to display image
 imaheview.post(new Runnable(){
  ImageLoader.getInstance().displayImage(imageuri,imageview,displayoptions);
 });

You can do something like this :

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

//Returns null, sizes are in the options variable
BitmapFactory.decodeFile("/sdcard/image.png", options);
int width = options.outWidth;
int height = options.outHeight;
//If you want, the MIME type will also be decoded (if possible)
String type = options.outMimeType;
olushola.lana

How I solved it was by creating a Bitmap[] array variable to store images, then in adapter's getView(), I used position to check if image in Bitmap[] array is null or has value. If it is has value, then I use the value instead of calling the new DownloadImageTask() construct again.

For example:

YourCustomArrayAdapter.java

public class MyCustomArrayAdapter extends ArrayAdapter {
   private static Bitmap[] myListViewImageViewsArray = new Bitmap[listViewItemsArray.length];
   private String[] myListViewImageURLsArray = new String[listViewItemsArray.length]{
      "image_url_1",
      "image_url_2",
      ...
      ...
   };


   @Override
   public View getView(int position, View view, ViewGroup parent){
       CustomViewHolder vHolder;
        if(view == null){
            view = inflater.inflate(R.layout.movies_coming_soon_content_template, null, true);
            vHolder = new CustomViewHolder();
            vHolder.imageView = (AppCompatImageView) view.findViewById(R.id.my_cutom_image);
            vHolder.imageUrl = "";
            view.setTag(vHolder);
        }
        else{
            vHolder = (CustomViewHolder)view.getTag();
            // -- Set imageview src to null or some predefined placeholder (this is not really necessary but it might help just to flush any conflicting data hanging around)
            vHolder.imageView.setImageResource(null);
        }

       // ... 


       // -- THIS IS THE MAIN PART THAT STOPPED THE FLICKERING FOR ME
       if(myListViewImageViewsArray[position] != null){
          vHolder.imageView.setImageBitmap(myListViewImageViewsArray[position]);
       }else{
          new DownloadImageTask(position, vHolder.imageView).execute(vHolder.imageUrl);
       }
       // -- END OF THE FLICKERING CONTROL    
   }
}

Then, in your image downloader construct, after downloading the image, make an insertion into the Bitmap[] image array for that position. For example:

YourImageDownloaderClass.java

public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
   AppCompatImageView imageView;
   int position;

   public DownloadImageTask(int position, AppCompatImageView imageView){
       this.imageView = imageView;
       this.position = position;
   }

   @Override
   protected Bitmap doInBackground(String...urls) {

       String urlOfImage = urls[0];
       Bitmap logo = null;
       try{            
           logo = BitmapFactory.decodeStream((InputStream) new URL(urlOfImage).getContent());
       }catch(Exception e){
           e.printStackTrace();            
       }
       return logo;
   }

   @Override
   protected void onPostExecute(Bitmap result){
       if(result != null) {
          YourCustomArrayAdapter.myListViewImageViewsArray [position] = result;
          imageView.setImageBitmap(result);                    
       }else{
          YourCustomArrayAdapter.myListViewImageViewsArray [position] = null;
          imageView.setImageResource(null);                  
       }
    }
}

my suggestion is to use grid view to avoid flickering of images it will load at first time if it is same url , it will load from cache

 Glide.with(mContext)
            .load(item.getImageUrl())
            .into(holder.mIVGridPic);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!