我想在ViewPager2
中禁用从右向左滑动。
我的导航抽屉中基本上有一个包含2页的viewpager2元素。我希望仅在单击第一页中的某些元素时显示第二页(从第一页从右向左滑动不应该打开第二页),而当我在第二页中时,则在viewpager2滑动(左向右滑动),就应该像在viewpager中一样滑动。
我尝试扩展ViewPager2
类并覆盖触摸事件,但是很遗憾,它ViewPager2
是最后一堂课,所以我不能扩展它。
其次,我尝试使用setUserInputEnabled
方法为false,但这完全禁用了所有滑动(我只想禁用从右向左滑动)。如果我可以找到一些监听器,该监听器可以在滑动前检查当前页面,并在其他情况下禁止滑动,则可能会起作用。
implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha05'
用于设置ViewPager2的代码
ViewPager2 pager = view.findViewById(R.id.pager);
ArrayList<Fragment> abc = new ArrayList<>();
abc.add(first);
abc.add(second);
navigationDrawerPager.setAdapter(new DrawerPagerAdapter(
this, drawerFragmentList));
pager.setAdapter(new FragmentStateAdapter(this), abc);
答案 0 :(得分:1)
我找到了一个监听器,可以在用户尝试滑动时进行监听,然后它将检查当前页面;如果它是第一页,请禁用用户输入,否则默认启用它。
这是该代码段:-
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
if (state == SCROLL_STATE_DRAGGING && pager.getCurrentItem() == 0) {
pager.setUserInputEnabled(false);
} else {
pager.setUserInputEnabled(true);
}
}
});
由于我的情况是只有2页,因此检查页码对我来说是件好事,但是如果我们有2页以上并且需要在一个特定方向上禁用滑动,则可以使用{{1} } onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
的侦听器,并根据viewpager2
和position
的正值或负值来处理所需的情况。
答案 1 :(得分:0)
扩展viewpager类并覆盖功能PostLogoutRedirectUris
和onInterceptTouchEvent
。然后确定滑动方向,如果不想滑动,则返回false。
您可以使用此辅助方法进行滑动检测:
onTouchEvent
然后在您的触摸事件方法中:
float downX; // define class level variable in viewpager
private boolean wasSwipeToLeftEvent(MotionEvent event){
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
return false;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
return event.getX() - downX > 0;
default:
return false;
}
}
我修改了此答案中的代码,如果您需要更多说明,请参见:https://stackoverflow.com/a/34111034/4428159
答案 2 :(得分:0)
覆盖MotionEvent之类的滑动方法。
答案 3 :(得分:0)
解决两个以上碎片的问题。 (向下滚动以获取解决方案)
这个有点棘手。
我认为,人们之所以希望在一个方向上禁用滑动,是因为无法在运行时添加片段,而又要保持先前片段的状态。
所以人们正在做的是他们正在预加载所有片段,并使其好像没有显示的片段就根本不存在。
现在,如果团队将Adapter设置为不受ViewLifeCycle约束,则可以使用ListDiffer选项轻松解决此问题,该选项可以将更新正确传播到RecyclerView Adapter,但是因为每个dataSetChanged都需要一个新的Adapter在ViewPager2中,整个片段都需要重新创建,并且ListDiffer对ViewPager2没有影响。
但是可能并不完全,因为我不确定ListDiffer是否能够识别“位置交换”以保留状态。
现在,在有关建议使用registerOnPageChangeCallback()
的答案上。
之所以没有必要用两个以上的片段来注册OnPageChangeCallback(),是因为在调用此方法时,已经来不及做一些事情,这是因为窗口在中途变得无响应,与addOnItemTouchListener()相反;能够在触摸到达视图之前对其进行拦截。
从某种意义上说,阻止和允许滑动的完整事务将由registerOnPageChangeCallback()和addOnItemTouchListener()这两种方法执行。
registerOnPageChangeCallback()
会告诉我们的适配器哪个方向应该停止工作(通常从左到右(我将其简称为“左”))以及在什么页面上停止,而addOnItemTouchListener()
会告诉视图在适当的时候按我们想要的方向拦截猛击。
问题在于,要使用该TouchListener,我们需要访问ViewPager2内部的内部RecyclerView。
做到这一点的方法是从onAttachedToWindow()
覆盖FragmentStateAdapter
方法。
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
现在将连接到RecyclerView的正确侦听器称为RecyclerView.SimpleOnItemTouchListener()
,问题是侦听器无法将“右”猛冲与“左”猛冲区分开。
public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e)
我们需要混合两种行为以获得所需的结果:
a)rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING
b)e.getX()
我们还需要跟踪最后一个x点,之所以可行,是因为侦听器将在rv.getScrollState()
变成SCROLL_STATE_DRAGGING
之前触发多次。
解决方案。
我以前从左到右识别的类:
public class DirectionResolver {
private float previousX = 0;
public Direction resolve(float newX) {
Direction directionResult = null;
float result = newX - previousX;
if (result != 0) {
directionResult = result > 0 ? Direction.left_to_right : Direction.right_to_left ;
}
previousX = newX;
return directionResult;
}
public enum Direction {
right_to_left, left_to_right
}
}
在事务处理之后无需将previousX int设为零,因为resolve()方法至少在rv.getScrollState()
变为SCROLL_STATE_DRAGGING
之前执行了3次以上。
定义了此类后,整个代码应如下所示(在FragmentStateAdapter
内):
private final DirectionResolver resolver = new DirectionResolver();
private final AtomicSupplier<DirectionResolver.Direction> directionSupplier = new AtomicSupplier<>();
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
recyclerView.addOnItemTouchListener(
new RecyclerView.SimpleOnItemTouchListener(){
@Override
public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
boolean shouldIntercept = super.onInterceptTouchEvent(rv, e);
DirectionResolver.Direction direction = directionSupplier.get();
if (direction != null) {
DirectionResolver.Direction resolved = resolver.resolve(e.getX());
if (rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {
//resolved will never be null if state is already dragging
shouldIntercept = resolved.equals(direction);
}
}
return shouldIntercept;
}
}
);
super.onAttachedToRecyclerView(recyclerView);
}
public void disableDrag(DirectionResolver.Direction direction) {
Log.println(Log.WARN, TAG, "disableDrag: disabling swipe: " + direction.name());
directionSupplier.set(() -> direction);
}
public void enableDrag() {
Log.println(Log.VERBOSE, TAG, "enableDrag: enabling swipe");
directionSupplier.set(() -> null);
}
如果您要问什么是AtomicSupplier,它与AtomicReference <>类似,因此,如果要使用它,它将给出相同的结果。
想法是重用相同的SimpleOnItemTouchListener()
,为此,我们需要为其提供参数。
我们需要检查是否为空,因为供应商第一次将recyclerView附加到窗口时(除非您将其初始化为初始值)为空。
现在使用它。
binding.journalViewPager.registerOnPageChangeCallback(
new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (conditionToDisableLeftSwipe.at(position)) {
adapter.disableDrag(DirectionResolver.Direction.right_to_left);
} else {
adapter.enableDrag();
}
}
}
}
);