Don't collapse Toolbar when RecyclerView fits the screen

前端 未结 10 1505
陌清茗
陌清茗 2020-11-30 21:08


I\'ve made an app using Android Design Library, with a Toolbar and TabLayout.
Actually 2 tabs are present, both with 2 RecyclerView, that automati

相关标签:
10条回答
  • 2020-11-30 21:15

    I took a slightly different approach to solve this.

    I created a custom AppBarBehavior that disables its self based on cells.

    public class CustomAppBarBehavior extends AppBarLayout.Behavior {
    
        private RecyclerView recyclerView;
        private boolean enabled;
    
        public CustomAppBarBehavior() {
        }
    
        public CustomAppBarBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
            updatedEnabled();
            return enabled && super.onInterceptTouchEvent(parent, child, ev);
        }
    
        @Override
        public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
            return enabled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
        }
    
        @Override
        public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
            return enabled && super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
        }
    
        private void updatedEnabled() {
            enabled = false;
            if(recyclerView != null) {
                RecyclerView.Adapter adapter = recyclerView.getAdapter();
                if (adapter != null) {
                    int count = adapter.getItemCount();
                    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                    if (layoutManager != null) {
                        int lastItem = 0;
                        if (layoutManager instanceof LinearLayoutManager) {
                            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
                            lastItem = Math.abs(linearLayoutManager.findLastCompletelyVisibleItemPosition());
                        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                            StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                            int[] lastItems = staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(new int[staggeredGridLayoutManager.getSpanCount()]);
                            lastItem = Math.abs(lastItems[lastItems.length - 1]);
                        }
                        enabled = lastItem < count - 1;
                    }
                }
            }
        }
    
        public void setRecyclerView(RecyclerView recyclerView) {
            this.recyclerView = recyclerView;
        }
    }
    

    Then set the custom behavior on the app bar layout

    appBarBehavior = new CustomAppBarBehavior();
    CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
    appBarLayoutParams.setBehavior(appBarBehavior);
    appBarLayout.setLayoutParams(appBarLayoutParams);
    

    Last on page change of the view pager updated the RecyclerView on the behavior

    private ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
    
            @Override
            public void onPageSelected(final int position) {             
                appBarLayout.setExpanded(true, true);
                appBarLayout.post(new Runnable() {
                    @Override
                    public void run() {
                        appBarBehavior.setRecyclerView(childFragments.get(position).getRecyclerView());
                    }
                });
            }
    
            @Override
            public void onPageScrollStateChanged(int state) { }
        };
    

    This should work with changing datasets.

    0 讨论(0)
  • 2020-11-30 21:18

    You can check if the last item in RecyclerView is visible. If it's not then turn off scrolling programmatically using this method:

                //turn off scrolling
                AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
                toolbarLayoutParams.setScrollFlags(0);
                mToolbar.setLayoutParams(toolbarLayoutParams);
    
                CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
                appBarLayoutParams.setBehavior(null);
                appBarLayout.setLayoutParams(appBarLayoutParams);
    
    0 讨论(0)
  • 2020-11-30 21:19

    Just remove scroll from

    app:layout_scrollFlags="scroll|enterAlways"
    

    So it should be

    app:layout_scrollFlags="enterAlways"
    
    0 讨论(0)
  • 2020-11-30 21:22

    I guess it's the best solution. You need to define your custom AppBarLayout behavior:

    class CustomScrollingViewBehavior : AppBarLayout.Behavior {
    
        constructor() : super()
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    
        var shouldScroll = true
    
        override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: AppBarLayout, directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
            return shouldScroll && when (target) {
                is NestedScrollView, is ScrollView, is RecyclerView -> {
                    return target.canScrollVertically(1) || target.canScrollVertically(-1)
                }
                else -> super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
            }
        }
    }
    

    And then you just need to use it in your layout as attribute of AppBarLayout:

    ...
    
     <com.google.android.material.appbar.AppBarLayout
                android:id="@+id/appbar"
                android:layout_width="match_parent"
                android:layout_height="56dp"
                app:layout_behavior=".CustomScrollingViewBehavior">
    
    ...
    

    That's it.

    Note: custom behavior also supports fully disabling of the scrolling - you just need to set shouldScroll flag to false

    customScrollingViewBehavior.shouldScroll = false
    
    0 讨论(0)
  • 2020-11-30 21:22
    //turn off scrolling
    AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
    toolbarLayoutParams.setScrollFlags(0);
    mToolbar.setLayoutParams(toolbarLayoutParams);
    
    0 讨论(0)
  • 2020-11-30 21:25

    Final Solution (thanks Michał Z.)

    Methods to turn off/on Toolbar scrolling:

    public void turnOffToolbarScrolling() {
        Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
        AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar_layout);
    
        //turn off scrolling
        AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
        toolbarLayoutParams.setScrollFlags(0);
        mToolbar.setLayoutParams(toolbarLayoutParams);
    
        CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
        appBarLayoutParams.setBehavior(null);
        appBarLayout.setLayoutParams(appBarLayoutParams);
    }
    
    public void turnOnToolbarScrolling() {
        Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
        AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar_layout);
    
        //turn on scrolling
        AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
        toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
        mToolbar.setLayoutParams(toolbarLayoutParams);
    
        CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
        appBarLayoutParams.setBehavior(new AppBarLayout.Behavior());
        appBarLayout.setLayoutParams(appBarLayoutParams);
    }
    


    Find out if last item of RecyclerView is visible in my Fragment.
    If yes, disable scrolling:

    public void updateToolbarBehaviour(){
        if (mLayoutManager.findLastCompletelyVisibleItemPosition() == items.size()-1) {
            ((MainActivity) getActivity()).turnOffToolbarScrolling();
        } else {
            ((MainActivity)getActivity()).turnOnToolbarScrolling();
        }
    }
    
    0 讨论(0)
提交回复
热议问题