RecyclerView头部分组列表

转载请注明出处: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);
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

首先就是根据当前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);
            }
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

其中用到了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();
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

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

源码下载,请点击这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值