Ответ 1
Переопределение onInterceptTouchEvent позволяет избежать остановки при нажатии.
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
return false;
}
У меня есть 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;
}
}
Переопределение onInterceptTouchEvent позволяет избежать остановки при нажатии.
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
return false;
}
Во-первых, я не думаю, что это возможно не программно.
Путь 1: Простой - отключить другое представление, основанное на касании.
Если пользователь прикасается к родительскому, отключите прокрутку дочерних просмотров. наоборот.
Часть кода для этого
recyclerView.setOnTouchListener(new View.OnTouchListener()
{
public boolean onTouch(View p_v, MotionEvent p_event)
{
yourChildView.getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
});
Способ 2: поскольку у вас небольшие проблемы в вашей реализации. Если вы публикуете его, кто-то может это исправить.
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
Это дает эффект непрерывности.
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;
}
});
Вот решение для горизонтальной прокрутки, которое позволяет отключить прокрутку, но при этом включить ее с помощью вызова 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)
}
}
android:nestedScrollingEnabled="false"
Сделайте вышеуказанный в файле xml в TAG RecyclerView.
recyclerView.setNestedScrollingEnabled(false);
Подробнее см. документация.