如何禁用ViewPager2中特定方向的刷卡

时间:2019-06-18 11:21:16

标签: android kotlin android-viewpager2

我想在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);

4 个答案:

答案 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)的侦听器,并根据viewpager2position的正值或负值来处理所需的情况。

答案 1 :(得分:0)

扩展viewpager类并覆盖功能PostLogoutRedirectUrisonInterceptTouchEvent。然后确定滑动方向,如果不想滑动,则返回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();
                        }
                    }

             }
         }
    );