13

Ho un ListView, ognuno dei cui elementi è un orizzontale RecyclerView. Il problema che sto affrontando è, lo scorrimento orizzontale non è molto liscia. Se faccio scorrere/scorri in una direzione perfettamente orizzontale, allora funziona bene, ma se lo scorrimento è anche leggermente non orizzontale (diciamo con un angolo di 10 gradi rispetto all'orizzontale), lo considero come scorrimento su/giù piuttosto che a sinistra/destra, a causa del genitore ListView. Ho provato questo metodo:Recycler orizzontaleVista all'interno di ListView non scorrevole molto scorrevole

recyclerView.setOnTouchListener(new FlingRecyclerView.OnTouchListener() { 
       @Override 
       public boolean onTouch(View v, MotionEvent event) { 
        int action = event.getAction(); 
        switch (action) { 
         case MotionEvent.ACTION_DOWN: 
          // Disallow ScrollView to intercept touch events. 
          v.getParent().requestDisallowInterceptTouchEvent(true); 
          Log.i("Scroll down", "Intercept true"); 
          break; 

         case MotionEvent.ACTION_UP: 
          // Allow ScrollView to intercept touch events. 
          v.getParent().requestDisallowInterceptTouchEvent(false); 
          break; 

         case MotionEvent.ACTION_MOVE: 
          // Allow ScrollView to intercept touch events. 
          v.getParent().requestDisallowInterceptTouchEvent(true); 
          break; 
        } 

        // Handle HorizontalScrollView touch events. 
        v.onTouchEvent(event); 
        return true; 
       } 
      }); 

Ma non è stato molto efficace. In effetti, non ho notato molti miglioramenti. Questo perché motionEvent viene chiamato solo quando il movimento è perfettamente orizzontale - quel caso sta già funzionando bene. Poi ho provato a fare questo:

class MyGestureDetector extends GestureDetector.SimpleOnGestureListener { 
     @Override 
     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 
      try { 
       Log.i("TAG", "VelocityX " + Float.toString(velocityX) + "VelocityY " + Float.toString(velocityY)); 
       Log.i("TAG", "e1X " + Float.toString(e1.getX()) + "e1Y " + Float.toString(e1.getY()) + "e2X " + Float.toString(e2.getX()) + "e2Y " + Float.toString(e2.getY())); 
       // downward swipe 
       if (e2.getY() - e1.getY() > SWIPE_MAX_OFF_PATH && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) { 
        Toast.makeText(TestActivity.this, "Downward Swipe", Toast.LENGTH_SHORT).show(); 
        Log.i("TAG", "Downward swipe"); 
       } 
       else if (e1.getY() - e2.getY() > SWIPE_MAX_OFF_PATH && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) { 
        Toast.makeText(TestActivity.this, "Upward Swipe", Toast.LENGTH_SHORT).show(); 
        Log.i("TAG", "Upward swipe"); 
       } 
        // right to left swipe 
       else if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { 
        Toast.makeText(TestActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show(); 
        Log.i("TAG", "Left swipe"); 
       } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { 
        Toast.makeText(TestActivity.this, "Right Swipe", Toast.LENGTH_SHORT).show(); 
        Log.i("TAG", "Right swipe"); 
       } 
      } catch (Exception e) { 
       // nothing 
      } 
      return false; 
     } 

    } 

E ho notato che registra gli eventi solo quando ho comunicato il mio dito, non quando avvio il colpo. Non funziona non appena tocco lo RecyclerView. E anche questo non è riuscito a produrre alcun miglioramento evidente. Ho notato anche una cosa che questi metodi sono in qualche modo dipendenti anche dalla pressione. Quando ho fatto un colpo con una leggera pressione, non è successo nulla. E la stessa cosa fatta nello stesso percorso, con maggiore pressione produce scroll.

C'è un modo per rendere scorrevole lo scorrimento anche se il percorso del passaggio del mouse non è orizzontale o anche se il movimento è di tipo circolare? Qualsiasi aiuto sarebbe molto apprezzato.

+0

Che cosa si otterrà utilizzando un recyclerview all'interno di listview per il solo bisogno di scorrimento orizzontale? – Brendon

+0

@Brendon Il mio layout è di un feed. Può scorrere verticalmente e ogni elemento in ListView è una galleria. E la galleria ha alcuni post che possono essere scrollati orizzontalmente. –

+0

Quindi hai solo bisogno di scorrere orizzontalmente a destra? Basta usare lo scorrimento orizzontale all'interno gonfiando una vista in esso, scorrerà liscio e perfetto .. Prova questo in modo alternativo .. – Brendon

risposta

1

IIRC è possibile ignorare onInterceptTouchEvent metodo nella ListView

private Point movementStart; 

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
    boolean superResult = super.onInterceptTouchEvent(ev); 
    if(movementStart == null) { 
     movementStart = new Point((int)ev.getX(), (int)ev.getY()); 
    } 
    switch (ev.getAction()) { 
     case MotionEvent.ACTION_DOWN: 
      return false; 
     case MotionEvent.ACTION_MOVE: 
      //if movement is horizontal than don't intercept it 
      if(Math.abs(movementStart.x - ev.getX()) > Math.abs(movementStart.y - ev.getY())){ 
       return false; 
      } 
       break; 
     case MotionEvent.ACTION_CANCEL: 
     case MotionEvent.ACTION_UP: 
      movementStart = null; 
      break; 
    } 
    return superResult; 
} 

È possibile estendere codice di cui sopra in questo modo:

private Point movementStart; 
private boolean disallowIntercept = false; 

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
    boolean superResult = super.onInterceptTouchEvent(ev); 
    if(movementStart == null) { 
     movementStart = new Point((int)ev.getX(), (int)ev.getY()); 
    } 
    switch (ev.getAction()) { 
     case MotionEvent.ACTION_DOWN: 
      return false; 
     case MotionEvent.ACTION_MOVE: 
      //if movement is horizontal than don't intercept it 
      if(Math.abs(movementStart.x - ev.getX()) > Math.abs(movementStart.y - ev.getY())){ 
       disallowIntercept = true; 
      } 
      break; 
     case MotionEvent.ACTION_CANCEL: 
     case MotionEvent.ACTION_UP: 
      movementStart = null; 
      disallowIntercept = false; 
      break; 
    } 

    if(disallowIntercept) { 
     return false; 
    } else { 
     return superResult; 
    } 
} 
+0

Quando eseguo l'override del metodo precedente per 'ListView', lo scorrimento orizzontale di' RecyclerView' è perfetto. Ma scorrendo verso l'alto o verso il basso su 'RecyclerView' (touch che inizia all'interno di' RecyclerView') non scorre il 'ListView' verticalmente. 'ListView' scorre verticalmente solo se il tocco inizia da qualche parte su' ListView', ad esempio, un po 'di spazio tra due 'RecyclerView' orizzontali. –

+0

Ho modificato la mia risposta e ho aggiunto la chiamata a super.onInterceptTouchEvent. ListView ha un comportamento diverso rispetto a ScrollView e ho avuto ScrollView.onInterceptTouchEvent in memoria :) – Drake29a

+0

Funziona nella maggior parte dei casi, ma non è liscio. A volte la lista non si muove. –

1

Hai provato verticalRow.setNestedScrollingEnabled(false); // Qui verticalRow è esempio recylerView

+0

Ho lo stesso problema e la mia osservazione è che setNestedScrollingEnabled (false) corregge la scorrevolezza della scrollview padre/recyclerview ma non è scorrevolezza nelle visure scrollview/recyclvisiews secondarie – user1354603

4

Il problema è probabile che gli eventi tattili non siano sicuri su dove andare. Probabilmente vuoi utilizzare onInterceptTouchEvent insieme a TouchSlop per determinare se devi rubare gli eventi di tocco dalle righe figlio (visualizzazioni di recycler).

onInterceptTouchEvent è importante affinché ListView possa decidere se rubare gli eventi di tocco dai bambini.

Il touch slop è importante perché definisce la quantità di movimento necessaria prima che dovremmo agire (in questo caso per rubare gli eventi dal bambino). Questo perché semplicemente toccando lo schermo con il dito (senza movimenti evidenti/intenzionali) genererà eventi MotionEvent.ACTION_MOVE (puoi verificare tu stesso aggiungendo la registrazione sotto MOTION_MOVE per stampare eventi getX e getY

Se tu si verificano ancora problemi, verificare che i bambini della vista riciclatore non si aspettino eventi di movimento .Per quei bambini potrebbero essere necessario utilizzare requestDisallowInterceptTouchEvent per impedire che riciclabilità e listview intercettino i loro eventi.

Questo codice era basato sul codice trovato sul documento Android è possibile vedere l'originale e leggere di più sugli eventi di tocco here.

 MotionEvent mOriginal; 
    mIsScrolling = false; 
    // put these two lines in your constructor 
    ViewConfiguration vc = ViewConfiguration.get(view.getContext()); 
    mTouchSlop = vc.getScaledTouchSlop(); 


    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
    /* 
    * This method JUST determines whether we want to intercept the motion. 
    * If we return true, onTouchEvent will be called and we do the actual 
    * scrolling there. 
    */ 


    final int action = MotionEventCompat.getActionMasked(ev); 

    // Always handle the case of the touch gesture being complete. 
    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 
     // Release the scroll. 

     mIsScrolling = false; 
     return false; // Do not intercept touch event, let the child handle it 
    } 

    switch (action) { 
     case MotionEvent.ACTION_DOWN: { 
      mIsScrolling = false; 
      setOriginalMotionEvent(ev); 
     } 
     case MotionEvent.ACTION_MOVE: { 
      if (mIsScrolling) { 
       // We're currently scrolling, so yes, intercept the 
       // touch event! 
       return true; 
      } 

      // If the user has dragged her finger horizontally more than 
      // the touch slop, start the scroll 

      final int xDiff = calculateDistanceX(ev); 
      final int yDiff = calculateDistanceY(ev); 

      // Touch slop should be calculated using ViewConfiguration 
      // constants. 
      if (yDiff > mTouchSlop && yDiff > xDiff) { 
       // Start scrolling! 
       mIsScrolling = true; 
       return true; 
      } 
      break; 
     } 
     ... 
    } 

    // In general, we don't want to intercept touch events. They should be 
    // handled by the child view. Be safe and leave it up to the original definition 
    return super.onInterceptTouchEvent(ev); 
} 

public void setOriginalMotionEvent(MotionEvent ev){ 
    mOriginal = ev.clone(); 
} 

public int calculateDistanceX(ev){ 
    int distance = Math.abs(mOriginal.getX() - ev.getX()); 
    return distance; 
} 

public int calculateDistanceY(ev){ 
    int distance = Math.abs(mOriginal.getY() - ev.getY()); 
    return distance; 
} 
5

Jim Baca mi ha preso sulla strada giusta, il mio problema era che la vista genitore (scroll verticale) stava rubando il rotolo dalle viste secondarie (scroll orizzontale).Questo ha funzionato per me:

/** 
* This class is a workaround for when a parent RecyclerView with vertical scroll 
* is a bit too sensitive and steals onTouchEvents from horizontally scrolling child views 
*/ 
public class NestedScrollingParentRecyclerView extends RecyclerView { 
    private boolean mChildIsScrolling = false; 
    private int mTouchSlop; 
    private float mOriginalX; 
    private float mOriginalY; 

    public NestedScrollingParentRecyclerView(Context context) { 
     super(context); 
     init(context); 
    } 

    public NestedScrollingParentRecyclerView(Context context, @Nullable AttributeSet attrs) { 
     super(context, attrs); 
     init(context); 
    } 

    public NestedScrollingParentRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     init(context); 
    } 

    private void init(Context context) { 
     ViewConfiguration vc = ViewConfiguration.get(context); 
     mTouchSlop = vc.getScaledTouchSlop(); 
    } 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     final int action = MotionEventCompat.getActionMasked(ev); 

     if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 
      // Release the scroll 
      mChildIsScrolling = false; 
      return false; // Let child handle touch event 
     } 

     switch (action) { 
      case MotionEvent.ACTION_DOWN: { 
       mChildIsScrolling = false; 
       setOriginalMotionEvent(ev); 
      } 
      case MotionEvent.ACTION_MOVE: { 
       if (mChildIsScrolling) { 
        // Child is scrolling so let child handle touch event 
        return false; 
       } 

       // If the user has dragged her finger horizontally more than 
       // the touch slop, then child view is scrolling 

       final int xDiff = calculateDistanceX(ev); 
       final int yDiff = calculateDistanceY(ev); 

       // Touch slop should be calculated using ViewConfiguration 
       // constants. 
       if (xDiff > mTouchSlop && xDiff > yDiff) { 
        mChildIsScrolling = true; 
        return false; 
       } 
      } 
     } 

     // In general, we don't want to intercept touch events. They should be 
     // handled by the child view. Be safe and leave it up to the original definition 
     return super.onInterceptTouchEvent(ev); 
    } 

    public void setOriginalMotionEvent(MotionEvent ev) { 
     mOriginalX = ev.getX(); 
     mOriginalY = ev.getY(); 
    } 

    public int calculateDistanceX(MotionEvent ev) { 
     return (int) Math.abs(mOriginalX - ev.getX()); 
    } 

    public int calculateDistanceY(MotionEvent ev) { 
     return (int) Math.abs(mOriginalY - ev.getY()); 
    } 
} 
Problemi correlati