L'adaptateur RecyclerView prend de mauvaises valeurs

J'ai un RecyclerView qui montre deux types de View s one représente une publication User et une autre qui représente une publication Event. Les deux ont des éléments en commun, par exemple un TextView qui montre un horodatage. Donc, j'ai créé un PublicationViewHolder qui prend cette TextView dans une variable et la charge. Mon problème est que l'adaptateur, d'abord, charge les bonnes valeurs, mais lorsque je me déplace vers le bas et que je remonte, les valeurs dans les positions sont modifiées par des valeurs provenant d'autres positions. Voici le code:

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

Maintenant, je ferai mon UserPublicationViewHolder pour les publications des utilisateurs uniquement.

  • Le groupe de radio déclenchant l'événement ne déclenche pas, comment puis-je savoir qui est sélectionné?
  • La collecte des ordures provoque: MediaPlayer finalisé sans être libéré
  • AIR 3 Native Extensions for Android - Puis-je / Comment inclure des bibliothèques tierces?
  • Comment utiliser Android DownloadManager?
  • Android OpenGL ES et 2D
  • Transmettre la vidéo du serveur php à l'Android par programme
  •  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. } } 

    Maintenant, je ferai de même, mais pour les publications de l'événement

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

    Maintenant, voici mon adaptateur 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(); } } 

    Je ne sais pas ce qui peut être faux, UserPublication et EventPublication s'étend de la Publication. Je ne fais pas de demande ou de rechargement de l'adaptateur. Je ne charge que l'adaptateur une fois.

    Mettre à jour:

    BTW J'utilise ce RecyclerView dans un Fragment qui est chargé dans un PageAdapter qui est chargé dans un ViewPager qui se trouve dans un Fragment, peut-être est-ce le problème?

    Mise à jour: c'est l'autre code de liaison.

    C'est la méthode de chargement de 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); } } } 

    Et c'est la méthode de chargement de 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()); } 

    J'ai commenté un certain code simplement parce que je testais, mais comme vous le voyez, je ne fais que setTexts et j'adore certaines images.

    Et c'est ainsi que j'ai configuré l'adaptateur, LinearLayoutManager, etc. Dans la méthode onViewCreated du fragment.

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

    BTW L'adaptateur est chargé avec l'ensemble de données dans une méthode personnalisée que j'ai, appeléeHttpClientReady, mais cela ne semble pas être le problème.

    Voici quelques captures d'écran:

    Haut de la liste lorsque j'entre dans l'application pour la première fois:

    Entrez la description de l'image ici

    Ensuite, quand je reviens: Entrez la description de l'image ici

    TOUJOURS, les préférés, les antidouètes et les boutons favoris, si quelqu'un les a fait plus d'une fois, afficheront une valeur numérique, ces valeurs sont également démunies si elles sont.

    MISE À JOUR: Maintenant, je sais que ce n'était pas parce que les fragments imbriqués. J'ai changé mon code de la même façon que, maintenant, chaque fragment d'onglet est dans PageStateAdapter qui se trouve dans le ViewPager qui se trouve dans une Activité. Mais le problème est toujours là.

    MISE À JOUR: J'ai trouvé que la méthode getItemId n'est jamais exécutée, IDK pourquoi encore.

  • Android ListView Adapter comment détecter une liste vide?
  • Vue Recycler montrant un seul élément
  • OnItemClickListener ne fonctionne pas avec le bouton contenant l'élément ListView
  • RecyclerView Scrolling Performance
  • RemoveView (View) n'est pas pris en charge dans AdapterView
  • Définir OnClick Listener sur le bouton à l'intérieur de la liste dans Android
  • 5 Solutions collect form web for “L'adaptateur RecyclerView prend de mauvaises valeurs”

    Je suggère d'examiner la hiérarchie et l'utilisation de votre classe. En général, si vous faites un type == type type d'opération dans une classe de base, vous détruisez le but de l'abstraction et de l'héritage. Quelque chose comme ça fonctionnerait pour vous:

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

    Maintenant, votre "événement" ou "publication d'utilisateur" dérive simplement de cette classe et implémente la méthode constructeur et bindViews() . Assurez-vous d'appeler à la superclasse dans les deux cas. De plus, assurez-vous que vous définissez toutes les vues dans la mise en page de la publication spécifique dans vos méthodes bindViews() .

    Dans votre adaptateur, il vous suffit de créer le support approprié en fonction du type de publication dans cette position dans votre ensemble de données:

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

    Cela se produit habituellement lorsque vous avez quelque chose comme "if (field! = Null) holder.setField (field)", sans autre chose. Le titulaire est recyclé, cela signifie qu'il aura des valeurs là-bas, donc vous devez nettoyer ou remplacer TOUTES les valeurs, s'il est nulle, vous devriez nuler, sinon, vous devriez l'écrire, TOUJOURS. C'est tard, mais comme réponse pour les autres.

    Pour moi, setHasStableIds(false) résolu le problème.

    A eu le même problème avec les images chargées asynchrones, qui avaient des hauteurs différentes . Ainsi, avec le débogueur, vous pouvez voir que les positions pour le recyclage dépendent de la taille réelle des vues.

    La solution simple pour moi était de spécifier différentes tailles, de sorte que le système connaisse la taille exacte de tous les éléments. https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)

    Par exemple paysage, portrait et carré.

    J'ai donc créé des vues séparées et les ai utilisées comme: (simplifié)

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

    Enfin RecycleView ne se confond pas avec les différentes tailles d'objets dynamiques.

    La seule grande variable dans votre code de liaison est dans votre formatage de date: DateFormatter.getTimeAgo(publication.getTimeStamp())

    Sans voir cette classe directement, il est difficile de dire avec certitude, mais il semble que, si l'horodatage est immuable, mais le formateur est basé sur l'heure actuelle, cela serait compatible avec le changement de texte lorsque la vue est rebondissante.

    Je pense qu'un problème plus important (et un peu différent) est la lisibilité du code, ce qui rend difficile de repérer facilement le problème visuellement. Le motif d'héritage et les surcharges ici rendent difficile de raisonner sur le code et de décider quel chemin est pris et si cela se fait bien. Voici un code de serviette (ne l'avez pas construit ou exécuté) en utilisant une approche plus composée qui pourrait être une organisation plus claire et plus facile à déboguer des problèmes:

    Nouvelle classe d'aide pour le code de support de vue commune, remplace PublicationViewHolder :

     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 comme exemple (faites la même chose pour 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 } } 

    Notez qu'il n'y a pas de classe de base maintenant dans votre adaptateur, et que vous n'avez pas besoin de vérification de type, donc il y a beaucoup moins de code.

    Maintenant, l'adaptateur reste le même à l'exception du type générique et 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. } } ... } 

    Notez qu'il maintient un modèle très similaire à votre onCreateViewHolder , donc il n'y a pas seulement un code global inférieur, mais aussi une cohérence interne plus. Ce n'est certainement pas la seule façon de le faire, juste une suggestion basée sur votre cas d'utilisation particulier.

    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.