2015-05-17 1 views
54

Ich verwende folgenden Code für die Behandlung von Zeilen Klicks. (source)Handle-Taste Klicken Sie in eine Zeile in RecyclerView

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

Dies funktioniert jedoch, wenn ich auf jeder Zeile sagen, eine Löschtaste haben will. Ich bin mir nicht sicher, wie ich das umsetzen soll.

Ich angehängt OnClick Listener zum Löschen der Taste, die funktioniert (löscht die Zeile), aber es feuert auch den Onclick auf die volle Zeile.

Kann mir jemand helfen, wie man das Klicken der ganzen Reihe vermeidet, wenn eine einzelne Taste geklickt wird.

Danke.

Antwort

85

das ist, wie ich mehrere onClick Ereignisse in einem recyclerView Griff:

Edit: aktualisiert, um Rückrufe (wie in anderen Kommentaren erwähnt). Ich habe einen WeakReference in der ViewHolder verwendet, um ein mögliches Speicherleck zu beseitigen.

definieren Schnittstelle:

public interface ClickListener { 

    void onPositionClicked(int position); 

    void onLongClicked(int position); 
} 

Dann wird der Adapter:

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

Dann in Ihrer Aktivität/Fragment - was auch immer Sie implementieren können: Clicklistener - oder anonyme Klasse, wenn Sie wie dies wünschen:

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

Um zu sehen, auf welches Objekt geklickt wurde, stimmen Sie mit der Ansichts-ID überein v.getId() == whateverItem.getId()

Hoffe, dieser Ansatz hilft!

+1

Danke, ich habe dieses Muster nur für meine Implementierung verwendet. Das Problem war jedoch etwas anderes. Sehen Sie hier http://StackOverflow.com/Questions/30287411/issue-with-cardview-and-onclicklistener-in-recyclerview –

+0

Wie würden Sie erfahren, welche Zeile der Schaltfläche angeklickt wurde? –

+0

'getAdapterPosition()' ruft die Zeile ab. –

2

Sie müssen innerhalb von Wahr zurückgeben, wenn Sie Klickereignis behandeln.

+1

Hallo, kann u auf dem aufwändigere sein. Ich verwende folgenden Code zum Binden an den Löschbutton btnDelete = (ImageButton) itemView.findViewById (R.id.btnDelete); btnDelete.setOnClickListener (neu View.OnClickListener() { @Override public void onClick (Ansicht Ansicht) { remove (getLayoutPosition());} }); –

+0

Wie onTouchEvent() gibt der Rückgabewert an, ob das Ereignis behandelt wurde oder nicht, und wenn es nicht an die vollständige Zeile übergeben wurde. –

31

Ich finde, dass typisch:

  • ich mehrere Zuhörer verwenden müssen, weil ich mehrere Tasten haben.
  • Ich möchte meine Logik in der Aktivität und nicht der Adapter oder Viewholder.

So @ mark-scharf Antwort der funktioniert gut, aber eine Schnittstelle bietet Flexibilität mehr mit:

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

Wo OnClickListener in Ihrem Adapter definiert ist:

public MyAdapterListener onClickListener; 

public interface MyAdapterListener { 

    void iconTextViewOnClick(View v, int position); 
    void iconImageViewOnClick(View v, int position); 
} 

Und wahrscheinlich durch Ihren Konstruktor gesetzt :

public MyAdapter(ArrayList<MyListItems> newRows, MyAdapterListener listener) { 

    rows = newRows; 
    onClickListener = listener; 
} 

T hen können Sie die Ereignisse in Ihrer Aktivität behandeln oder wo auch immer Ihr RecyclerView verwendet wird:

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

Dies ist ein anderer Weg und baut auf meiner Antwort auf (eine ähnliche benutze ich selbst), aber wie übermittelst du 'onClickListener' an die statisch verschachtelte Viewholder-Klasse? Wenn ich etwas nicht verpasse, kann ich nicht sehen, wie Sie es an Ihren ViewHolder weitergeben. Wenn Sie nur eine Schnittstellenmethode verwenden, können Sie einen Lambda-Ausdruck verwenden, der alles zusammenfasst. –

+0

public MyAdapterListener onClickListener; ist eine Membervariable, die in Ihrem Adapter im obigen Code definiert und in Ihrem Adapterkonstruktor festgelegt ist. (Alternativ dazu können Sie auch einen benutzerdefinierten Setter wie setOnClickListener verwenden.) – LordParsley

+3

Ich habe gerade gefragt, wie Sie in einer statischen geschachtelten Klasse (ViewHolder) auf eine Member-/Instanzvariable in Ihrer Adapter-Klasse zugreifen. –

4

ich wollten eine Lösung, die keine zusätzliche Objekte erstellt haben (dh Hörer), der Müll später gesammelt sein müssten, und es war nicht erforderlich, einen Ansichtshalter in einer Adapterklasse zu verschachteln.

In den ViewHolder Klasse

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

Punkte zu beachten: Die ClickHandler Schnittstelle definiert ist, aber nicht hier initialisiert, so dass es keine Annahme in der onClick Methode, die es jemals initialisiert wurde.

Die ClickHandler Schnittstelle sieht wie folgt aus:

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

Im Adapter, setzen Sie eine Instanz von ‚clickhandler‘ im Konstruktor, und außer Kraft setzen onBindViewHolder, zu initialisieren `clickhandler‘ auf der Ansicht, Inhaber:

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

Hinweis: Ich weiß, dass viewHolder.clickHandler möglicherweise mehrere Male mit genau dem gleichen Wert gesetzt wird, aber das ist billiger als die Suche nach Null und Verzweigung, und es gibt keinen Speicher c Ost, nur eine zusätzliche Anweisung.

Schließlich, wenn Sie den Adapter erstellen, werden Sie eine ClickHandler Instanz an den Konstruktor übergeben gezwungen, wie so:

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

Beachten Sie, dass adapter ist eine Membervariable hier nicht eine lokale Variable

3

Ich wollte nur eine andere Lösung hinzufügen, wenn Sie bereits einen Recycler-Touch-Listener haben und alle Berührungsereignisse in ihm behandeln möchten, anstatt das Schaltflächenberührungsereignis separat im Ansichtshalter zu behandeln. Der Schlüssel, den diese angepasste Version der Klasse hat, ist die Rückgabe der Schaltflächenansicht im Callback onItemClick(), wenn sie angetippt wird, im Gegensatz zum Objektcontainer. Sie können dann testen, ob die Ansicht eine Schaltfläche ist, und eine andere Aktion ausführen. Beachten Sie, langes Tippen auf die Schaltfläche wird als langes Tippen auf die gesamte Zeile interpretiert.

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

    } 
} 

mit es dann von Aktivität/Fragmente:

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

können Sie überprüfen, ob Sie Ähnliche Einträge haben zuerst, wenn Sie eine Sammlung mit der Größe 0 zu erhalten, starten Sie eine neue Abfrage zu speichern.

ODER

professioneller und schneller Weg. einen Cloud-Trigger erstellen (vor speichern)

Check out diese Antwort https://stackoverflow.com/a/35194514/1388852