【问题标题】:Scroll not working for multiple RecyclerView in BottomSheet滚动不适用于 BottomSheet 中的多个 RecyclerView
【发布时间】:2016-09-05 08:11:33
【问题描述】:

我使用DialogFragment 方法实现了BottomSheet。我在BottomSheet 中有一个TabLayoutViewPagerViewPager 托管 2 个页面,每个页面扩展一个 RecyclerView。第一个(咖啡标签)RecyclerView 滚动良好。我现在遇到的问题是,对于第二个(牛奶标签),滚动不起作用。知道如何解决这个问题吗?谢谢!

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

【问题讨论】:

标签: android bottom-sheet


【解决方案1】:

正如 R. Zagórski 所说,我描述了这种滚动行为的原因here,即BottomSheetBehavior 仅支持一个滚动子项。然而,这个答案并不关注底部工作表对话框。

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

要在您的项目中使用,只需将 maven repo 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 repo 上提出了问题.
  • 在提到的issue回答了
  • 如果您使用的是 AndroidX,您可以查看github.com/svrlopatrik/ViewPagerBottomSheet
  • 5 年过去了,这个问题还没有解决。
【解决方案2】:

将此视图用作根视图:

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

}

然后在用于bottmSheet的布局中:

<?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>

【讨论】:

  • 完美运行。非常感谢
  • 这段代码在调试 6 小时后解决了我的问题。非常感谢!
【解决方案3】:

当试图在 StackOverflow 上查找问题时,我发现了 this thread。它描述了这个错误(至少我是这么看的),BottomSheetBehaviour 仅适用于它找到的第一个可滚动子项。它还建议使用不同的CoordinatorLayout.Behavior 建议和发布here

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

基本上进行了以下更改:

  1. StatisticFragment 扩展 ViewPagerBottomSheetDialogFragment 而不是 BottomSheetDialogFragment
  2. StatisticsFragment中的onCreateDialog函数发生了变化:

    @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. 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

【讨论】:

  • 它对我不起作用 :(。我只能滚动第一个 viewpager 项目的项目。这个库也是同样的问题
  • 你能帮我在持久底页而不是模态底页中实现这个吗,我尝试使用 vbps 库,但它有一些问题,我已经在 git repo 上提出了问题.
【解决方案4】:

我遇到了同样的问题,要解决此问题而无需覆盖 BottomSheetBehavior 或需要额外的库,您可以执行以下操作: 在底部工作表实现中实现一个回调,以注册页面的更改。

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

在 onLayoutChild 的 BottomSheetBehavior 实现中,会查找第一个支持嵌套滚动的子项,并通过此更改重复查找。不是最佳解决方案,但在我的情况下工作正常

【讨论】:

    【解决方案5】:

    你可以在 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>
    

    查看此帖子link

    【讨论】:

      【解决方案6】:

      更好的解决方案是在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

      【讨论】:

        【解决方案7】:

        您无需将StatisticFragment 扩展为ViewPagerBottomSheetDialogFragment,也无需为此使用任何库。

        是你的代码,我刚刚在你的Static Fragment 中做了一些与View Pager. 相关的更改

        这是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";
                    }
        
                }
            }
        

        完成了。

        【讨论】:

        • 它将失去上一页的滚动状态,因为您只是在每次用户向左/向右滑动时重新创建适配器并重新绑定到 tablayout。它不仅肮脏且昂贵,而且在页面重新布局时会导致闪烁。我实际上正在寻找一种解决方案,将 fling 事件/计算转移到 viewpager 的活动页面。
        猜你喜欢
        • 1970-01-01
        • 2021-01-08
        • 1970-01-01
        • 2017-04-23
        • 1970-01-01
        • 1970-01-01
        • 2019-06-01
        • 2017-10-07
        • 2017-11-15
        相关资源
        最近更新 更多