Recycleview的itemdecoration使用

来源:互联网 发布:搜索软件有哪些 编辑:程序博客网 时间:2024/05/21 06:55

最近在做一个项目,有一个页面的功能和微信联系人列表相似,只不过没有右边的abc….。
开始的时候思路是让服务端那边帮我把数据分好组,然后包给我,然后我这边通过ExpandListView去实现,然后将分组的点击事件屏蔽掉。但是且不说服务端大哥不同意,因为他说如果他这边分类的话,要多循环3次,所以不同意,要我客户端自己去分组,无奈啊,然后就自己将请求到的数据分组,然后通过ExpandListView去实现。
最后是效果还行,有点low,而且不能实现那种组被顶上去的感觉。后来就百度啦,发现Recycleview可以定制item间隔的样式,也看了一篇文章(附上链接:http://www.jianshu.com/p/b46a4ff7c10a http://www.jianshu.com/p/3eff217839fc http://www.jianshu.com/p/e742df6f59e2)。
下面自己进行总结一下:

RecycleView可以通过addItemDecoration(ItemDecoration i);方法给item添加间隔,支持定制,因为是add,所以可以添加多个ItemDecoration。也可以使用默认的ItemDecoration,默认样式是灰色间隔。
ItemDecoration的作用:给具体的view添加具体的图画或者layout的位移,对于绘制view之间的分割线,视觉分组边界也是很有用的。
继承ItemDecoration,并实现:
onDraw(Canvas c, RecyclerView parent, RecyclerView.State state),
onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state),
getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)

onDraw():该方法会在绘制ItemView之前调用绘制itemdecoration
onDrawOver():该方法是在绘制itemview之后调用绘制itemdecoration,具体体现就是绘制的东西在itemview上面
getItemoffsets:是控制outRect的大小,就是item之间的间隔的矩形。

源码:

// 添加ItemDecoration
public void addItemDecoration(ItemDecoration decor) {
addItemDecoration(decor, -1);
}
// 添加ItemDecoration
public void addItemDecoration(ItemDecoration decor, int index) {
if (mLayout != null) {
mLayout.assertNotInLayoutOrScroll(“Cannot add item decoration during a scroll or”
+ ” layout”);
}
if (mItemDecorations.isEmpty()) {
setWillNotDraw(false);
}
if (index < 0) {
mItemDecorations.add(decor);
} else {
mItemDecorations.add(index, decor);
}
markItemDecorInsetsDirty();
requestLayout();
}

// onLayout 最终会调用到此方法Rect getItemDecorInsetsForChild(View child) {    ....    final int decorCount = mItemDecorations.size();    for (int i = 0; i < decorCount; i++) {        ...        mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);        ...    }    ...}@Overridepublic void onDraw(Canvas c) {    super.onDraw(c);    final int count = mItemDecorations.size();    for (int i = 0; i < count; i++) {        mItemDecorations.get(i).onDraw(c, this, mState);    }}@Overridepublic void draw(Canvas c) {    super.draw(c);    final int count = mItemDecorations.size();    for (int i = 0; i < count; i++) {        mItemDecorations.get(i).onDrawOver(c, this, mState);    }}

可以看出add方法调用后会按照添加的itemDecoration顺序依次调用ItemDecoration的getItemOffsets–>onDraw–>onDrawOver

用法1、给RecycleView设置边距,列举2中使用情况:

 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { //介绍3个方法 //1、可以通过下面这个方法拿到当前绘制的itemview的位置 int position = parent.getChildAdapterPosition(view); //2、可以通过下面这个方法拿到itemview的数量 int itemCount = parent.getAdapter().getItemCount(); //3、可以通过下面这个方法拿到recycleview的方向 RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if(manager instanceOf LinearLayoutManager) int orientation = ((LinearLayoutManager) layoutManager).getOrientation();//然后对position进行判断,并设置间隔大小if(position == 0){//第一个item    outRect.set(left,top,right,bottom);}else if(position == itemCount-1){//最后一个item    outRect.set(left,top,right,bottom);}else{//其他item的间隔    outRect.set(left,top,right,bottom);}

用法2、绘制分割线,其实就是画矩形,填充颜色
主要是在getitemOffsets方法中设置间隔,然后onDraw方法中计算出上下左右坐标,然后画rect。
用法3、stickHeader效果,类似微信联系人界面,上下滑动时分组会被顶上去的效果。
其实有2种header,一种是和item一个级别,类似divider一样,然后只给统一分组设置header;另一种就是处于item上面一个层级,绘制在item上面一层,然后根据滑动情况顶上去。第一种直接根据ondraw和getitemoffsets方法就可以搞定。第二种可以借助ondrawover方法实现:主要思想就是先根据分组通过onDraw方法绘制每个组的header,然后滑动的时候通过调用ondrawover方法绘制浮在上面的组。下一组的第一个item往上顶的时候,这是一个临界点:一个组的最后一个item的bottom

@Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {        if (如果是头部) {            outRect.set(0, (int) mDividerHeight, 0, 0);        }    }    @Override    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {        int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            View view = parent.getChildAt(i);            int position = parent.getChildAdapterPosition(view);            // 如果是头            if (position != RecyclerView.NO_POSITION                    && ((SectionBean) view.getTag()).isGroupStart) {                drawHeader(c, parent, view, position);            }        }    }    /**     * 画头部     *     * @param c     * @param parent     * @param view     * @param position     */    private void drawHeader(Canvas c, RecyclerView parent, View view, int position) {        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();        final int left = parent.getPaddingLeft();        final int right = parent.getWidth() - parent.getPaddingRight();        int bottoom = view.getTop() - params.topMargin - Math.round(ViewCompat.getTranslationY(view));        int top = (int) (bottoom - mDividerHeight);        // 计算文字居中时候的基线        Rect targetRect = new Rect(left, top, right, bottoom);        Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();        int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;        c.drawRect(left, top, right, bottoom, mPaint);        c.drawText(mDatas.get(position).tag, left, baseline, mTextPaint);    }

接下来处理浮在上面的header:

@Override    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {        View view = parent.getChildAt(0);        View view2 = parent.getChildAt(1);        if (view != null && view2 != null) {            SectionBean section1 = (SectionBean) view.getTag();            SectionBean section2 = (SectionBean) view2.getTag();            int position = parent.getChildAdapterPosition(view);            final int left = parent.getPaddingLeft();            final int right = parent.getWidth() - parent.getPaddingRight();            int bottoom = (int) mDividerHeight;            int top = 0;            // 判断是否达到临界点            // (第一个可见item是每组的最后一个,第二个可见tiem是下一组的第一个,并且第一个可见item的底部小于header的高度)            // 这里直接判断item的底部位置小于header的高度有点欠妥,应该还要考虑paddingtop以及margintop,这里展示不考虑了            if (section1.isGroupEnd && section2.isGroupStart && view.getBottom() <= mDividerHeight) {                bottoom = view.getBottom();                top = (int) (bottoom - mDividerHeight);            }            // 计算文字居中时候的基线            Rect targetRect = new Rect(left, top, right, bottoom);            Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();            int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;            // 背景            c.drawRect(left, top, right, bottoom, mPaint);            // 文字            c.drawText(mDatas.get(position).tag, left, baseline, mTextPaint);        }    }

这样滑动的时候就会实现上一个header被顶上去的效果。

原创粉丝点击