Bouton Poignée cliquez à l’intérieur d’une rangée dans RecyclerView

J’utilise le code suivant pour gérer les clics de ligne. ( source )

static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener { private GestureDetector gestureDetector; private ClickListener clickListener; public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) { this.clickListener = clickListener; gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public void onLongPress(MotionEvent e) { View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null) { clickListener.onLongClick(child, recyclerView.getChildPosition(child)); } } }); } @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { View child = rv.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) { clickListener.onClick(child, rv.getChildPosition(child)); } return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { } } 

Cela fonctionne cependant, si je veux dire un bouton de suppression sur chaque ligne. Je ne suis pas sûr de savoir comment mettre cela en œuvre.

J’ai joint l’écouteur OnClick pour supprimer le bouton qui fonctionne (supprime la ligne) mais il déclenche également le clic sur la ligne entière.

Quelqu’un peut-il m’aider à éviter les clics sur les lignes complètes en cliquant sur un seul bouton?

Merci.

Voici comment je gère plusieurs événements onClick dans un recyclerView:

Modifier: mis à jour pour inclure les rappels (comme mentionné dans d’autres commentaires). J’ai utilisé une WeakReference dans le ViewHolder pour éliminer une fuite de mémoire potentielle.

Définir l’interface:

 public interface ClickListener { void onPositionClicked(int position); void onLongClicked(int position); } 

Ensuite, l’adaptateur:

 public class MyAdapter extends RecyclerView.Adapter { private final ClickListener listener; private final List itemsList; public MyAdapter(List itemsList, ClickListener listener) { this.listener = listener; this.itemsList = itemsList; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_row_layout), parent, false), listener); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { // bind layout and data etc.. } @Override public int getItemCount() { return itemsList.size(); } public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { private ImageView iconImageView; private TextView iconTextView; private WeakReference listenerRef; public MyViewHolder(final View itemView, ClickListener listener) { super(itemView); listenerRef = new WeakReference<>(listener); iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView); iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView); itemView.setOnClickListener(this); iconTextView.setOnClickListener(this); iconImageView.setOnLongClickListener(this); } // onClick Listener for view @Override public void onClick(View v) { if (v.getId() == iconTextView.getId()) { Toast.makeText(v.getContext(), "ITEM PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show(); } listenerRef.get().onPositionClicked(getAdapterPosition()); } //onLongClickListener for view @Override public boolean onLongClick(View v) { final AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext()); builder.setTitle("Hello Dialog") .setMessage("LONG CLICK DIALOG WINDOW FOR ICON " + String.valueOf(getAdapterPosition())) .setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.create().show(); listenerRef.get().onLongClicked(getAdapterPosition()); return true; } } } 

Ensuite, dans votre activité / fragment – tout ce que vous pouvez implémenter: Clicklistener – ou classe anonyme si vous le souhaitez:

 MyAdapter adapter = new MyAdapter(myItems, new ClickListener() { @Override public void onPositionClicked(int position) { // callback performed on click } @Override public void onLongClicked(int position) { // callback performed on click } }); 

Pour obtenir l’élément sur lequel vous avez cliqué, vous correspondez à l’identifiant de la vue ievgetId () == whatItem.getId ()

J’espère que cette approche aide!

Je trouve cela typiquement:

  • J’ai besoin d’utiliser plusieurs écouteurs car j’ai plusieurs boutons.
  • Je veux que ma logique soit dans l’activité et non l’adaptateur ou le visualiseur.

Donc, la réponse de @ mark-keen fonctionne bien mais avoir une interface offre plus de flexibilité:

 public static class MyViewHolder extends RecyclerView.ViewHolder { public ImageView iconImageView; public TextView iconTextView; public MyViewHolder(final View itemView) { super(itemView); iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView); iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView); iconTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onClickListener.iconTextViewOnClick(v, getAdapterPosition()); } }); iconImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onClickListener.iconImageViewOnClick(v, getAdapterPosition()); } }); } } 

Où onClickListener est défini dans votre adaptateur:

 public MyAdapterListener onClickListener; public interface MyAdapterListener { void iconTextViewOnClick(View v, int position); void iconImageViewOnClick(View v, int position); } 

Et probablement par le biais de votre constructeur:

 public MyAdapter(ArrayList newRows, MyAdapterListener listener) { rows = newRows; onClickListener = listener; } 

Ensuite, vous pouvez gérer les événements dans votre activité ou partout où votre RecyclerView est utilisé:

 mAdapter = new MyAdapter(mRows, new MyAdapter.MyAdapterListener() { @Override public void iconTextViewOnClick(View v, int position) { Log.d(TAG, "iconTextViewOnClick at position "+position); } @Override public void iconImageViewOnClick(View v, int position) { Log.d(TAG, "iconImageViewOnClick at position "+position); } }); mRecycler.setAdapter(mAdapter); 

Je voulais une solution qui ne créait pas d’ objects supplémentaires (c.-à-d. Des écouteurs) qui devraient être récupérés plus tard et qui ne nécessitaient pas l’imbrication d’un titulaire de vue dans une classe d’adaptateur.

Dans la classe ViewHolder

 private static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private final TextView ....// declare the fields in your view private ClickHandler ClickHandler; public MyHolder(final View itemView) { super(itemView); nameField = (TextView) itemView.findViewById(R.id.name); //find other fields here... Button myButton = (Button) itemView.findViewById(R.id.my_button); myButton.setOnClickListener(this); } ... @Override public void onClick(final View view) { if (clickHandler != null) { clickHandler.onMyButtonClicked(getAdapterPosition()); } } 

Points à noter: l’interface ClickHandler est définie, mais pas initialisée ici, il n’y a donc aucune hypothèse dans la méthode onClick .

L’interface ClickHandler ressemble à ceci:

 private interface ClickHandler { void onMyButtonClicked(final int position); } 

Dans l’adaptateur, définissez une instance de ‘ClickHandler’ dans le constructeur et remplacez onBindViewHolder pour initialiser `clickHandler ‘sur le titulaire de la vue:

 private class MyAdapter extends ...{ private final ClickHandler clickHandler; public MyAdapter(final ClickHandler clickHandler) { super(...); this.clickHandler = clickHandler; } @Override public void onBindViewHolder(final MyViewHolder viewHolder, final int position) { super.onBindViewHolder(viewHolder, position); viewHolder.clickHandler = this.clickHandler; } 

Remarque: je sais que viewHolder.clickHandler est potentiellement défini plusieurs fois avec la même valeur, mais cela revient moins cher que de vérifier la présence de null et de twigments, et il n’y a pas de coût en mémoire, juste une instruction supplémentaire.

Enfin, lorsque vous créez l’adaptateur, vous êtes obligé de transmettre une instance ClickHandler au constructeur, comme ClickHandler :

 adapter = new MyAdapter(new ClickHandler() { @Override public void onMyButtonClicked(final int position) { final MyModel model = adapter.getItem(position); //do something with the model where the button was clicked } }); 

Notez que l’ adapter est une variable membre ici, pas une variable locale

Je voulais juste append une autre solution si vous avez déjà un auditeur tactile de recycleur et que vous souhaitez y gérer tous les événements tactiles plutôt que de traiter l’évènement tactile séparément dans le support de vue. L’essentiel de cette version adaptée de la classe est de renvoyer la vue du bouton dans le rappel onItemClick () lorsqu’il est activé, par opposition au conteneur d’éléments. Vous pouvez ensuite tester la vue en tant que bouton et effectuer une action différente. Remarque: un long tapotement sur le bouton est interprété comme un appui long sur toute la rangée.

 public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { public static interface OnItemClickListener { public void onItemClick(View view, int position); public void onItemLongClick(View view, int position); } 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) { // Important: x and y are translated coordinates here final ViewGroup childViewGroup = (ViewGroup) recyclerView.findChildViewUnder(e.getX(), e.getY()); if (childViewGroup != null && mListener != null) { final List viewHierarchy = new ArrayList(); // Important: x and y are raw screen coordinates here getViewHierarchyUnderChild(childViewGroup, e.getRawX(), e.getRawY(), viewHierarchy); View touchedView = childViewGroup; if (viewHierarchy.size() > 0) { touchedView = viewHierarchy.get(0); } mListener.onItemClick(touchedView, recyclerView.getChildPosition(childViewGroup)); return true; } return false; } @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)); } } }); } public void getViewHierarchyUnderChild(ViewGroup root, float x, float y, List viewHierarchy) { int[] location = new int[2]; final int childCount = root.getChildCount(); for (int i = 0; i < childCount; ++i) { final View child = root.getChildAt(i); child.getLocationOnScreen(location); final int childLeft = location[0], childRight = childLeft + child.getWidth(); final int childTop = location[1], childBottom = childTop + child.getHeight(); if (child.isShown() && x >= childLeft && x <= childRight && y >= childTop && y <= childBottom) { viewHierarchy.add(0, child); } if (child instanceof ViewGroup) { getViewHierarchyUnderChild((ViewGroup) child, x, y, viewHierarchy); } } } @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { mGestureDetector.onTouchEvent(e); return false; } @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent){} @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } } 

Puis en l'utilisant à partir d'une activité / d'un fragment:

 recyclerView.addOnItemTouchListener(createItemClickListener(recyclerView)); public RecyclerItemClickListener createItemClickListener(final RecyclerView recyclerView) { return new RecyclerItemClickListener (context, recyclerView, new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { if (view instanceof AppCompatButton) { // ... tapped on the button, so go do something } else { // ... tapped on the item container (row), so do something different } } @Override public void onItemLongClick(View view, int position) { } }); } 

Vous devez retourner true dans onInterceptTouchEvent() lorsque vous gérez un événement click.

Vous pouvez vérifier si vous avez des entrées similaires en premier, si vous obtenez une collection de taille 0, lancez une nouvelle requête pour enregistrer.

OU

manière plus professionnelle et plus rapide. créer un déclencheur de nuage (avant sauvegarde)

Découvrez cette réponse https://stackoverflow.com/a/35194514/1388852