【问题标题】:How to avoid image flickering in a listview如何避免列表视图中的图像闪烁
【发布时间】:2016-07-01 00:52:04
【问题描述】:

我有一个显示一堆图像的列表。我正在使用 Universal Image Loader 将此图像从文件加载到 imageviews。

这些图像具有不同的尺寸,我希望它们都具有相同的宽度但相对于每个图像的纵横比具有不同的高度。

为此,我尝试将以下内容设置为我的图像视图

<ImageView
   android:layout_width = "400dp"
   android:layout_height="wrap_content"
   android:scaleType="centerCrop"
   android:adjustViewBounds="true"/>

这种方法的问题是滚动列表视图时会有很多闪烁,因为事先不知道图像视图的高度,并且必须先使用我的宽度来缩放图像以计算每个图像的高度这是纵横比

如何提前计算每个图像的高度,而不是让 imageview 处理它?

如果我有一个 400 X 700 的图像,并且我希望图像视图为 300 像素宽,我如何使用我的图像尺寸计算图像视图的高度并保持图像纵横比?这可以帮助避免在滚动列表视图时闪烁。

【问题讨论】:

  • 你为什么不设置一个固定的高度呢?并使用类似淡入淡入的动画使其更平滑?口粮由 scaleType 参数保存:)
  • @An-droid 我不想设置固定高度。我正在尝试实现 imageview 在后台执行的操作。如果您设置一个固定宽度并将高度设置为 wrap content,它将缩放图像并计算高度。但我想提前计算高度,以便用户在滚动图像时不会看到图像视图的丑陋调整​​span>
  • 在像 Spotify 这样的知名应用程序中,大多数 imageViews 只是设置了固定大小或 matchparent 和 scaleType 设置为 centerCrop。它有什么作用 ?图像将填满您给它的所有空间,并将根据她的身高按比例调整大小。这很好,因为您不知道用户将在哪些设备上使用您的应用程序,以何种密度......使用固定宽度是,我认为非常肮脏和危险
  • @An-droid 你有没有使用过whatsapp或任何可以发送图片的聊天应用程序?图像的大小调整得非常好,即使您发送 20 张图像,您也不会看到 imageview 自己调整大小。一切都很顺利。我正在尝试实现类似的东西

标签: android image android-imageview universal-image-loader


【解决方案1】:

这种闪烁的原因是,在列表视图中,列表项被重复使用。重新使用时,列表项中的图像视图会保留最先显示的旧图像引用。稍后,一旦下载了新图像,它就会开始显示。这会导致闪烁行为。 为避免此闪烁问题,请在重用时始终从 imageview 中清除旧的图像引用。

在您的情况下,在 holder = (ViewHolder) convertView.getTag();

之后添加 holder.image.setImageBitmap(null);

因此,您的 getView() 方法将如下所示:

@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;
}

【讨论】:

  • 这也是问题之一但我的意思是当我向下滚动时,imageview出现的宽度为400px,高度为0。然后在计算高度后,突然出现。我想提前计算一下高度,设置成image view,避免Resize og imageview
  • 你明白我的担心吗?
  • holder.setImageBitmap(null) 与在通用图像加载器 DisplayImageOptions() 中使用 .resetViewBeforeLoading(true) 相同
【解决方案2】:

你可以这样做:

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;

【讨论】:

    【解决方案3】:

    经过数小时的研究,我知道了在保持图像纵横比的情况下可以用来计算新图像视图高度的方法。

        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);
     });
    

    【讨论】:

      【解决方案4】:

      我的解决方法是创建一个Bitmap[] 数组变量来存储图像,然后在适配器的getView() 中,我使用位置来检查Bitmap[] 数组中的图像是null 还是具有值。如果它有值,那么我使用该值而不是再次调用new DownloadImageTask() 构造。

      例如:

      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    
         }
      }
      

      然后,在您的图像下载器结构中,下载图像后,将插入到该位置的Bitmap[] 图像数组中。例如:

      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);                  
             }
          }
      }
      

      【讨论】:

      • 感谢@CodyGray 的编辑。我对发布答案还是很陌生。希望随着时间的推移我会变得更好。
      • 别担心!你的回答已经很有帮助了。 Another user and I 刚刚清理了一些格式和英语语法。感谢您关心网站并努力改进!
      【解决方案5】:

      我的建议是使用网格视图来避免第一次加载的图像闪烁,如果它是相同的 url,它将从缓存中加载

       Glide.with(mContext)
                  .load(item.getImageUrl())
                  .into(holder.mIVGridPic);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-09-12
        • 2018-10-24
        • 2017-05-11
        • 2012-10-28
        • 2018-01-16
        • 2023-04-02
        相关资源
        最近更新 更多