RecyclerView添加折叠式headView

摘要:整体思路就是通过CoordinatorLayout来实现头部view和RecyclerView联动,其中重要的一部分是利用了布局的behavior属性来控制头部view折叠效果,先来个gif图效果:

 

github完整项目地址:https://github.com/Bellriver/HeadFoldingRecyclerView

 

下面来讲解下部分类的核心代码理解:

Activity_main.xml:




    

        
            
            

            
            

                
            

        
    

    

        
    

    

理解:根据CoordinatorLayout的属性要求,要求添加Behavior属性的子布局必须是在CorrdinatorLayout的第一级子布局下。

 

HeadViewBehavior.java:这个类主要是控制headview的缩放效果。

代码1:这部分主要是实现当CoordinatorLayout滑动停止时HeadView自动实现立体层叠式缩放效果。

    @Override
    public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull RelativeLayout child, @NonNull View target, int type) {
        super.onStopNestedScroll(coordinatorLayout, child, target, type);
        if (!mOverScroller.isFinished()) {
            mOverScroller.forceFinished(true);
        }
        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        snapToChildIfNeeded(coordinatorLayout, child, params.height, params);
    }

    private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, RelativeLayout child, int finalHeight,
                                     CoordinatorLayout.LayoutParams layoutParams) {
        if (!isAbleFold) {
            return;
        }
        if (!mOverScroller.isFinished()) {
            mOverScroller.forceFinished(true);
        }
        if (finalHeight > (maxHeight - minHeight) / 2 + minHeight) {//fold
            mOverScroller.startScroll(0, finalHeight, 0, maxHeight - finalHeight, duration);
        } else {//unfold
            mOverScroller.startScroll(0, finalHeight, 0, minHeight - finalHeight, duration);
        }
        if (mFlingRunnable == null) {
            mFlingRunnable = new FlingRunnable(coordinatorLayout, child, layoutParams);
        }
        ViewCompat.postOnAnimation(child, mFlingRunnable);

    }

    private class FlingRunnable implements Runnable {
        private final CoordinatorLayout mParent;
        private final RelativeLayout mCardView;
        CoordinatorLayout.LayoutParams params;

        FlingRunnable(CoordinatorLayout parent, RelativeLayout layout, CoordinatorLayout.LayoutParams params) {
            mParent = parent;
            mCardView = layout;
            this.params = params;
        }

        @Override
        public void run() {
            if (mCardView != null && mOverScroller != null) {
                if (mOverScroller.computeScrollOffset()) {
                    int abs = Math.abs(mOverScroller.getCurrY());
                    refresh(mParent,mCardView, params, abs);
                    ViewCompat.postOnAnimation(mCardView, this);
                } else {
//                    onFlingFinished(mParent, mCardView, params);
                }
            }
        }
    }

    private void refresh(CoordinatorLayout parent, @NonNull RelativeLayout child, CoordinatorLayout.LayoutParams layoutParams, int finalHeight) {
        layoutParams.height = finalHeight;
        child.setLayoutParams(layoutParams);
        float factor = (float) finalHeight / maxHeight;
        float alpha = 0.5f * (1 + factor);
        float scale = 0.8f + 0.2f * factor;
        child.setAlpha(alpha);
        child.setScaleX(scale);
        child.setScaleY(scale);

        /*顶部右图固定方案*/
        RelativeLayout rightImg = parent.findViewById(R.id.ic_top_card_right);
        alpha = 0.9f + 0.1f * factor;
        rightImg.setAlpha(alpha);
        rightImg.setScaleY(scale);
        rightImg.setScaleX(scale);
    }

代码2:这个方法主要是实现在CoordinatorLayout滚动过程中实现HeadView的收缩变化效果,dy代表CoordinatorLlayout上下滚动的距离,dy<0这是向下滚动,dy>0则是向上滚动。

@Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull RelativeLayout child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        if (!isAbleFold) {
            return;
        }
        Log.d(TAG, "onNestedPreScroll");
        if (!mOverScroller.isFinished()) {
            mOverScroller.forceFinished(true);
        }

        if (target instanceof RecyclerView) {
            RecyclerView recyclerView = (RecyclerView) target;
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
            int firstCompletelyVisibleItemPosition = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
            CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
            int oldheight = layoutParams.height;
            int finalHeight = oldheight - dy;
            if (dy > 0 && oldheight >= minHeight /*&& firstCompletelyVisibleItemPosition == 0*/) {//up
                finalHeight = finalHeight > minHeight ? finalHeight : minHeight;
                refresh(coordinatorLayout, child, layoutParams, finalHeight);
                consumed[1] = /*dy*/oldheight - finalHeight;
            } else if (dy < 0 && oldheight <= maxHeight && firstCompletelyVisibleItemPosition == 0) {//down
                finalHeight = finalHeight > maxHeight ? maxHeight : finalHeight;
                refresh(coordinatorLayout, child, layoutParams, finalHeight);
                consumed[1] = dy/*oldheight - finalHeight*/;
            } else {
//                CoordinatorLayout.LayoutParams layoutParams1 = (CoordinatorLayout.LayoutParams) recyclerView.getLayoutParams();
//                CoordinatorLayout.Behavior behavior = layoutParams1.getBehavior();
//                behavior.onNestedPreScroll(coordinatorLayout, recyclerView, recyclerView, dx, dy, consumed, type);
//                refresh(child, layoutParams, finalHeight);
            }
        }
    }

 

RecycleBehavior.java:这个类是用来控制recyclerview依据headview的缩放高度来联动自己的Y轴的坐标也就是在布局中的高度位置。

代码1:这个方法是根据headView的高度计算RecyclerView的高度。

    @Override
    public boolean onMeasureChild(CoordinatorLayout parent, RecyclerView child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
        Log.d(TAG,"onMeasureChild");
        List dependencies = parent.getDependencies(child);
        View dependency = findFirstDependency(dependencies);
        if (dependency!=null){
            int mode = View.MeasureSpec.getMode(parentHeightMeasureSpec);
            int size = View.MeasureSpec.getSize(parentHeightMeasureSpec);
            CoordinatorLayout.LayoutParams layoutParams= (CoordinatorLayout.LayoutParams) dependency.getLayoutParams();
            int i=size-minHeight-layoutParams.topMargin+gapMaxHeight;
            int measureHeight = View.MeasureSpec.makeMeasureSpec(i, mode);
            parent.onMeasureChild(child,parentWidthMeasureSpec,widthUsed,measureHeight,heightUsed);
            return true;
        }
        return false;
    }

代码2:这个方法主要是根据HeadView的高度设置RecyclerView的y坐标也就是顶部位置。

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, RecyclerView child, View dependency) {
        Log.d(TAG,"onDependentViewChanged");
        CoordinatorLayout.LayoutParams params= (CoordinatorLayout.LayoutParams) dependency.getLayoutParams();
        float factor=(float)params.height/maxHeight;
        int gapHeight = (int) (factor* gapMaxHeight); //中間縫隙高度
        int y= (int) (factor*params.height+params.topMargin+gapHeight);  //child距离顶部的高度
        child.setY(y);
        return true;
    }

 

你可能感兴趣的:(安卓开发)