【问题标题】:Android ListView Universal Image Loader Changes Images on ScrollAndroid ListView 通用图像加载器在滚动时更改图像
【发布时间】:2023-12-29 10:00:01
【问题描述】:

我有一个问题困扰了我好几个星期。当我更新到新的 Facebook SDK 时,我有一段时间忘记了它,但现在它又回到了我的梦想中。

我基本上是在制作一个 Facebook 订阅阅读器。它从 API 调用中获取 JSON 数据并使用它来填充 ListView。每次自定义适配器调用 getView() 方法时,它都会检查它是什么类型的“状态”。如果是“照片”状态,则视图将有两个图像(个人资料图片和状态更新的图片)。我正在使用 nostra13 的 Universal Image Loader 在使用 holder 类对 ImageView 进行充气后加载两个图像,以帮助回收视图。这就是我认为我的问题所在。

当我滚动浏览列表时,所有个人资料图片都可以正常加载并且效果很好。但当我滚动时,状态照片会做几件事:

1.) 全部加载相同的图像(我以前没有这个问题,但我给自己尝试修复其他问题)。

2.) 图像跳来跳去,要么消失,要么出现在与预期不同的视图中。

3.) 如果图像加载正确并且并非全部显示相同的图像,则图像会随着我向上和向下滚动而改变,最终都变成相同的图像。

我将发布我的 getView() 代码(抱歉有点长)。

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

    final ViewHolder holder;

    try {
        jsonObject = getItem(position);
        jsonFrom = jsonObject.getJSONObject("from");
        postType = jsonObject.getString("type");
        posterName = jsonFrom.getString("name");
        posterID = jsonFrom.getString("id");
        posterPhoto = "http://graph.facebook.com/" + posterID
                + "/picture?type=square";
        if (jsonObject.has("message")) {
            posterMessage = jsonObject.getString("message");
        } else {
            posterMessage = jsonObject.getString("story");
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }

    if (postType.equals("status")) {
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.status_post_holder,null);
            holder = new ViewHolder();
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.posterName = (TextView) convertView.findViewById(R.id.posterName);
        holder.posterMessage = (TextView) convertView.findViewById(R.id.posterMessage);
        holder.posterProfilePhoto = (ImageView) convertView.findViewById(R.id.posterProfilePic);

        if (jsonObject != null) {
            holder.posterName.setText(posterName);
            if (posterMessage != null) {
                holder.posterMessage.setText(posterMessage);
            } else {
                holder.posterMessage.setText("No message for this post.");
            }
            profileImageLoader.displayImage(posterPhoto,
                    holder.posterProfilePhoto);
        }

    }

    else if (postType.equals("photo")) {
        if (convertView == null) {
            try {
                posterImageURL = jsonObject.getString("picture");
            } catch (JSONException e) {
                e.printStackTrace();
            }
            LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.photo_post_holder, null);
            holder = new ViewHolder();
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.posterName = (TextView) convertView.findViewById(R.id.posterName);
        holder.posterMessage = (TextView) convertView.findViewById(R.id.posterMessage);
        holder.posterProfilePhoto = (ImageView) convertView.findViewById(R.id.posterProfilePic);
        holder.posterImage = (ImageView) convertView.findViewById(R.id.posterImage);

        if (jsonObject != null) {
            holder.posterName.setText(posterName);
            if (posterPhoto != null) {
                profileImageLoader.displayImage(posterPhoto,holder.posterProfilePhoto);
            }
            if (posterImageURL != null) {
                pictureImageLoader.displayImage(posterImageURL,holder.posterImage);
            }

            if (posterMessage != null) {
                holder.posterMessage.setText(posterMessage);
            } else {
                holder.posterMessage.setText("No message for this post.");
            }
        }

    }

    else if (postType.equals("link")) {
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.status_post_holder,null);
            holder = new ViewHolder();
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.posterName = (TextView) convertView.findViewById(R.id.posterName);
        holder.posterMessage = (TextView) convertView.findViewById(R.id.posterMessage);
        holder.posterProfilePhoto = (ImageView) convertView.findViewById(R.id.posterProfilePic);

        if (jsonObject != null) {
            holder.posterName.setText(posterName);
            if (posterMessage != null) {
                holder.posterMessage.setText(posterMessage);
            } else {
                holder.posterMessage.setText("No message for this post.");
            }
            profileImageLoader.displayImage(posterPhoto,holder.posterProfilePhoto);
        }
    }

    else {
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.status_post_holder,null);
            holder = new ViewHolder();
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.posterName = (TextView) convertView.findViewById(R.id.posterName);
        holder.posterMessage = (TextView) convertView.findViewById(R.id.posterMessage);
        holder.posterProfilePhoto = (ImageView) convertView.findViewById(R.id.posterProfilePic);

        if (jsonObject != null) {
            holder.posterName.setText(posterName);
            if (posterMessage != null) {
                holder.posterMessage.setText(postType);
            } else {
                holder.posterMessage.setText("No message for this post.");
            }
            profileImageLoader.displayImage(posterPhoto,holder.posterProfilePhoto);
        }
    }

    return convertView;

}

我确信这与 Android 回收视图和效率有关,但我无法弄清楚我在哪里犯了错误。问题只存在于一个图像视图也很奇怪。任何帮助都会很热。谢谢。

更新:我们发现这是我的 JSON 解析错误。您必须使用 setTag() 来保留特定于该行项目的图像。适配器试图为视图设置一个 null 标记,这导致了 NPE。

再次更新:在深入挖掘之后,看起来是 ImageView 本身给出了一个空值,就好像它没有找到正确的布局一样。我为不同类型的帖子使用了两种不同的 xml 布局。所以现在看起来最好的解决方案是使用相同的布局,并根据状态类型有条件地在每个视图中膨胀不同的元素。

【问题讨论】:

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


【解决方案1】:

在使用和处理图像加载器类时请具体说明。 如果可能,不要创建同一类的更多实例。而不是以不同的方式使用相同的实例。

请参阅下面的适配器类并尝试在您的情况下集成相同的类。这将对您有所帮助。

 private class OrderAdapter extends ArrayAdapter<User> {
        private ArrayList<User> items;
        Activity a;
        public ImageLoader imageLoader;

        public OrderAdapter(Context context, int textViewResourceId,ArrayList<User> items) {
            super(context, textViewResourceId, items);
            this.items = items;
            // extra
            /*if (Utility.model == null) {
                Utility.model = new FriendsGetProfilePics();
            }
            Utility.model.setListener(this);*/
        }

        /*@Override
        public boolean isEmpty() {
            // TODO Auto-generated method stub
            super.isEmpty();
            return false;
        }*/

        @Override
        public View getView(final int position, View convertView,ViewGroup parent) {
            ViewHolder holder;
            View v = convertView;
            if (v == null) {
                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

                v = vi.inflate(R.layout.add_buddies_row_new, null);
                holder = new ViewHolder();
                imageLoader = new ImageLoader(NotificationsActivity.this);
                //--------------

                //---------------------------------------------------------------------
                // for head to head
                holder.userName = (TextView) v.findViewById(R.id.txtName);
                holder.userPic = (ImageView) v.findViewById(R.id.imgUserPic);
                holder.userCity = (TextView) v.findViewById(R.id.txtCity);
                holder.userState = (TextView) v.findViewById(R.id.txtstate);
                holder.userCountry = (TextView) v.findViewById(R.id.txtContry);
                //---------------------------------------------------------------------

                v.setTag(holder);

            }else{
                holder=(ViewHolder)v.getTag();
            }

            final User o = items.get(position);
            holder = (ViewHolder) v.getTag();

            if (o != null) {
                holder.userName.setText(o.getUserName());
                holder.userCity.setText(o.getUserCity()+", ");
                holder.userState.setText(o.getUserState()+", ");
                holder.userCountry.setText(o.getUserCountry());
                System.out.println("PIC: "+o.getUserPic());
                holder.userPic.setTag(o.getUserPic());
                imageLoader.DisplayImage(o.getUserPic(), a, holder.userPic);
            }
            return v;
        }
    }

    class ViewHolder {
        TextView userName ;
        TextView userCity;
        TextView userState;
        TextView userCountry;
        ImageView userPic ;
    }

对我有任何意见。

注意:您必须根据您的要求对上述代码进行一些更改。

如果您的问题无法解决,请告诉我。

如果您获取空数据也可能发生这种情况,因此请确认获取数据。

【讨论】:

  • 感谢您的回复。实际上,我为个人资料图片设置了一个标签,并且效果很好。但是当我尝试将它用于状态图片时,它给了我一个空指针异常。
  • 您正在使用加载器类而不启动实例,这就是我认为您遇到空指针异常的原因。你能告诉我你得到空指针异常的那一行吗?
  • 我使用了两个不同的图像加载器对象,一个用于个人资料图片,一个用于发布图片。我在适配器类的main方法中声明并初始化它们(FeedAdapter是类,FeedAdapter(Activity activity, JSONArray jsonArray)是我声明它们的方法。
  • NPE 发生在我首先尝试的任何 setTag 上。
  • 个人资料图片和帖子图片的URL取自JSON数据,取自JSON数组,用于getView方法中的特定位置。
【解决方案2】:

我知道自从创建主题以来已经有一段时间了,但是我对 @Wenger 的 NPE 的解决方案出现了,因为我在名为 v 而不是 ViewHolder 的视图成员上使用了 findViewById:

holder.userName = (TextView) v.findViewById(R.id.txtName);

工作参考:

holder.userName = (TextView) convertView.findViewById(R.id.txtName);

【讨论】:

    【解决方案3】:

    我在视图支架模式和通用图像加载器方面遇到了同样的问题。

    在调用通用加载器显示图像之前,我正在检查我的 url,如果 url 为空,我不会下载图像,只是将 imageview 背景设置为可绘制我没有图像可绘制。在 Listview 滚动上,我的 listview 图像混合在一起。

    然后我尝试不检查 url 是否为 null,而只是将一个 no url drawable 设置为通用图像加载器显示图像选项。这解决了我的问题。我不知道这是否是解决此问题的常用解决方案,但您可以尝试一下。

    【讨论】:

      【解决方案4】:

      首先,考虑使用视图类型 - getViewTypeCount()getItemViewType(int position) - ListView 不会尊重您的不同视图 - 更多信息:ListView 回收如何工作。

      另外,不要在 getView() 中解析 json - 在你得到它的地方做。

      另外(如上所述),您应该只初始化一次ImageLoader - 这样做的最佳位置是您的应用程序的 onCreate()。然后,你叫它——ImageLoader.getInstance().loadSmth();

      另外,对你的代码格式做一些事情——这太糟糕了。提示:

              final int viewType = getItemViewType(position);
              final int layoutResId;
              switch (viewType) {
                  case 0:
                      layoutResId = R.layout.layout_1;
                      break;
                  case 1:
                      layoutResId = R.layout.layout_2;
                      break;
                  default:
                      layoutResId = R.layout.layout_3;
              }
      
              final View view = mInflater.inflate(layoutResId, group, false);
      

      【讨论】:

        【解决方案5】:

        你可以做的修复很少

        您的视图持有者几乎没用。您必须按照以下方式使用它

        ViewHolder viewHolder;
        if (view == null) {
            viewHolder = new ViewHolder();    
            viewHolder.image = (ImageView) view.findViewById(R.id.capsule_media_list_item_thumbnail_1);             
            view.setTag(viewHolder);    
        }else{
         viewHolder =  view.getTag(viewHolder); 
        }
        

        您应该只声​​明一次 ImageLoader 和 DisplayImageOptions 实例。可能在构造函数中。

        ImageLoader imageLoader = ImageLoader.getInstance();
        DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true)
                .cacheOnDisc(true).resetViewBeforeLoading(true)
                .showImageForEmptyUri(fallbackImage)
                .showImageOnFail(fallbackImage)
                .showImageOnLoading(fallbackImage).build();
        

        【讨论】:

          最近更新 更多