在BottomSheet中,多个RecyclerView的滚动不起作用

27

我使用 DialogFragment 方法实现了 BottomSheet。在 BottomSheet 中,我有一个 TabLayout 和一个 ViewPager ,其中 ViewPager 包含 2 个页面,每个页面都有一个 RecyclerView。第一个页面(Coffee 标签)的 RecyclerView 可以正常滚动,但是我现在遇到的问题是第二个页面(Milk 标签)的滚动不起作用。您有什么想法可以解决这个问题吗?谢谢!

您可以使用我创建的演示项目进行测试:https://github.com/choongyouqi/bottomsheet

图像描述


我敢打赌它没有正确地膨胀。你把它设置为垂直了吗? - MNM
请检查我的答案,没有使用任何额外的库,也没有任何错误,就像其他答案中所述一样,请检查我的“静态片段”代码并将其应用到你的代码中。 - Jay Rathod
7个回答

16

使用此视图作为根视图:

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class DisallowInterceptView extends LinearLayout {
    public DisallowInterceptView(Context context) {
        super(context);
        requestDisallowInterceptTouchEvent(true);
    }

    public DisallowInterceptView(Context context, AttributeSet attrs) {
        super(context, attrs);
        requestDisallowInterceptTouchEvent(true);
    }

    public DisallowInterceptView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        requestDisallowInterceptTouchEvent(true);
    }


    public boolean dispatchTouchEvent(MotionEvent ev) {
        getParent().requestDisallowInterceptTouchEvent(true);
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                requestDisallowInterceptTouchEvent(false);
                break;
        }
        return super.onTouchEvent(event);
    }

}

然后在用于底部Sheet的布局中:

<?xml version="1.0" encoding="utf-8"?>
<com.your.package.DisallowInterceptView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    android:maxHeight="400dp"
    android:minHeight="300dp"
    android:orientation="vertical"
    >

    ...
</LinearLayout>

</com.your.package.DisallowInterceptView>

运行得非常完美。非常感谢。 - Tahmid
这段代码经过6个小时的调试解决了我的问题。非常感谢! - Nishant Jalan
运作得非常顺畅!看起来这节省了我的时间。 - Sagar Kacha

13

正如R. Zagórski所述,我在这里描述了此滚动行为的原因(链接),即 BottomSheetBehavior 仅支持一个可滚动子项。然而,这个答案并没有重点关注底部工作表对话框。

因此-就像R. Zagórski一样-我扩展了自己的来克服这个限制。 从0.0.3开始支持底部工作表对话框!您可以在此处找到库和示例应用程序: https://github.com/laenger/ViewPagerBottomSheet

要在项目中使用,请将maven存储库URL添加到build.gradle中:

repositories {
    maven { url "https://raw.github.com/laenger/maven-releases/master/releases" }
}

将该库添加到依赖项中:

dependencies {
    compile "biz.laenger.android:vpbs:0.0.3"
}

ViewPagerBottomSheetDialogFragment用作对话框片段的超类。然后在内容视图中设置任何ViewPager:

public class DialogFragment extends ViewPagerBottomSheetDialogFragment {
    @Override
    public void setupDialog(Dialog dialog, int style) {
        super.setupDialog(dialog, style);
        final View contentView = View.inflate(getContext(), R.layout.dialog_bottom_sheet, null);

        final ViewPager viewPager = (ViewPager) contentView.findViewById(R.id.viewpager);
        // ...
        BottomSheetUtils.setupViewPager(viewPager);

        dialog.setContentView(contentView);
    }
}

实现示例


你能帮我在持久底部表格中实现这个功能吗?我尝试使用vbps库,但它存在一些问题,我已经在git仓库上提出了问题。 - saurabh dhillon
在提到的问题中进行了回答。 - laenger
1
如果您正在使用AndroidX,可以查看https://github.com/svrlopatrik/ViewPagerBottomSheet。 - elementstyle
五年过去了,这个问题仍未得到解决。 - Sharukh Mohammed

6
当我在StackOverflow上寻找问题时,我发现了这个线程。它描述了一个错误(至少我是这样看的),即BottomSheetBehaviour仅适用于它找到的第一个可滚动子级。它还提出了不同的CoordinatorLayout.Behavior的使用方法,这些方法在此处被提出并发布。

然而,你的情况有点不同。使用了BottomSheetDialogFragment。这就是提供的解决方案不起作用的地方。但是,我设法克服了这个问题。发布了存储库,其中修改了你的项目以使其正常工作。它使用了前面提到的库中的ViewPagerBottomSheetBehavior

基本上,进行了以下更改:

  1. StatisticFragment extends ViewPagerBottomSheetDialogFragment and not BottomSheetDialogFragment
  2. The onCreateDialog function in StatisticsFragment is changed:

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        ViewPagerBottomSheetDialog dialog = (ViewPagerBottomSheetDialog) super.onCreateDialog(savedInstanceState);
        View rootView = View.inflate(getContext(), R.layout.sheet_main, null);
        viewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
        tabLayout = (TabLayout) rootView.findViewById(R.id.tabs);
        dialog.setContentView(rootView);
        mBehavior = ViewPagerBottomSheetBehavior.from((View) rootView.getParent());
        mBehavior.setPeekHeight(400);
        if (viewPager != null && tabLayout != null) {
            initViewPager();
        }
        return dialog;
    }
    
  3. The following function is invoked on the ViewPager:

    BottomSheetUtils.setupViewPager(viewPager);
    
这就是全部。项目可以正常运行。
以下是幕后工作:
BottomSheetDialogFragment 只有一个方法:
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return new BottomSheetDialog(getContext(), getTheme());
}

返回了一个BottomSheetDialog,但是它的行为被静态定义成了BottomSheetBehavior。需要做的是重写ViewPagerBottomSheetDialogFragment,返回ViewPagerBottomSheetDialog,并将其CoordinatorLayout.Behavior设置为ViewPagerBottomSheetBehavior。此外,自定义的BottomSheet也需要被重写以适应ViewPagerBottomSheetBehavior

1
它对我不起作用 :(。我只能滚动第一个viewpager项的项目。这个库也有同样的问题。 - Ajay Shrestha
你能帮我在持久底部表格中实现这个功能吗?我尝试使用vbps库,但它存在一些问题,我已经在git仓库上提出了问题。 - saurabh dhillon

2

我曾经遇到过同样的问题,为了解决这个问题而不需要覆盖BottomSheetBehavior或者需要额外的库,你可以按照以下步骤进行操作: 在你的底部面板实现中添加一个回调函数,用于注册页面的变化。

fun onPageChanged(currentPage: Int) {
   recycler1.isNestedScrollingEnabled = currentPage == 0
   recycler2.isNestedScrollingEnabled = currentPage == 1
   dialog?.findViewById<FrameLayout>(R.id.design_bottom_sheet)?.requestLayout()
}

在BottomSheetBehavior的onLayoutChild中,会查找第一个支持嵌套滚动的子元素,这个变化导致了查找的重复。虽然不是最优解,但在我的情况下可行。

0

你可以在CoordinatorLayout中使用2个RecyclerView。

<android.support.design.widget.CoordinatorLayout
         android:id="@+id/mainBottomSheet"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:background="@color/white">

         <android.support.v7.widget.RecyclerView
                  android:id="@+id/recyclerViewRight"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent" />

         <android.support.v7.widget.RecyclerView
                  android:id="@+id/recyclerViewLeft"
                  android:layout_width="200dp"
                  android:layout_height="match_parent" />

</android.support.design.widget.CoordinatorLayout>

请查看此帖子链接


0
一个更好的解决方案是将以下行添加到Proguard规则中:
-keep class androidx.viewpager.widget.ViewPager$LayoutParams { int position; }
-keep class com.google.android.material.bottomsheet.BottomSheetBehavior { *** findScrollingChild(...); }

关于此事的更多细节可以在以下链接中找到:https://github.com/kafumi/android-bottomsheet-viewpager#proguardr8


-1

你不需要将 StatisticFragment 扩展为 ViewPagerBottomSheetDialogFragment,也不需要使用任何库。

这是你的代码,我只是针对 View Pager 对你的 Static Fragment 进行了一些更改。

这是我所做的更改后的 Statistic Fragment

所有上述答案中所述的错误都不存在。

只需用这段代码替换你旧的 Static fragment,不需要进行其他任何更改,它将给你所需的输出结果。

我只是在你的 View Pager 周围进行了一些更改,并使其按照你的要求工作。使用了 OnPageChangeListener 方法来获取该视图。

Statistic Fragment.java

    public class StatisticFragment extends BottomSheetDialogFragment {

        private BottomSheetBehavior mBehavior;
        private TabLayout tabLayout;
        private ViewPager viewPager;

        @NonNull
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
            View rootView = View.inflate(getContext(), R.layout.sheet_main, null);

            viewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
            tabLayout = (TabLayout) rootView.findViewById(R.id.tabs);
            if (viewPager != null && tabLayout != null) {
                initViewPager();
            }

            final ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() {

                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

                }

                @Override
                public void onPageSelected(int arg0) {
                    // TODO Auto-generated method stub
                    View view = viewPager.findViewWithTag(arg0);
                    if (view == null) {
                        return;
                    }
                    CustomPagerAdapter adapter = new CustomPagerAdapter(getContext());
                    viewPager.setAdapter(adapter);
                    viewPager.setOffscreenPageLimit(10);
                    tabLayout.setupWithViewPager(viewPager);
                }

                @Override
                public void onPageScrollStateChanged(int state) {

                }
            };

            viewPager.addOnPageChangeListener(pageChangeListener);
            viewPager.post(new Runnable() {
                @Override
                public void run() {
                    pageChangeListener.onPageSelected(viewPager.getCurrentItem());
                }
            });


            dialog.setContentView(rootView);
            mBehavior = BottomSheetBehavior.from((View) rootView.getParent());
            return dialog;


        }

        private void initViewPager() {
            CustomPagerAdapter adapter = new CustomPagerAdapter(getContext());
            viewPager.setAdapter(adapter);
            viewPager.setOffscreenPageLimit(10);
            tabLayout.setupWithViewPager(viewPager);

        }

        @Override
        public void onStart() {
            super.onStart();
            //mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            //mBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
        }

        public class ServiceVideHolder extends RecyclerView.ViewHolder {
            protected ViewGroup mItemView;
            protected TextView mNameView;
            protected TextView mCodeView;

            public ServiceVideHolder(View v) {
                super(v);
                //rootView = v;
                mItemView = (ViewGroup) v.findViewById(R.id.item);
                mNameView = (TextView) v.findViewById(R.id.main_text);
                mCodeView = (TextView) v.findViewById(R.id.sub_text);
            }
        }

        public class ItemViewHolder extends RecyclerView.ViewHolder {
            protected TextView mMainText;
            protected TextView mSubText;

            public ItemViewHolder(View v) {
                super(v);
                mMainText = (TextView) v.findViewById(R.id.main_text);
                mSubText = (TextView) v.findViewById(R.id.sub_text);
            }
        }

        public class ItemAdapter extends RecyclerView.Adapter<ItemViewHolder> {
            private List<Item> items;

            public ItemAdapter(List<Item> items) {
                this.items = items;
            }

            @Override
            public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
                View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
                return new ItemViewHolder(view);
            }

            @Override
            public void onBindViewHolder(final ItemViewHolder viewHolder, final int position) {
                final Item item = items.get(position);
                viewHolder.mMainText.setText(item.name);
                viewHolder.mSubText.setText(item.value);
                viewHolder.mMainText.setTextColor(ResourcesCompat.getColor(getResources(), position % 2 == 0 ? R.color.md_red_500 : R.color.md_blue_500, null));
            }

            @Override
            public int getItemCount() {
                return items.size();
            }
        }

        class ViewPagerAdapter extends FragmentPagerAdapter {
            private final List<Fragment> mFragmentList = new ArrayList<>();
            private final List<String> mFragmentTitleList = new ArrayList<>();

            public ViewPagerAdapter(FragmentManager manager) {
                super(manager);
            }

            @Override
            public Fragment getItem(int position) {
                return mFragmentList.get(position);
            }

            @Override
            public int getCount() {
                return mFragmentList.size();
            }

            public void addFrag(Fragment fragment, String title) {
                mFragmentList.add(fragment);
                mFragmentTitleList.add(title);
            }

            @Override
            public CharSequence getPageTitle(int position) {
                return mFragmentTitleList.get(position);
            }
        }

        public class CustomPagerAdapter extends PagerAdapter {

            private Context mContext;

            public CustomPagerAdapter(Context context) {
                mContext = context;
            }

            @Override
            public Object instantiateItem(ViewGroup collection, int position) {
                //CustomPagerEnum customPagerEnum = CustomPagerEnum.values()[position];
                LayoutInflater inflater = LayoutInflater.from(mContext);
                ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.adapter, collection, false);
                rootView.setTag(position);


                Toast.makeText(mContext, "Inside Instanciate Item", Toast.LENGTH_SHORT).show();

                //View rootView = View.inflate(getContext(), R.layout.adapter, null);
                final RecyclerView mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
                ArrayList<Item> items = new ArrayList<>();

                if (position == 0) {
                    items.add(new Item("Coffee 1", "1"));
                    items.add(new Item("Coffee 2", "2"));
                    items.add(new Item("Coffee 3", "3"));
                    items.add(new Item("Coffee 4", "4"));
                    items.add(new Item("Coffee 5", "5"));
                    items.add(new Item("Coffee 6", "6"));
                    items.add(new Item("Coffee 7", "7"));
                    items.add(new Item("Coffee 8", "8"));
                    items.add(new Item("Coffee 9", "9"));
                    items.add(new Item("Coffee 10", "10"));
                } else {
                    items.add(new Item("Milk 1", "1"));
                    items.add(new Item("Milk 2", "2"));
                    items.add(new Item("Milk 3", "3"));
                    items.add(new Item("Milk 4", "4"));
                    items.add(new Item("Milk 5", "5"));
                    items.add(new Item("Milk 6", "6"));
                    items.add(new Item("Milk 7", "7"));
                    items.add(new Item("Milk 8", "8"));
                    items.add(new Item("Milk 9", "9"));
                    items.add(new Item("Milk 10", "10"));
                }

                final ItemAdapter mAdapter = new ItemAdapter(items);

                mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
                mRecyclerView.setNestedScrollingEnabled(false);
                mRecyclerView.setAdapter(mAdapter);
                Paint paint = new Paint();
                paint.setStrokeWidth(1);
                paint.setColor(ResourcesCompat.getColor(getResources(), R.color.md_grey_500, null));
                paint.setAntiAlias(true);
                paint.setPathEffect(new DashPathEffect(new float[]{25.0f, 25.0f}, 0));
                mRecyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).showLastDivider().paint(paint).build()); //.marginResId(R.dimen.leftmargin, R.dimen.rightmargin)

                collection.addView(rootView);


                return rootView;
            }

            @Override
            public void destroyItem(ViewGroup collection, int position, Object view)    {
                collection.removeView((View) view);
            }

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

            @Override
            public boolean isViewFromObject(View view, Object object) {
                return view == object;
            }

            @Override
            public CharSequence getPageTitle(int position) {
                //CustomPagerEnum customPagerEnum = CustomPagerEnum.values()[position];
                return position == 0 ? "Coffee" : "Milk";
            }

        }
    }

完成了。

enter image description here


1
你只是在每次用户向左/右滑动时重新创建适配器并重新绑定到TabLayout,因此它将丢失前一页的滚动状态。不仅这样做很糟糕且代价高昂,而且会在页面重新布局时导致闪烁。我实际上正在寻找一种解决方案,可以将“fling”事件/计算传输到ViewPager的活动页面。 - You Qi

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