10

ProblemVerhindern RecyclerView von Touch-Ereignisse raubend

ich das genaue Verhalten der Google Play-Funktion zu simulieren versucht bin.

Derzeit, wenn Sie durch eine Kategorie wie 'Top Games' blättern und Sie die Liste berühren, die Zelle sowie die RecyclerView behandelt das Touch-Ereignis, können Sie beide sie behandeln aufgrund der Ripple erscheinen wann Sie halten den Finger gedrückt und das Scrollen kommt zum Stillstand. Oder wenn Sie auf eine Zelle klicken, während sich die Liste noch bewegt, wird der Inhalt sofort geöffnet.

Das voreingestellte Verhalten ist, dass das Scrollen sofort stoppt, wenn Sie den Finger während der Bewegung ablegen und dann dieses Berührungsereignis konsumieren, was bedeutet, dass die Zelle niemals über diese Berührung informiert wird. Das ist unglaublich irritierend, wenn sich der Hauptinhalt Ihrer Anwendung in einer RecyclerView befindet. Wenn der Benutzer ständig scrollt und dann auf eine Zelle klickt, müssen diese zwei Mal klicken, da der erste Klick durch das Anhalten der Bildlaufleiste verbraucht wurde, selbst wenn die Liste war kaum bewegt.

, was ich bisher

Zuerst versucht haben, dachte ich, es irgendeine Art von Flagge innerhalb der RecyclerView sein muß, die es den Touch-Ereignis nicht zu konsumieren erzählt. Ich habe den Android-Quellcode durchgesehen und die Android-Dokumentation ohne Erfolg durchgelaufen.

Dann habe ich versucht, das Berührungsereignis mit onInterceptTouchEvent zu erfassen und meine Ansicht manuell zu erzählen, dass es berührt wird;

@Override 
public boolean onInterceptTouchEvent(MotionEvent event) { 
    View myCell = recyclerView.findChildViewUnder(event.getX(), event.getY()); 
    myCell.dispatchTouchEvent(event); 
    return super.onInterceptTouchEvent(event); 
} 

Hier dispatchTouchEvent scheinen nichts zu tun, auch wenn die Aussicht Ich war immer der entsprechende Tag gehabt, die ich in den Adapter gesetzt.

Ich habe versucht, dieses Problem mit meinem Code Maschinengewehr in allen Winkeln zu schießen; Spielen mit requestDisallowInterceptTouchEvent, manuell aufrufen onTouch und überschreiben alle Arten von Listenern für die RecyclerView und darüber hinaus.

Nichts scheint zu funktionieren. Ich bin mir sicher, dass es eine elegante Lösung dafür geben muss und ich habe irgendwo auf dem Weg zum Wahnsinn ein wichtiges Detail überflogen. Vor allem, weil sich Google-eigene Apps auf diese Weise verhalten.

Bitte helfen! :) Vielen Dank.

Die Lösung

Dank Jagoan Neon eine Lösung gefunden wurde. Ich beendete seine Antwort in einem benutzerdefinierten RecyclerView für die Benutzerfreundlichkeit.

public class MultiClickRecyclerView extends RecyclerView { 
    public MultiClickRecyclerView(Context context) { 
     super(context); 
    } 

    public MultiClickRecyclerView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public MultiClickRecyclerView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent event) { 
     if (event.getAction() == MotionEvent.ACTION_DOWN && this.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING) { 
      this.stopScroll(); 
     } 
     return super.onInterceptTouchEvent(event); 
    } 
} 

Antwort

9

Zunächst einmal sollten Sie eine OnItemTouchListener definieren, vielleicht als globale Variable wie folgt aus:

RecyclerView.OnItemTouchListener mOnItemTouchListener = new RecyclerView.OnItemTouchListener() { 
    @Override 
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { 
     if (e.getAction() == MotionEvent.ACTION_DOWN && rv.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING) { 
      Log.d(TAG, "onInterceptTouchEvent: click performed"); 
      rv.findChildViewUnder(e.getX(), e.getY()).performClick(); 
      return true; 
     } 
     return false; 
    } 

    @Override 
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {} 

    @Override 
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {} 
}; 

Warum ACTION_DOWN und SCROLL_STATE_SETTLING?

ACTION_DOWN Bewegungsereignis wird ausgelöst, wenn Sie eine Berührung auf dem RecyclerView ausführen, und zu diesem Zeitpunkt ändert RecyclerView seinen Scroll-Status in SCROLL_STATE_SETTLING, während es versucht, das Scrollen zu stoppen.Und genau dann möchten Sie den Klick ausführen.

Und vergessen Sie nicht, true zurückzugeben, damit Sie Ihrem RecyclerView mitteilen, dass Sie das MotionEvent verbraucht haben. Andernfalls könnte Ihr performClick() mehrmals aufgerufen werden.

es zu Ihrem RecyclerView bei onResume hinzufügen und es an onPause entfernen, und Sie sind alle gesetzt;)

UPDATE

ein Null-checker hinzufügen, wenn Sie findChildViewUnder tun - es gibt null zurück, wenn Sie Berühre keine bestimmte Zelle.

View childView = rv.findChildViewUnder(e.getX(), e.getY()); 
if (childView != null) childView.performClick(); 

UPDATE 2

Es stellte sich heraus, dass würde ausreichen, um aus Scrollen der RecyclerView zu stoppen. Tun Sie dies stattdessen:

@Override 
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { 
     if (e.getAction() == MotionEvent.ACTION_DOWN && rv.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING) 
      rv.stopScroll(); 
     return false; 
    } 
+0

Ich implementierte Ihre Antwort heute Morgen und wir sind fast da! Es ist komisch; Diese Lösung scheint ungefähr 30% der Zeit zu funktionieren, abhängig davon, wie langsam ich vorher ziehe. Ich habe das Bewegungsereignis und den Bildlaufstatus abgemeldet und es scheint keinen Unterschied zu geben, wann es funktioniert und wann nicht. Wenn ich schnell durch die Liste scrolle und dann auf den Finger tippe und halte, registriert sie fast nie den Touch und zeigt die Welligkeit an (wie die Google Play App). Funktioniert es ständig für dich? Ich benutze ein Grid-Layout mit zwei Spannen, obwohl ich mir nicht vorstellen würde, dass dies einen Unterschied machen würde. – vguzzi

+0

Hast du meine zweite Bearbeitung gesehen? –

+0

Ja, ich bin gerade auf der Stop Scroll View Implementierung. Ich habe auch die vorherigen Vorschläge ausprobiert. – vguzzi