当前位置: 首页 > 知识库问答 >
问题:

如何在ViewPager2中禁用特定方向的滑动

翟英达
2023-03-14

我想在查看页面2中禁用从右向左滑动。我基本上有一个viewpager2元素,在导航抽屉中有两页。我希望我的第二页仅在单击第一页中的某个元素时显示(从第一页向右向左滑动不应打开第二页),而当我在第二页时,viewpager2滑动(从左向右滑动)应像在viewpager中一样滑动。

我尝试过扩展ViewPager2类并覆盖触摸事件,但不幸的是它ViewPager2是最终类,因此我无法扩展它。

其次,我尝试使用将userinputened设置为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);

共有3个答案

公良骁
2023-03-14

2个以上片段的最简单解决方案

int previousPage = 0;
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        super.onPageScrolled(position, positionOffset, positionOffsetPixels);
        if(position < previousPage){
            pager.setCurrentItem(previousPage, false);
        } else {
            previousPage = position;
        }
    }
});
曾喜
2023-03-14
匿名用户

2个以上碎片的解决方案。(向下滚动查看解决方案,最后更新代码以更正某些错误。)

这个有点棘手。

我认为,人们之所以希望在一个方向上禁用滑动,是因为在运行时无法添加片段,同时保持先前显示片段的状态。

因此,人们所做的是预加载所有片段,使其看起来好像没有显示的片段根本就不存在。

现在,如果团队使适配器不受ViewLifeCycle的限制,那么可以通过使用ListDifference选项轻松解决这一问题,该选项将正确地将更新传播到RecyclerView适配器,但由于ViewPager2的每个dataSetChanged都需要一个新适配器,因此需要重新创建整个片段,ListDifference对ViewPager2没有影响。

但可能不完全是因为我不确定ListDifference是否能够识别“头寸互换”以保持状态。

现在,关于建议使用registerOnPageChangeCallback()的答案。

使用超过2个片段注册registerOnPageChangeCallback()是无用的,这是因为当调用此方法时,已经太晚了,无法执行某些操作,这会导致窗口中途变得无响应,而不是addOnItemTouchListener();它能够在触摸到达视图之前拦截触摸。

在某种意义上,阻止和允许刷卡的完整事务将由两个方法执行,registerOnPageChangeCallback()和addOnItemTouchListener()。

registerOnPageChangeCallback()将告诉我们的适配器应该停止工作的方向(通常从左到右(我简单地称之为“左”))以及在什么页面,而addOnItemTouchListener()将告诉视图在我们想要的方向上的正确时刻拦截抛投。

问题是,要使用TouchListener,我们需要访问ViewPager2中的内部回收器视图。

实现这一点的方法是重写来自碎片状态适配器(FragmentStateAdapter)的onAttachedToWindow()方法。

@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);
}

现在要附加到RecyclView的正确侦听器称为RecyclView。SimpleOnItemTouchListener(),问题是侦听器无法区分“右”和“左”。

public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e)

我们需要混合两种行为以获得期望的结果:

a)rv.getScrollState()==RecyclView.SCROLL_STATE_DRAGGING

b) e.getX()

我们还需要跟踪最后一个x点,之所以这样做是因为侦听器会在rv之前多次触发。getScrollState()旋转滚动\u状态\u拖动

解决方案。

我用来从左到右标识的类:

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
    }

}

因为resolve()方法在rv之前至少执行了3次以上,所以不需要在事务之后加上previousX int。getScrollState()变为拖动时的滚动状态

定义此类后,整个代码应如下所示(在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

我们需要检查空值,因为供应商第一次将为空(除非您将其设置为初始值),回收器视图首先附加到窗口。

现在使用它。

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

             }
         }
    );

已经对DirectionResolver.class进行了一些更新,以解决中小企业错误和更多功能:

private static class DirectionResolver {

    private float previousX = 0;
    private boolean right2left;

    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;
        } else {
            directionResult = Direction.left_and_right;
        }
        previousX = newX;
        return right2left ? Direction.right_to_left : directionResult;
    }


    public void reset(Direction direction) {
        previousX = direction == Direction.left_to_right ? previousX : 0;
    }

    public void reset() {
        right2left = false;
    }


}

方向枚举:

public enum Direction {
    right_to_left, left_to_right, left_and_right;

    //Nested RecyclerViews generate a wrong response from the resolve() method in the direction resolver.
    public boolean equals(Direction direction, DirectionResolver resolver) {
        boolean result = direction == left_and_right || super.equals(direction);
        resolver.right2left = !result && direction == left_to_right;
        return result;
    }

}

实施:

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

                    Direction direction = directionSupplier.get();

                    if (direction != null) {
                        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, resolver);
                            resolver.reset(direction);
                        }
                    }

                    return shouldIntercept;
                }
            }
    );

    super.onAttachedToRecyclerView(recyclerView);
}

public void disableDrag(Direction direction) {
    directionSupplier.set(() -> direction);
    resolver.reset();
}

public void enableDrag() {
    directionSupplier.set(() -> null);
}

使用:

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

             }
         }
    );

李胡媚
2023-03-14

我找到了一个监听器,当用户尝试滑动时可以监听,然后它会检查当前页面,如果它是第一页,则禁用用户输入,否则按默认情况启用它。

下面是这方面的代码片段

在Java中:

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

静态编程语言

viewPager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
    override fun onPageScrollStateChanged(state: Int) {
        super.onPageScrollStateChanged(state)

        viewPager.isUserInputEnabled = !(state == SCROLL_STATE_DRAGGING && viewPager.currentItem == 0)
    }
})

由于我的场景只有2页,检查页码对我有好处,但如果我们有超过2页并且我们需要禁用在一个特定方向上的滑动,我们可以使用onPageScrolled(int位置,浮点位置偏移,int位置偏移Pixels)viewpage er2的监听器,并根据位置的正负值处理所需的场景。

 类似资料:
  • 我想禁用刷卡,但仅限于右侧。我在这个答案中找到了一个有效的解决方案。不幸的是,这复制了整个源代码以实现目标。有没有只继承现有类而不复制的方法?

  • 问题内容: 在这里四处寻找了一段时间,但似乎找不到有效的解决方案。 我试图在Swift中禁用滑动以返回到上一个视图手势。 我尝试了多种解决方案,包括: 和 是否有执行此操作的新方法或其他可行的方法? 问题答案: 您可以禁用它,但是不建议这样做,因为大多数iOS用户通过滑动来回退,而通过按后退按钮可以减少回退。如果要禁用它,则使用a 代替推送转移(不是很大的转移)更为合理。如果您真的想摆脱滑动返回功

  • 如何阻止viewpager仅向一个方向滚动。例如,只允许向右滑动,而不允许向左滑动。我完全被卡住了,任何帮助都将不胜感激。要注意的是,我需要让它在Android 2.2版上工作,所以我使用ViewPager的兼容性库。 提前感谢

  • 我有一个Recyerview。RecycerView的每个单元格都有一个关联的文件。我正在使用ItemTouchHelper进行刷屏。当用户刷一个单元格时,我希望禁用其他单元格的刷,只要与被刷单元格相关联的文件没有被删除。如何在特定时间内启用和禁用刷屏。我在堆栈上看了关于这个的问题,但他们大多回答了如何禁用特定位置的滑动,这是我不想要的。

  • 我已将ViewPager2与Tablayout连接。其他引用ViewPager的帖子提供了覆盖setCurrentItem(position,false)的方法,其中false禁用平滑滚动。但是,TabLayoutMediator调用OntabSelect(tablayout.tab tab),后者调用viewPager.setCurrentItem(postion,true)。如果TabLayo

  • 问题内容: 我正在使用无状态Spring Security,但是如果要注册,我想禁用Spring Security。我禁用了 但它不起作用,我在下面收到错误消息: 我认为这意味着弹簧安全过滤器正在工作 我的网址顺序始终为“ / api / v1” 我的spring配置是 我的身份验证过滤器是 我的控制器是 我怎么做? 问题答案: 使用它意味着每个经过身份验证的用户,但是你禁用了匿名访问,因此将无法