RecyclerView轻松实现悬浮头部分组列表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/binbinqq86/article/details/54427505

转载请注明出处:http://blog.csdn.net/binbinqq86/article/details/54427505

关于RecyclerView的使用,相信大家都不陌生,并且功能的强大早已让众多开发者臣服,本篇主要讲解联系人列表效果的悬浮头部分组列表的实现,先上效果图:

一般的思路应该是利用RecyclerView的itemType来区分标题和下面的子数据而采用不同的布局,再加上RecyclerView的滚动监听来实现头部的悬浮标题移动、隐藏、显示。本篇的主要思路是从ItemDecoration下手,说到这里我想你应该心中有个眉目了,如果对ItemDecoration还不熟悉的朋友,可以去看我的前两篇文章:

先上代码:

@Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int pos=parent.getChildViewHolder(view).getAdapterPosition();
        if(keys.containsKey(pos)){//留出头部偏移
            outRect.set(0,mTitleHeight,0,0);
        }else{
            outRect.set(0,dividerHeight,0,0);
        }
    }

首先就是根据当前view的position判断需要在上方留出分割线空隙还是标题栏头部空隙。

然后是onDraw方法绘制具体的分割线和标题栏,看代码:

private void drawVertical(Canvas c, RecyclerView parent){
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int top=0;
        int bottom=0;
        for (int i = 0; i < parent.getChildCount(); i++) {
            View child=parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            if(!keys.containsKey(params.getViewLayoutPosition())){
                //画普通分割线
                top=child.getTop()-params.topMargin-dividerHeight;
                bottom=top+dividerHeight;
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }else{
                //画头部
                top=child.getTop()-params.topMargin-mTitleHeight;
                bottom=top+mTitleHeight;
                c.drawRect(left,top,right,bottom,mBackgroundPaint);
                float x=TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,mContext.getResources().getDisplayMetrics());
                float y=bottom - (mTitleHeight - mTextHeight) / 2 - mTextBaselineOffset;//计算文字baseLine
                c.drawText(keys.get(params.getViewLayoutPosition()),x,y,mTextPaint);
            }
        }
    }

其中用到了canvas.drawText方法,这里需要注意y坐标的计算,文本的绘制是根据baseLine来进行的,不懂的朋友可以去网上查阅相关的知识,本文不再重点讲解。

最后就是onDrawOver方法的实现,该方法主要用来在RecyclerView最上层绘制,这样当列表滚动的时候,可以绘制标题栏,这样就看上去就一直悬浮在页面顶部,主要难点就是去判断上下两组标题栏的碰撞,下面看代码:

@Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        if(!showFloatingHeaderOnScrolling){
            return;
        }
        int firstVisiblePos=((LinearLayoutManager)parent.getLayoutManager()).findFirstVisibleItemPosition();
        if(firstVisiblePos==RecyclerView.NO_POSITION){
            return;
        }
        String title=getTitle(firstVisiblePos);
        if(TextUtils.isEmpty(title)){
            return;
        }
        boolean flag=false;
        if(getTitle(firstVisiblePos+1)!=null&&!title.equals(getTitle(firstVisiblePos+1))){
            //说明是当前组最后一个元素,但不一定碰撞了
            View child=parent.findViewHolderForAdapterPosition(firstVisiblePos).itemView;
            if(child.getTop()+child.getMeasuredHeight()<mTitleHeight){
                //进一步检测碰撞
                c.save();//保存画布当前的状态
                flag=true;
                c.translate(0,child.getTop()+child.getMeasuredHeight()-mTitleHeight);//负的代表向上
            }
        }
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int top=parent.getPaddingTop();
        int bottom=top+mTitleHeight;
        c.drawRect(left,top,right,bottom,mBackgroundPaint);
        float x=TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,mContext.getResources().getDisplayMetrics());
        float y=bottom - (mTitleHeight - mTextHeight) / 2 - mTextBaselineOffset;//计算文字baseLine
        c.drawText(title,x,y,mTextPaint);
        if(flag){
            //还原画布为初始状态
            c.restore();
        }
    }

注意第四行,我加了个变量来控制是否需要悬浮头部的效果,如果不需要则直接返回,只有分组列表的效果。而getTitle方法主要是用来获取每个item的title,碰撞的检测主要是根据当前第一个可见元素和下一个元素进行对比,也就是代码第16行,如果这两个元素所在的title不一样,就说明是属于不同的分组,标题栏即将进行碰撞了,当然这样检测还是不够的,还需要根据item的高度和标题栏的高度结合item的getTop值来共同判断,这样就可以去移动画布,造成标题栏被下一个挤上去的效果,同时需要在下面还原移动过的画布。

怎么样,整个逻辑和过程就讲解完了,实现起来简单吧,有疑问的朋友可以在下面留言~~~

源码下载,请点击这里

猜你喜欢

转载自blog.csdn.net/binbinqq86/article/details/54427505