Подтвердить что ты не робот

Как отключить прокрутку RecyclerView, кроме программных

У меня есть RecyclerView с LinearLayoutManager в ориентации HORIZONTAL. Каждый элемент в нем может иметь интерактивные элементы (включая вертикальные ScrollView s). Есть ли способ легко заставить RecyclerView игнорировать любые попытки пользователя прокручивать или перемещать RecyclerView по горизонтали, не перехватывая события касания к детям?

Я программно управляю прокруткой RecyclerView, которая отлично работает, пока пользователь не запустит ее.

Я пробовал что-то очень простое, когда идея заключается в том, что я вызываю smoothScrollToPosition в ответ на какое-то событие, я разрешаю прокручивать и отключать события касания до тех пор, пока свиток не опустится. Вот так:

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

И используйте его вот так.

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

который на самом деле почти работает... но я все еще могу испортить программную прокрутку, нажав на экран, когда плавный свиток движется. ясно, что я пропускаю что-то очевидное или есть гораздо более умный способ сделать это.

UPDATE решение, не относящееся к RecyclerView, было найдено здесь: Как отключить пейджинг, проверив пальцем в ViewPager, но все же сможете прокручивать программно?

/**
 * 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;
  }
}
4b9b3361

Ответ 1

Переопределение onInterceptTouchEvent позволяет избежать остановки при нажатии.

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

Ответ 2

Во-первых, я не думаю, что это возможно не программно.

Путь 1: Простой - отключить другое представление, основанное на касании.

Если пользователь прикасается к родительскому, отключите прокрутку дочерних просмотров. наоборот.

Часть кода для этого

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

Способ 2: поскольку у вас небольшие проблемы в вашей реализации. Если вы публикуете его, кто-то может это исправить.

Ответ 3

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

Ответ 4

Это дает эффект непрерывности.

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

Ответ 5

Вот решение для горизонтальной прокрутки, которое позволяет отключить прокрутку, но при этом включить ее с помощью вызова smoothScrollToPosition. Это также позволяет пользователю взаимодействовать с дочерними представлениями в представлении переработчика.

Я обнаружил, что другие решения, которые позволили прокрутке временно вызывать функцию smoothScrollToPosition, имели побочный эффект, позволяя пользователю прерывать прокрутку даже при отключении сенсорных событий или при добавлении другого представления поверх представления реселлера.

Чтобы заставить smoothScrollToPosition работать, нужно переопределить LinearSmoothScroller.calculateDxToMakeVisible и снять проверку с canScrollHorizontally()

class NoScrollHorizontalLayoutManager(context: Context) : LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) {

    override fun canScrollHorizontally(): Boolean {
        return false
    }

    override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State?, position: Int) {
        val linearSmoothScroller = ForceHorizontalLinearSmoothScroller(recyclerView.context)
        linearSmoothScroller.targetPosition = position
        startSmoothScroll(linearSmoothScroller)
    }
}



class ForceHorizontalLinearSmoothScroller(context: Context) : LinearSmoothScroller(context) {

    override fun calculateDxToMakeVisible(view: android.view.View, snapPreference: Int): Int {
        val layoutManager = layoutManager
        if (layoutManager == null) {
            return 0
        }
        val params = view.layoutParams as RecyclerView.LayoutParams
        val left = layoutManager.getDecoratedLeft(view) - params.leftMargin
        val right = layoutManager.getDecoratedRight(view) + params.rightMargin
        val start = layoutManager.paddingLeft
        val end = layoutManager.width - layoutManager.paddingRight
        return calculateDtToFit(left, right, start, end, snapPreference)
    }
}

EDIT: Я обнаружил, что пользователь все еще может прекратить прокрутку при нажатии на представление рециркуляции во время выполнения прокрутки. Исправление состояло в том, чтобы переопределить RecycleView.onInterceptTouchEvent и предотвратить события, когда выполнялась плавная прокрутка или когда состояние прокрутки было установлено на SCROLL_STATE_SETTLING. (Использование RecycleView.addOnItemTouchListener для этого не сработает, потому что RecycleView перестает прокручивать, тогда слушатель возвращает true в RecyleView.onInterceptTouchEvent.)

class NoScrollRecyclerView : RecyclerView {
    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)

    override fun onInterceptTouchEvent(e : MotionEvent) : Boolean {
        if (layoutManager?.isSmoothScrolling() == true || scrollState == SCROLL_STATE_SETTLING) {
            return true
        }
        return super.onInterceptTouchEvent(e)
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(e :MotionEvent) : Boolean {
        if (layoutManager?.isSmoothScrolling() == true || scrollState == SCROLL_STATE_SETTLING) {
            return true
        }
        return super.onTouchEvent(e)
    }
}

Ответ 6

android:nestedScrollingEnabled="false"

Сделайте вышеуказанный в файле xml в TAG RecyclerView.