2016-10-03 1 views
0

J'ai un RecyclerView qui affiche une série de vues dérivées d'une base de données. Je souhaite confirmer que l'utilisateur a sélectionné l'une des vues en mettant en évidence ses bords, et j'ai réussi à le faire. Si l'utilisateur continue à sélectionner une option différente, je souhaite supprimer la surbrillance sur la sélection originale. C'est là que j'ai rencontré quelques problèmes.Accéder à un ViewHolder, directement depuis RecyclerView, par position

Le point culminant initial était sans problème, comme je le faisais, en interne. Cependant, je ne sais pas comment accéder à la vue précédente avec seulement sa position d'adaptateur. J'ai cherché StackOverflow pendant environ une heure maintenant, car je ne peux pas trouver beaucoup de choses à faire dans l'API Android, ou à partir de google. Beaucoup d'utilisateurs semblent poser des questions similaires, mais finalement, des différences subtiles annulent toutes les réponses utiles.

intérieur de mon ViewHolder, qui est un public class interne de mon RecyclerView, j'ai un OnClickListener comme suit:

@Override 
    public void onClick(View view) { 
     if(!selected) { 
      selected = true; 
      view.setBackgroundResource(R.drawable.default_selection_background); 
     } else { 
      selected = false; 
      view.setBackgroundResource(0); 
     } 

     UpdateSelected(getAdapterPosition()); 
    } 

Ceci nous ramène à mon RecyclerView, et franchement, je suis encore d'ajouter un code de travail à ma méthode UpdateSelected(int position).

Si elle aide, je l'intention de le faire fonctionner comme ceci:

void UpdateSelected(int position) { 
    if(position != currentlySelected) { 
     ViewHolder[currentlySelected].Deselect(); 
    } 
} 

public class ViewHolder extends RecyclerView.ViewHolder { 
    ... 

    public void Deselect() { 
     // and from here, I can go on as normal. 
    } 
} 

je les utilisateurs remarqués conseiller les autres à utiliser getLayoutPosition() pour obtenir la position à des fins de l'interface utilisateur, à moins qu'ils ne souhaitent plus particulièrement de continuer à travailler avec le données internes, ce que j'ai l'intention de faire.

Comment accéder à un ViewHolder particulier à partir de mon RecyclerView, en utilisant sa position?

Répondre

3

Vous pourriez faire quelque chose comme ceci.

Dans votre adaptateur, créez une variable membre qui gardera trace de la position de l'élément sélectionné.

int selectedPosition = -1; // -1 some default value for nothing selected 

Ensuite, dans votre onBindViewHolder de l'adaptateur de vue recycleur

int backgroundRes = (position == selectedPosition)? R.drawable.default_selection_background : 0; 
    view.setBackgroundResource(backgroundRes); 

Enfin, dans onClick de votre viewholder

@Override 
public void onClick(View view) { 
    selectedPosition = getAdapterPosition(); 
    notifyDataSetChanged(); 
} 
+0

Brillant! fonctionne comme un charme. Il n'y avait que le changement que j'ai dû faire dans 'onBindViewHolder', qui a immédiatement accès à' view'. J'ai simplement utilisé 'viewHolder.itemView.setBackgroundResource (backgroundRes)'. Pour le point de vue de l'efficacité, j'ai aussi 'notifyItemChanged' sur' previousSelection' et 'currentSelection', mais merci beaucoup. – Gnemlock

+0

Je suis également confronté au même problème. donc j'essaie cette solution mais ne fonctionnait pas dans mon cas. @Gnemlock –

+0

J'ai juste trébuché sur cette réponse, et bien que ce soit bon, je suggère de changer 'notifyDataSetChanged()' 'notifyItemChanged (selectedPosition)'. Il est conseillé d'éviter d'appeler 'notifyDataSetChanged()' lorsque vous pouvez indiquer précisément à l'adaptateur quelles vues doivent être mises à jour. –

1

Vous pouvez accéder à la viewholder par sa position de recyclerview

Voici mon implémentation

recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, 
       recyclerView, new RecyclerItemClickListener.OnItemClickListener() { 
      @Override 
      public void onItemClick(View view, int position) { 
       /*your coding */ 
      } 

      @Override 
      public void onItemLongClick(View view, int position) { 

      } 

     })); 

et créer la classe

public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { 

    private OnItemClickListener mListener; 
    private GestureDetector mGestureDetector; 
    public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) { 
     mListener = listener; 

     mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { 
      @Override 
      public boolean onSingleTapUp(MotionEvent e) { 
       return true; 
      } 
      @Override 
      public void onLongPress(MotionEvent e) { 
       View childView = recyclerView.findChildViewUnder(e.getX(), e.getY()); 

       if (childView != null && mListener != null) { 
        mListener.onItemLongClick(childView, recyclerView.getChildPosition(childView)); 
       } 
      } 
     }); 
    } 

    @Override 
    public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { 
     View childView = view.findChildViewUnder(e.getX(), e.getY()); 
     if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) { 
      mListener.onItemClick(childView, view.getChildPosition(childView)); 
     } 
     return false; 
    } 

    @Override 
    public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { 
    } 

    @Override 
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { 

    } 
    public interface OnItemClickListener { 
     void onItemClick(View view, int position); 
     void onItemLongClick(View view, int position); 
    } 
} 
0

Au lieu d'échanger la ressource en arrière-plan manuellement, vous pouvez à la place marquer la vue comme sélectionné ou non, et ont changement Android l'arrière-plan utilisant des sélecteurs pouvant être dessinés.

résolution/dessin/arrière-plan.xml:

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
    <item android:state_selected="true" android:drawable="@drawable/default_selection_background" /> 
    <item android:drawable="@null" /> 
</selector> 

background.xml serait défini comme android:background dans le XML de mise en page de votre article.

Ensuite, dans votre onBindViewHolder(ViewHolder holder, int position) vous mettez à jour l'état de l'Android Vue:

@Override 
public void onBindViewHolder(ViewHolder holder, int position) { 
    boolean isSelected = position == selectedPosition; 
    holder.itemView.setSelected(isSelected); 
} 

Pour moi, je préférerais avoir un adaptateur qui était plus bête et avait moins de logique. Vous pouvez le faire en gardant une liste de ViewModels (classes de données qui représentent ce que vous montrerez dans la vue - pas plus, pas moins) où l'une des propriétés pourrait être l'indicateur sélectionné (dans chaque ViewModel). Lorsque l'utilisateur sélectionne ou désélectionne une vue, un rappel envoie une mise à jour au présentateur, qui met ensuite à jour l'adaptateur et informe l'adaptateur des positions qui ont été mises à jour. Cela signifierait conserver l'élément sélectionné actuel (de préférence ID, mais pourrait stocker ViewModel ou position) dans le présentateur afin que vous puissiez le mettre à jour.