使用嵌套滚动视图(NestedScrollView)处理VerticalViewPager的滚动

8

背景:

我正在使用ViewPager的变体,它不是水平方向滚动,而是垂直方向滚动。

VerticalViewPager: https://android.googlesource.com/platform/packages/apps/DeskClock/+/master/src/com/android/deskclock/VerticalViewPager.java

android.support.v4.app.FragmentStatePagerAdapter被用来设置VerticalViewPager的内容。

我每次调用FragmentStatePagerAdaptergetItem()时,都会创建并返回一个新的android.support.v4.app.Fragment实例,如下所示:

@Override
public Fragment getItem(int position) {
   return  PreviewFragment.getNewFragmentInstance(position);
}

PreviewFragment渲染可能不适合屏幕的内容。 为了显示所有不适合屏幕的内容,android.support.v4.widget.NestedScrollView被用作PreviewFragment布局的父元素。

问题:

PreviewFragment的内容在位置0滚动得很好。 但是在页面位置1上滑动非常困难,必须多次执行扔或滑动操作才能切换到下一页。

此外,回到位置0的扔或滑动也非常困难或几乎不可能。

要求:

扔或滑动或切换到PreviewFragment的下一个实例应该是平稳的。 换句话说,内容的滚动应该平稳,同时还要考虑到向下一页轻松滑动的情况。

链接到上述示例项目


是的,我该怎么做呢?我的问题是“如何使用我在问题中提到的当前实现使其工作?”我尝试使用RecyclerView(item宽度和高度设置为match_parent),通过将每个子项包装在NestedScrollView中,但没有成功。由于没有成功,我切换到了VerticalViewPager,其中每个页面再次包装在NestedScrollView中。如何在使用RecyclerView或VerticalViewPager时使内容滚动,并使页面本身可以滚动或切换到下一页? - Anurag Singh
@CommonsWare 这就像如何使包含在另一个ScrollView(RecyclerView或ViewPager)中的子项或页面内部滚动一样,您能告诉我是否遗漏了任何信息吗? - Anurag Singh
2
我从未使用过NestedScrollView,因此我不知道如何解决您的情况。我已发布了一份悬赏以查看是否可以得到答案。 - CommonsWare
@CommonsWare 当然。谢谢你 :-) - Anurag Singh
@AnuragSingh,你能在Github上发布一个带有你的设置的简单项目吗? - azizbekian
显示剩余3条评论
1个回答

6
我查看了你的Github代码。我为你解决了问题,并发送了一个pull request。问题在于,如果返回true,则onInterceptTouchEvent会使ViewPager滚动,如果返回false,则会禁用ViewPager触摸并使NestedScrollView滚动。所以我在这里做的是检查是否需要滚动NestedScrollView,如果NestedScrollView在顶部或底部,则返回false,否则返回true。我在这里发布代码。你可以修改它并使其更加简洁。 Github链接 - https://github.com/karanatwal/vertical-viewpager-nestedscrollview 下面是VerticalViewPager代码 -
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    boolean toIntercept = super.onInterceptTouchEvent(flipXY(ev));
    // Return MotionEvent to normal


    float x = ev.getX();

    flipXY(ev);
    int direction=1;

    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mStartDragX = x;
            break;

        case MotionEvent.ACTION_MOVE:
            if (mStartDragX < x ) {
                direction=-1;
            } else if (mStartDragX > x) {
                direction=1;
            }
            MainActivity.MyPagerAdapter adapter = (MainActivity.MyPagerAdapter)this.getAdapter();
            if (adapter.getCurrentFragment() instanceof FragmentViewPager){
                FragmentViewPager fragmentViewPager = (FragmentViewPager)adapter.getCurrentFragment();
                toIntercept = !fragmentViewPager.getNestedScrollView().canScrollVertically(direction);
                int range = fragmentViewPager.getNestedScrollView().computeVerticalScrollRange();
                int offset = fragmentViewPager.getNestedScrollView().computeVerticalScrollOffset();
                int extent = fragmentViewPager.getNestedScrollView().computeVerticalScrollExtent();
                int percentage = (int)(100.0 * offset / (float)(range - extent));

                System.out.println("  range: "+range);
                System.out.print("  offset: "+offset);
                System.out.print("  extent: "+extent);                  
                System.out.print("  percentage: "+percentage);
                System.out.print("  direction: "+direction);

                if (fragmentViewPager.getNestedScrollView().computeVerticalScrollOffset()==0 && direction==-1){
                    //top
                    return true;
                }
                else if (percentage>99 && direction==1){
                    //bottom
                    return true;
                }else {
                    //scroll nestedscrollview
                    return false;
                }

            }

            break;
    }


    return toIntercept;
}

您的MyPagerAdapter -

public class MyPagerAdapter extends FragmentStatePagerAdapter {

    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    private Fragment mCurrentFragment;

    public Fragment getCurrentFragment() {
        return mCurrentFragment;
    }
    //...
    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        if (getCurrentFragment() != object) {
            mCurrentFragment = ((Fragment) object);
        }
        super.setPrimaryItem(container, position, object);
    }

    @Override
    public android.support.v4.app.Fragment getItem(int pos) {
        switch (pos) {
            case 0:
                return FragmentViewPager.newInstance("Title Start", R.drawable.ic_launcher_background);
            case 1:
                return FragmentViewPager.newInstance("Rock", R.drawable.rock);
            case 2:
                return FragmentViewPager.newInstance("Paper", R.drawable.paper);
            case 3:
                return FragmentViewPager.newInstance("Scissors", R.drawable.scissors);
            case 4:
                return FragmentViewPager.newInstance("Title End", R.drawable.ic_launcher_background);
            default:
                return FragmentViewPager.newInstance("Default Title", R.drawable.rock);
        }
    }

    @Override
    public int getCount() {
        return 5;
    }
}

为了避免使用受限的API方法- 使用以下逻辑作为替代方案

 //offset , range and extent
            int scrollY = scrollView.getScrollY();               
            int scrollContentHeight = scrollView.getChildAt(0).getHeight();
            int screenHeight = Utility.getScreenHeight(context); 
            int statusBarHeight = Utility.getStatusBarHeight(context);

            double percent = ((((float) scrollY) / ((float) (scrollContentHeight - screenHeight + statusBarHeight))));

你所需要的只是滚动百分比和滑动方向。关于如何获取滚动百分比和滑动方向,Stack Overflow上有很多相关问题,例如thisthis。你需要做一些研究。

NestedScrollView.computeVerticalScrollRange(), NestedScrollView.computeVerticalScrollOffset(), NestedScrollView.computeVerticalScrollExtent(),只能在同一库组(groupId=com.android.support)内调用。此API已被标记为未满足限制的限制。 - Anurag Singh
受限方法不应从Android框架中访问,因为这是不可接受的,而且限制在未来可能会更加严格或随时更改。这将导致应用程序无法编译并显然破坏当前功能。 - Anurag Singh
不用担心,你可以使用 @SuppressLint("RestrictedApi")。参见此链接 - https://dev59.com/DFgR5IYBdhLWcg3wlOEr - karanatwal.github.io
2
安乐瑞格·辛格是正确的,这些方法之所以限制,是因为谷歌不想将它们纳入API的一部分,这样他们就可以随时更改或消失。 - Leo supports Monica Cellio
我现在没多少时间,伙计们 @LeonardoAcevedo ,Anurag 你们应该尝试其他方法,而不是对它进行反对票或者研究开发。反对并静止不动不是解决办法,我还是会发布解决方案的,你可以查看编辑内容。 - karanatwal.github.io
1
@KarandeepAtwal,感谢您抽出宝贵时间发布解决方案。我仍在修改您发布的解决方案,以克服RestrictedApi访问并修改我的当前嵌套滚动方法。 顺便说一下,我没有投反对票。我不知道还有一个功能可以知道谁投了反对票。 - Anurag Singh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接