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 avoir un bouton Supprimer sur chaque ligne. Je ne suis pas sûr de savoir comment mettre cela en œuvre avec cela.
J'ai attaché l'écouteur OnClick pour supprimer le bouton qui fonctionne (supprime la ligne), mais il déclenche également le clic onClick sur la ligne complète.
Quelqu'un peut-il m'aider à éviter de cliquer sur une ligne entière si un seul bouton est cliqué?.
Merci.
voici comment je gère plusieurs événements onClick dans un recyclerView:
Edit: Mis à jour pour inclure les rappels (comme mentionné dans d'autres commentaires). J'ai utilisé un 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);
}
Puis l'adaptateur:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private final ClickListener listener;
private final List<MyItems> itemsList;
public MyAdapter(List<MyItems> 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<ClickListener> 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 une 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 sur quel élément vous avez cliqué, vous faites correspondre l'identifiant de la vue, à savoir v.getId () == WhateverItem.getId ()
J'espère que cette approche aide!
Je trouve ça typiquement:
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 défini par votre constructeur:
public MyAdapter(ArrayList<MyListItems> newRows, MyAdapterListener listener) {
rows = newRows;
onClickListener = listener;
}
Vous pouvez ensuite gérer les événements de votre activité ou de l’utilisation de votre RecyclerView:
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ée aucun objet extra (c'est-à-dire des auditeurs) qui devrait être récupéré ultérieurement et ne nécessite pas l'imbrication d'un détenteur 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 n'est pas initialisée ici. Il n'y a donc aucune hypothèse dans la méthode onClick
qu'elle ait déjà été initialisée.
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 détenteur 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 configuré plusieurs fois avec la même valeur, mais cela revient moins cher que de rechercher la valeur NULL et la création de branches, et il n'y a pas de coût de mémoire, mais seulement instruction.
Enfin, lorsque vous créez l'adaptateur, vous êtes obligé de transmettre une instance ClickHandler
au constructeur, comme suit:
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 adapter
est une variable membre ici, pas une variable locale
Je voulais simplement ajouter une autre solution si vous avez déjà un écouteur tactile recycleur et que vous souhaitez gérer tous les événements tactiles qu'il contient plutôt que de traiter l'événement tactile avec le bouton séparément dans le détenteur de la vue. L’essentiel pour cette version adaptée de la classe est de retourner la vue du bouton dans le rappel onItemClick () lorsqu’elle est tapée, par opposition au conteneur d’élément. Vous pouvez ensuite tester que la vue est un bouton et effectuer une action différente. Remarque: un appui long sur le bouton est interprété comme un appui long sur toute la ligne.
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<View> viewHierarchy = new ArrayList<View>();
// 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<View> 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) {
}
}
Ensuite, en l'utilisant depuis activité/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 renvoyer true dans onInterceptTouchEvent()
lorsque vous gérez un événement clic.
Il suffit de mettre une méthode de substitution nommée getItemId Obtenez-la par clic droit> générer> méthodes de substitution> getItemId Placez cette méthode dans la classe Adapter
Vous pouvez d'abord vérifier si vous avez des entrées similaires. Si vous obtenez une collection de taille 0, lancez une nouvelle requête à enregistrer.
OR
manière plus professionnelle et plus rapide. créer un déclencheur de nuage (avant la sauvegarde)
découvrez cette réponse https://stackoverflow.com/a/35194514/1388852