RecyclerView适配器采取错误的值

我有一个RecyclerView这显示了两种View一个代表一个用户发布和另一个代表一个事件发布。 两者都有共同的元素,例如显示时间戳的TextView 。 所以我创build了一个将此TextView时间戳记到一个variables并加载它的PublicationViewHolder 。 我的问题是,适配器,最初加载正确的值,但是当我向下滚动,再次向上滚动,位置中的值由另一个位置的值更改。 这里是代码:

 public class PublicationViewHolder extends RecyclerView.ViewHolder { private TextView vTimeStamp; public PublicationViewHolder(View itemView) { super(itemView); this.vTimeStamp = (TextView) itemView.findViewById(R.id.txt_view_publication_timestamp); } public void load(Publication publication, int i) { load(publication); try { if (Publication.TYPE_USER_PUBLICATION == publication.getType()) { load((UserPublication) publication); } else if (Publication.TYPE_EVENT_PUBLICATION == publication.getType()) { load((EventPublication) publication); } } catch (ClassCastException e) { throw new RuntimeException("Publication type cast fail. See PublicationViewHolder."); } } public void load(Publication publication) { vTimeStamp.setText(DateFormatter.getTimeAgo(publication.getTimeStamp())); } public void load( UserPublication publication) { //This method is override by UserPublicationViewHolder }; public void load( EventPublication publication) { //This method is override by EventPublicationViewHolder }; } 

现在我将只为用户出版物制作UserPublicationViewHolder

 public class UserPublicationViewHolder extends PublicationViewHolder { private ImageView vImageView, vLikeButton, vDislikeButton, vFavButton, vEditPost, vDeletePost; private TextView vText, vUsername, vLikeCount, vDislikeCount, vFavCount; private PostImagesLayout vImagesContainer; private TagCloudLocationFriends tagsView; public UserPublicationViewHolder(View itemView) { super(itemView); vImageView = (ImageView) itemView.findViewById(R.id.img_view_publication_user); vText = (TextView) itemView.findViewById(R.id.txt_view_publication_text); vLikeCount = (TextView) itemView.findViewById(R.id.txt_view_like_count); vFavCount = (TextView) itemView.findViewById(R.id.txt_view_fav_count); vDislikeCount = (TextView) itemView.findViewById(R.id.txt_view_dislike_count); vUsername = (TextView) itemView.findViewById(R.id.txt_view_publication_user_name); vLikeButton = (ImageView) itemView.findViewById(R.id.img_view_like); vDislikeButton = (ImageView) itemView.findViewById(R.id.img_view_dislike); vFavButton = (ImageView) itemView.findViewById(R.id.img_view_fav); vImagesContainer = (PostImagesLayout) itemView.findViewById(R.id.container_post_images); tagsView = (TagCloudLocationFriends) itemView.findViewById(R.id.location_friends_tag); // edit - remove icons vDeletePost = (ImageView) itemView.findViewById(R.id.img_view_delete_post); vEditPost = (ImageView) itemView.findViewById(R.id.img_view_edit_post); } @Override public void load(UserPublication publication) { //Load the UserPublicationViewHolder specific views. } } 

现在我会做同样的事情,但事件的出版物

 public class EventPublicationViewHolder extends PublicationViewHolder { private TextView vTextViewTitle; private TextView vTextViewText; public EventPublicationViewHolder(View itemView) { super(itemView); vTextViewTitle = (TextView) itemView.findViewById(R.id.txt_view_publication_event_title); vTextViewText = (TextView) itemView.findViewById(R.id.txt_view_publication_event_text); } @Override public void load(EventPublication publication) { //Load the EventPublicationViewHolder specifics views } } 

这里是我的RecyclerView适配器:

 public class PublicationAdapter extends RecyclerView.Adapter<PublicationViewHolder> { public static final int USER_PUBLICATION_TYPE = 1; public static final int EVENT_PUBLICATION_TYPE = 2; private List<Publication> publications = new ArrayList<Publication>(); public List<Publication> getPublications() { return publications; } public void setPublications(List<Publication> publications) { this.publications = publications; } @Override public int getItemViewType(int position) { if (publications.get(position) instanceof UserPublication) { return USER_PUBLICATION_TYPE; } if (publications.get(position) instanceof EventPublication) { return EVENT_PUBLICATION_TYPE; } throw new RuntimeException("Unknown view type in PublicationAdapter"); } @Override public PublicationViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) { View v; switch (type) { case USER_PUBLICATION_TYPE: v = LayoutInflater.from(getActivity()).inflate(R.layout.view_holder_user_publication, viewGroup, false); return new UserPublicationViewHolder(v); case EVENT_PUBLICATION_TYPE: v = LayoutInflater.from(getActivity()).inflate(R.layout.view_holder_event_publication, viewGroup, false); return new EventPublicationViewHolder(v); } return null; } @Override public void onBindViewHolder(PublicationViewHolder aPublicationHolder, int i) { aPublicationHolder.load(publications.get(i), i); } @Override public long getItemId(int position) { //Here I tried returning only position or 0 without luck. //The id is unique BTW return publications.get(position).getId(); } @Override public int getItemCount() { return publications.size(); } } 

我不知道什么是错的,UserPublication和EventPublication都是从出版物延伸出来的。 我没有做一些请求或重新加载适配器。 我只加载适配器一次。

更新:

顺便说一句我在一个片段内使用这个RecyclerView这是加载在一个ViewPager是一个页面加载在一个片段,也许是这个问题?

更新:这是其他的绑定代码。

这是UserPublicationViewHolder的加载方法。

  @Override public void load(UserPublication publication) { PicassoHelper.publicationUser(getActivity(), publication.getUser().getAvatarUrl(), vImageView); vText.setText(publication.getText()); vUsername.setText(publication.getUser().getName()); boolean hasLocation = false; if (publication.getImages().length > 0) { vImagesContainer.setImages(publication.getImages()); } else { vImagesContainer.setVisibility(View.GONE); } tagsView.setTags(new ArrayList<MinikastTag>()); tagsView.drawTags(); if(publication.getLocation() != null || publication.getTaggedFriends().size() > 0){ if(publication.getLocation() != null){ hasLocation = true; tagsView.add(new MinikastTag(1,"Post from ",1)); tagsView.add(new MinikastTag(2, publication.getLocation().getName(), 2)); } if(publication.getTaggedFriends().size() > 0){ if(hasLocation) tagsView.add(new MinikastTag(3," with ",1)); else tagsView.add(new MinikastTag(3,"With ",1)); int i = 0; for(User aUser: publication.getTaggedFriends()){ MinikastTag aTag; if(i == publication.getTaggedFriends().size() - 1 ) { aTag = new MinikastTag(4, aUser.getName(), 3); aTag.setUserID(aUser.getId()); aTag.setUserName(aUser.getName()); tagsView.add(aTag); } else { aTag = new MinikastTag(4, aUser.getName() + ", ", 3); aTag.setUserID(aUser.getId()); aTag.setUserName(aUser.getName()); tagsView.add(aTag); } i = i+1; } } } tagsView.drawTags(); // likes, dislikes, favs if(publication.getLikesAmount() > 0) vLikeCount.setText(String.valueOf(publication.getLikesAmount())); if(publication.getDislikesAmount() > 0) vDislikeCount.setText(String.valueOf(publication.getDislikesAmount())); if(publication.getLovesAmount() > 0) vFavCount.setText(String.valueOf(publication.getLovesAmount())); // reset buttons vFavButton.setPressed(false); vDislikeButton.setPressed(false); vLikeButton.setPressed(false); if(publication.getRelationship().equals("LOVE")) vFavButton.setPressed(true); else if (publication.getRelationship().equals("LIKE")) vLikeButton.setPressed(true); else if (publication.getRelationship().equals("DISLIKE")) vDislikeButton.setPressed(true); // edit - remove icons if(String.valueOf(publication.getUser().getId()).equals(StartupSharedPreferences.getProfileId())){ vEditPost.setVisibility(View.VISIBLE); vDeletePost.setVisibility(View.VISIBLE); }else{ vEditPost.setVisibility(View.INVISIBLE); vDeletePost.setVisibility(View.INVISIBLE); } } } 

这是EventPublicationViewHolder的加载方法:

 @Override public void load(EventPublication publication) { vTimeStamp.setVisibility(View.GONE); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //GoTo.eventDetail(getActivity(), publication); } }); vTextViewTitle.setText(publication.getTitle()); vTextViewText.setText(publication.getText()); } 

我仅仅因为testing而评论了一些代码,但正如你所看到的,我只是做了setTexts并分配了一些图片。

这就是我如何设置适配器,LinearLayoutManager等在片段的onViewCreated方法。

 vRecyclerView = (FixedRecyclerView) view.findViewById(R.id.recycler_view_publications); vSwipeRefresh = (SwipeRefreshLayout) view.findViewById(R.id.swipe_container); mFeedCallback.onScrollReady(vRecyclerView); mLayoutManager = buildLayoutManager(); vRecyclerView.setLayoutManager(mLayoutManager); vRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST)); mAdapter = new PublicationAdapter(); vSwipeRefresh.setOnRefreshListener(this); vSwipeRefresh.setColorSchemeResources(R.color._SWIPER_COLOR_1, R.color._SWIPER_COLOR_2, R.color._SWIPER_COLOR_3, R.color._SWIPER_COLOR_4); vRecyclerView.setAdapter(mAdapter); 

顺便说一句,适配器加载数据集在我有一个自定义的方法,称为onHttpClientReady,但这似乎不是问题。

以下是一些截图:

当我第一次在应用程序中input时,列表顶部:

在这里输入图像说明

那我回来的时候 在这里输入图像说明

顺便说一句,不喜欢和喜欢的button,如果有人不止一次点击它们,将显示一个数值,这个值也是错位的,如果是的话。

更新:现在我知道这不是因为嵌套的片段。 我改变了我的代码的方式,现在,每个选项卡片段都在ViewPager内部的PageStateAdapter中,这是在一个Activity中。 但问题仍然存在。

更新:我发现getItemId方法永远不会被执行,IDK为什么呢。

Solutions Collecting From Web of "RecyclerView适配器采取错误的值"

我build议查看你的类层次结构和用法。 一般来说,如果你在一个基类中做一个type == type的操作,那么你正在击败抽象和inheritance的目的。 像这样的东西会为你工作:

 public abstract class PublicationViewHolder extends RecyclerView.ViewHolder { private TextView mTimeStamp; public PublicationViewHolder(View itemView) { mTimeStamp = (TextView)itemView.findViewById(R.id. txt_view_publication_timestamp); } public void bindViews(Publication publication) { mTimeStamp.setText(DateFormatter.getTimeAgo(publication.getTimeStamp())); } } 

现在你的“事件”或“用户发布”只是从这个类派生出来的,并实现了构造函数和bindViews()方法。 在这两种情况下,一定要打电话给超类。 另外,请确保您在bindViews()方法中为特定发布的布局中设置了每个视图。

在您的适配器中,您只需根据数据集中该位置的发布types创build正确的持有者:

 public class PublicationAdapter extends RecyclerView.Adapter { private ArrayList<Publication> mPubs; // Your other code here, like // swapPublications(), getItemCount(), etc. ... public int getItemViewType(int position) { return mPubs.get(position).getType(); } public PublicationViewHolder createViewHolder(ViewGroup parent, int type) { PublicationViewHolder ret; View root; LayoutInflater inflater = LayoutInflater.from(parent.getContext()); if (type == USER_PUBLICATION_TYPE) { root = inflater.inflate(R.layout.view_holder_user_publication, parent, false); ret = new UserPubHolder(root); } else { root = inflater.inflate(R.layout.view_holder_event_publication, parent, false); ret = new EventPubHolder(root); } return ret; } public bindViewHolder(PublicationViewHolder holder, int position) { holder.bindViews(mPubs.get(position)); } } 

这通常发生在你有“if(field!= null)holder.setField(field)”之类的东西时,没有别的东西。 持有者是回收的,这意味着它将在那里有价值,所以你需要清除或取代EVERY价值,如果它为空,你应该nullit,如果不是,你应该写它,总是。 已经很晚了,但是作为其他人的回答。

对我来说setHasStableIds(false)解决了这个问题。

对于具有不同高度的asynchronous加载的图像也有同样的问题。 所以用debugging器可以看到,回收的位置取决于视图的实际大小。

简单的解决scheme是指定不同的大小,所以系统知道所有项目的确切大小。 https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int&#xFF09;

例如风景,人像和广场。

所以我创build了单独的视图,并使用它们:(简体)

 public class YourAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { // ... public static class ViewHolderLandscape extends RecyclerView.ViewHolder { ... } public static class ViewHolderPortrait extends RecyclerView.ViewHolder { ... } public static class ViewHolderSquare extends RecyclerView.ViewHolder { ... } @Override public int getItemViewType(int position) { return mDataset.get(position).getImageType(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { int mLayoutId = 0; switch (viewType) { case 0: mLayoutId = R.layout.list_item_landscape; break; case 1: mLayoutId = R.layout.list_item_portrait; break; case 2: mLayoutId = R.layout.list_item_square; break; } View v = LayoutInflater.from(parent.getContext()).inflate(mLayoutId, parent, false); ButterKnife.inject(this, v); return new ViewHolder(v); } } 

最后,RecycleView不会对不同/dynamic项目大小感到困惑。

绑定代码中的一个大variables是date格式: DateFormatter.getTimeAgo(publication.getTimeStamp())

如果没有直接看到这个类,就很难说,但是,如果时间戳是不可变的,但是格式化器是基于当前时间的,那么当视图被反弹时,这将与文本改变一致。

我认为一个更大的问题(也有一些问题)是代码的可读性,这使得难以直观地发现问题。 这里的inheritance模式和重载使得很难对代码进行推理,并决定采用哪条path以及是否正确。 这里有一些餐巾纸代码(还没有build立或运行)使用更具成分性的方法,可能是一个更清晰的组织,使debugging更容易:

常见视图持有者代码的新帮助器类replacePublicationViewHolder

 public class PublicationViewHolderHelper { private final TextView vTimeStamp; public PublicationViewHolder(View itemView) { super(itemView); this.vTimeStamp = (TextView) itemView.findViewById(R.id.txt_view_publication_timestamp); } /** Binds view data common to publication types. */ public void load(Publication publication) { vTimeStamp.setText(DateFormatter.getTimeAgo(publication.getTimeStamp())); } } 

EventPublicationViewHolder作为一个例子(为UserPublicationViewHolder做同样的事情):

 public class EventPublicationViewHolder extends ViewHolder { private final PublicationViewHolderHelper helper; // View fields... public EventPublicationViewHolder(View itemView) { super(itemView); helper = new PublicationViewHolderHelper(itemView); // Populated view fields... } @Override public void load(EventPublication publication) { helper.load(publication); //Load the EventPublicationViewHolder specifics views } } 

注意你的适配器里没有基类,也不需要types检查,所以代码less了很多。

现在适配器保持与通用types和onBindViewHolder的例外相同:

 public class PublicationAdapter extends RecyclerView.Adapter<ViewHolder> { ... @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { final Publication publication = publications.get(position); final int viewType = getItemViewType(position); switch (viewType) { case USER_PUBLICATION_TYPE: ((UserPublicationViewHolder) viewHolder).load((UserPublication) publication); break; case EVENT_PUBLICATION_TYPE: ((EventPublicationViewHolder) viewHolder).load((EventPublication) publication); break; default: // Blow up in whatever way you choose. } } ... } 

注意它和你的onCreateViewHolder保持了一个非常相似的模式,所以不仅有更less的整体代码,而且更多的内部一致性。 这当然不是唯一的方法,只是根据您的具体使用情况提出build议。