Search code examples
androidandroid-coordinatorlayoutandroid-design-libraryandroid-bottom-nav-view

Vertical Scrolling CoordinatorLayout Behavior for BottomNavigationView with RecyclerView vertical scrolling


How to define a CoordinatorLayout.Behavior class for BottomNavigationView which scrolls synchronized with the RecyclerView veritical scroll.

I have seen this and this, but all it does is just show/hide the NavigationView on veritical scroll events immediately. I don't want to show/hide the NavigationView immediately, instead i want a behavior something like the AppbarLayout having a Toolbar with scroll flag as app:layout_scrollFlags="scroll|enterAlways".

 public class BottomNavigationBehavior extends CoordinatorLayout.Behavior<BottomNavigationView> {

    public BottomNavigationBehavior() {
        super();
    }

    public BottomNavigationBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, BottomNavigationView child, View dependency) {
        boolean dependsOn = dependency instanceof FrameLayout;
        return dependsOn;
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, BottomNavigationView child, View directTargetChild, View target, int nestedScrollAxes) {
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, BottomNavigationView child, View target, int dx, int dy, int[] consumed) {
        if(dy < 0) {
            showBottomNavigationView(child);
        }
        else if(dy > 0) {
            hideBottomNavigationView(child);
        }
    }

    private void hideBottomNavigationView(BottomNavigationView view) {
        view.animate().translationY(view.getHeight());
    }

    private void showBottomNavigationView(BottomNavigationView view) {
        view.animate().translationY(0);
    }
}

Solution

  • After some trying I came up with this solution:

      recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                        super.onScrolled(recyclerView, dx, dy);
                        if(dy > 0 && visible){
                            mBinding.bnv.test.setY(mBinding.bnv.getY() + dy);
                            DisplayMetrics metrics = new DisplayMetrics();
                            getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
                            visible = mBiding.bnv.getY() > metrics.heightPixels;
                            if(!visible) {
                                mBinding.bnv.setY(metrics.heightPixels);
                            }
                        } else {
                            mBinding.bnv.setY(mBinding.bnv.getY() + dy);
                            DisplayMetrics metrics = new DisplayMetrics();
                            getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
                            visible = mBinding.bnv.getY() > metrics.heightPixels;
                        }
            }
    

    So you are scrollling the BottomNavigationView with the recycler view

    Or with CoordinatorLayout.Behavior class:

    public class ViewScrollWithRecyclerViewBehavior extends CoordinatorLayout.Behavior<View> {
        private boolean visible = true;
        private boolean inStartPosition = true;
        private float oldY;
        private DisplayMetrics metrics;
    
    
    
        public ViewScrollWithRecyclerViewBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
            metrics = Resources.getSystem().getDisplayMetrics();
        }
    
        @Override
        public boolean layoutDependsOn(CoordinatorLayout parent, View fab, View dependency) {
            return dependency instanceof AppBarLayout;
        }
    
        @Override
        public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
            if (dependency instanceof AppBarLayout) {
                CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
                float dy  = oldY - dependency.getY();
                if(dy > 0 && visible){
                    moveDown(child, oldY);
                } else if(!inStartPosition) {
                   moveUp(child, oldY);
                }
                oldY = dependency.getY();
            }
            return true;
        }
    
        private void moveUp(View child, float dy){
            if(child.getY() + dy >= metrics.heightPixels - child.getHeight()){
                child.setY(metrics.heightPixels - child.getHeight());
            } else {
                child.setY(child.getY() + dy);
            }
            inStartPosition = child.getY() == metrics.heightPixels - child.getHeight();
            visible = child.getY() > metrics.heightPixels;
        }
    
    
        private void moveDown(View child, float dy){
            child.setY(child.getY() + dy);
    
            visible = child.getY() > metrics.heightPixels;
            if(!visible) {
                child.setY(metrics.heightPixels);
            }
        }
    
        @Override
        public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final View child,
                                           final View directTargetChild, final View target, final int nestedScrollAxes) {
            return true;
        }
    
        @Override
        public void onNestedScroll(final CoordinatorLayout coordinatorLayout,
                                   final View child,
                                   final View target, final int dxConsumed, final int dy,
                                   final int dxUnconsumed, final int dyUnconsumed) {
            super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dy, dxUnconsumed, dyUnconsumed);
            if(dy > 0 && visible){
               moveDown(child, dy);
            } else if(!inStartPosition) {
              moveUp(child, dy);
            }
        }
    
    
    }