web-dev-qa-db-fra.com

Comment désactiver le défilement de RecyclerView sauf par programmation

J'ai une RecyclerView avec une LinearLayoutManager en orientation HORIZONTAL. Chaque élément de celui-ci peut avoir des éléments interactifs (y compris ScrollViews vertical). Existe-t-il un moyen de faire en sorte que RecyclerView ignore toutes les tentatives de l'utilisateur de faire défiler ou de lancer la RecyclerView horizontalement sans intercepter les événements tactiles des enfants?

Je contrôle par programme le parchemin de la RecyclerView qui fonctionne très bien jusqu'à ce que l'utilisateur le lance.

J'ai essayé quelque chose de très simple: lorsque j'appelle smoothScrollToPosition en réponse à un événement, j'active le défilement et désactive les événements tactiles jusqu'à ce que le défilement soit réglé. Comme ça:

  private class NoScrollHorizontalLayoutManager extends LinearLayoutManager {
    ScrollingTouchInterceptor interceptor = new ScrollingTouchInterceptor();
    protected boolean canScroll;

    public NoScrollHorizontalLayoutManager(Context ctx) {
      super(ctx, LinearLayoutManager.HORIZONTAL, false);
    }

    public RecyclerView.OnItemTouchListener getInterceptor() {
      return interceptor;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
      canScroll = true;
      super.smoothScrollToPosition(recyclerView, state, position);
    }

    @Override
    public void onScrollStateChanged(int state) {
      super.onScrollStateChanged(state);
      if(state == RecyclerView.SCROLL_STATE_IDLE) {
        canScroll = false;
      }
    }

    @Override
    public boolean canScrollHorizontally() {
      return canScroll;
    }

    public class ScrollingTouchInterceptor implements RecyclerView.OnItemTouchListener {
      @Override
      public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        return canScrollHorizontally();
      }

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

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

Et utilisez-le comme ça ..

NoScrollHorizontalLayoutManager layout = new NoScrollHorizontalLayoutManager(context);
recycler.setLayoutManager(layout);
recycler.addOnItemTouchListener(layout.getInterceptor());

ce qui en fait presque _ fonctionne ... mais je peux toujours bousiller le défilement par programmation en touchant l'écran lorsque le défilement régulier est en mouvement Clairement, il me manque quelque chose d’évident ou il existe un moyen beaucoup plus intelligent de le faire. 

UPDATE la solution non-RecyclerView a été trouvée ici: Comment désactiver la pagination en balayant avec le doigt dans ViewPager tout en pouvant effectuer un balayage par programme?

/**
 * ViewPager that doesn't allow swiping.
 */
public class NonSwipeableViewPager extends ViewPager {

  public NonSwipeableViewPager(Context context) {
    super(context);
  }

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

  @Override
  public boolean onInterceptTouchEvent(MotionEvent event) {
    // Never allow swiping to switch between pages
    return false;
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    // Never allow swiping to switch between pages
    return false;
  }
}
22
danb

En remplaçant onInterceptTouchEvent, vous évitez le comportement d'arrêt des clics.

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
    return false;
}
2
Borja

Tout d’abord, je ne pense pas que cela soit réalisable de manière non programmatique.

Way 1: Simple - Désactive l’autre vue en fonction du toucher.

Si l'utilisateur touche le parent, désactive le défilement des vues enfants. vice versa aussi

Morceau de code pour le faire

recyclerView.setOnTouchListener(new View.OnTouchListener() 
{
   public boolean onTouch(View p_v, MotionEvent p_event) 
    {
       yourChildView.getParent().requestDisallowInterceptTouchEvent(false);
       return false;
    }
});

Voie 2: Depuis que vous rencontrez de petits problèmes dans votre mise en œuvre. Si vous le publiez, quelqu'un pourra également résoudre ce problème.

1
recyclerView.setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
          return true;
      }
  });
0
ZeroOne

Cela donne l'effet de continuité.  

recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                if (action == MotionEvent.ACTION_DOWN) {
                    recyclerView.smoothScrollToPosition(position);
                    return true;
                } else if (action == MotionEvent.ACTION_UP) {
                    recyclerView.smoothScrollToPosition(position);
                    return true;
                }
                return false;
            }
        });
0
Bogdan Martynov