Android ListView通用图像加载器在滚动时更改图像

7

我有一个问题困扰了我几周。当我更新到新的Facebook SDK时,我暂时忘记了它,但现在它又回来了。

我正在制作一个Facebook信息流阅读器。它从API调用中获取JSON数据,并使用该数据填充ListView。每次自定义适配器调用getView()方法时,它都会检查它是什么类型的“状态”。如果它是“照片”状态,则视图将具有两个图像(个人资料图片和状态更新的图片)。我使用nostra13的通用图像加载器,在充气ImageViews之后使用持有人类来帮助循环利用视图。这就是我认为问题存在的地方。

当我浏览列表时,所有个人资料图片都能正常加载并运行良好。但随着我滚动,状态照片会出现以下几种情况:

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本身提供了null值,就好像它没有找到正确的布局一样。我正在为不同类型的帖子使用两个不同的XML布局。因此,现在最好的解决方案似乎是使用相同的布局,并根据状态类型有条件地充气每个视图中的不同元素。

这对我解决了问题: https://dev59.com/WX7aa4cB1Zd3GeqPmSWR - David
5个回答

3
请在使用和处理图像加载器类时保持具体。如果可能的话,请不要创建更多相同类的实例。而是在不同的方式中使用相同的实例。
请查看下面的适配器类,并尝试将其集成到您的情况中,这将有所帮助。
 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 ;
    }

如果您有任何疑问,请在评论中提出。

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

如果您的问题无法通过此解决,请告知我。

如果您获取到的数据为空,也可能会发生此情况,请确保获取数据时进行确认。


谢谢回复。实际上,我已经为个人资料图片设置了一个标签,并且它运行得很好。但是当我尝试将其用于状态图片时,它给了我一个空指针异常。 - Wenger
您正在使用加载器类而没有初始化实例,这就是我认为您遇到空指针异常的原因。您能告诉我哪一行代码出现了空指针异常吗? - Shreyash Mahajan
个人资料图片和帖子图片的URL是从JSON数据中获取的,该数据是从getView方法中特定位置的JSON数组中获取的。 - Wenger
让我们在聊天中继续这个讨论 - Shreyash Mahajan
该实现在setTag行处给我一个空指针异常:holder.userPic.setTag(o.getUserPic()); - Wenger
显示剩余2条评论

0

有几个修复方法可以尝试:

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

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

0
首先,考虑使用视图类型 - 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);

0

我知道这个话题已经存在一段时间了,但是我的解决方案针对@Wenger的NPE问题是因为我在View成员v上使用了findViewById而不是ViewHolder:

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

工作参考:

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

0

我在使用视图持有者模式和通用图像加载器时遇到了同样的问题。

在调用通用加载器显示图像之前,我会检查我的URL,如果URL为空,我不会下载图像,只会将ImageView背景设置为我的无图像Drawable。在ListView滚动时,我的ListView图像混合在一起。

然后我尝试不检查URL是否为空,而是将无URL Drawable设置为通用图像加载器显示图像选项。这解决了我的问题。我不知道这是否是解决此问题的常见方法,但您可以尝试一下。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接