Expand appbarlayout when recyclerview is scrolled/fling to top

后端 未结 2 484
春和景丽
春和景丽 2021-02-14 01:23

I implemented a collapsingtoolbar layout with a recyclerview as shown in the sample code attached. My issue is that, when I fling the list downward, it does not go all the way t

相关标签:
2条回答
  • 2021-02-14 01:58

    I had similar problem and I used a simple trick to expand AppBarLayout when RecyclerView fling to top (you need to have support library >= 23.x.x)

    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                        int firstVisiblePosition = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
                        if (firstVisiblePosition == 0) {
                            mAppBarLayout.setExpanded(true, true);
                        }
                    }
                }
    });
    
    0 讨论(0)
  • 2021-02-14 02:10

    You can fully expand or collapse the App Bar with the setExpanded() method. One implementation could involve overriding dispatchTouchEvent() in your Activity class, and auto-collapsing/expanding your App Bar based on whether it is collapsed past the halfway point:

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            float per = Math.abs(mAppBarLayout.getY()) / mAppBarLayout.getTotalScrollRange();
            boolean setExpanded = (per <= 0.5F);
            mAppBarLayout.setExpanded(setExpanded, true);
        }
        return super.dispatchTouchEvent(event);
    }
    

    In respect to automatically scrolling to the last position on a fling, I have put some code on GitHub that shows how to programmatically smooth scroll to a specific location that may help. Calling a scroll to list.size() - 1 on a fling for instance could replicate the behaviour. Parts of this code by the way are adapted from the StylingAndroid and Novoda blogs:

    public class RecyclerLayoutManager extends LinearLayoutManager {
    
        private AppBarManager mAppBarManager;
        private int visibleHeightForRecyclerView;
    
        public RecyclerLayoutManager(Context context) {
            super(context);
        }
    
        @Override
        public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
            View firstVisibleChild = recyclerView.getChildAt(0);
            final int childHeight = firstVisibleChild.getHeight();
            int distanceInPixels = ((findFirstVisibleItemPosition() - position) * childHeight);
            if (distanceInPixels == 0) {
                distanceInPixels = (int) Math.abs(firstVisibleChild.getY());
            }
            //Called Once
            if (visibleHeightForRecyclerView == 0) {
                visibleHeightForRecyclerView = mAppBarManager.getVisibleHeightForRecyclerViewInPx();
            }
            //Subtract one as adapter position 0 based
            final int visibleChildCount = visibleHeightForRecyclerView/childHeight - 1;
    
            if (position <= visibleChildCount) {
                //Scroll to the very top and expand the app bar
                position = 0;
                mAppBarManager.expandAppBar();
            } else {
                mAppBarManager.collapseAppBar();
            }
    
            SmoothScroller smoothScroller = new SmoothScroller(recyclerView.getContext(), Math.abs(distanceInPixels), 1000);
            smoothScroller.setTargetPosition(position);
            startSmoothScroll(smoothScroller);
        }
    
        public void setAppBarManager(AppBarManager appBarManager) {
            mAppBarManager = appBarManager;
        }
    
        private class SmoothScroller extends LinearSmoothScroller {
            private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;
            private final float distanceInPixels;
            private final float duration;
    
            public SmoothScroller(Context context, int distanceInPixels, int duration) {
                super(context);
                this.distanceInPixels = distanceInPixels;
                float millisecondsPerPx = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
                this.duration = distanceInPixels < TARGET_SEEK_SCROLL_DISTANCE_PX ?
                        (int) (Math.abs(distanceInPixels) * millisecondsPerPx) : duration;
            }
    
            @Override
            public PointF computeScrollVectorForPosition(int targetPosition) {
                return RecyclerLayoutManager.this
                        .computeScrollVectorForPosition(targetPosition);
            }
    
            @Override
            protected int calculateTimeForScrolling(int dx) {
                float proportion = (float) dx / distanceInPixels;
                return (int) (duration * proportion);
            }
        }
    }
    

    Edit:

    AppBarManager in the above code snippet refers to an interface used to communicate with the AppBarLayout in an Activity. Collapse/expand app bar methods do just that, with animations. The final method is used to calculate the number of RecyclerView rows visible on screen:

    AppBarManager.java

    public interface AppBarManager {
    
        void collapseAppBar();
        void expandAppBar();
        int getVisibleHeightForRecyclerViewInPx();
    
    }
    

    MainActivity.java

    public class MainActivity extends AppCompatActivity implements AppBarManager{
    
    @Override
    public void collapseAppBar() {
        mAppBarLayout.setExpanded(false, true);
    }
    
    @Override
    public void expandAppBar() {
        mAppBarLayout.setExpanded(true, true);
    }
    
    @Override
    public int getVisibleHeightForRecyclerViewInPx() {
    
        if (mRecyclerFragment == null) mRecyclerFragment =
                (RecyclerFragment) getSupportFragmentManager().findFragmentByTag(RecyclerFragment.TAG);
    
        int windowHeight, appBarHeight, headerViewHeight;
        windowHeight = getWindow().getDecorView().getHeight();
        appBarHeight = mAppBarLayout.getHeight();
        headerViewHeight = mRecyclerFragment.getHeaderView().getHeight();
        return windowHeight - (appBarHeight + headerViewHeight);
    }
    
    0 讨论(0)
提交回复
热议问题