ItemDecoration基本用法
ItemDecoration是用来给RecyclerView添加分隔线的,我们可以通过自定义ItemDecoration来实现各种效果,我们先来看一下基本用法。
首先我们继承ItemDecoration,要实现三个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); }
@Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDrawOver(c, parent, state); }
@Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); }
|
- getItemOffsets():使用这个方法来给其中一个item(view)添加间隔,用来绘制分割线,比如我们通过parent.getChildAdapterPosition(view)得到这个item的position,然后我们使用outRect.top = 100来使这个item的上面多出100高度的矩形空间用来绘制,对应的left之类的可以类比。
- onDraw():用来绘制分割线,一般在这个方法里遍历parent的全部child,然后得到position(注意这里的child只能得到展示在页面的,所以需要额外计算得到真实的position)根据情况看看需不需要绘制分割线。
- onDrawOver():和onDraw()很像,区别在于这个绘制的显示在最上方,原因马上说。
在RecyclerView绘制的时候,首先调用onDraw绘制分割线,然后调用自身的onDraw绘制自己(各种item),最后调用onDrawOver绘制,三个绘制用的一个Canvas,所以最后绘制的会覆盖之前的。
代码实战
刚看理论肯定云里雾里,这时候就需要敲代码来加深理解和记忆了,我们要完成的效果如下

在RecyclerView中添加数据
data
1 2 3 4
| private String[] data = {"1-你好", "1-你好", "1-你好", "1-你好", "1-你好", "1-你好", "1-你好", "2-OK", "2-OK", "2-OK", "2-OK", "2-OK", "2-OK", "2-OK", "3-你好", "3-你好", "3-你好", "3-你好", "3-你好", "3-你好", "3-你好", "4-OK", "4-OK", "4-OK", "4-OK", "4-OK", "4-OK", "4-OK",};
|
Adapter
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
| RecyclerView.Adapter<RecyclerView.ViewHolder> adapter = new RecyclerView.Adapter<RecyclerView.ViewHolder>() { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_layout, parent, false); return new MyViewHolder(v); }
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { MyViewHolder viewHolder = (MyViewHolder) holder; viewHolder.textView.setText(data[position]); }
@Override public int getItemCount() { return data.length; }
class MyViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public MyViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.text); } } };
|
item_layout
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="20dp" android:textSize="20sp"/>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="#000"/>
</LinearLayout>
|
给每一组前面添加组名
首先我们需要两个方法分别判断是不是该组第一个item和返回改组的组名
1 2 3 4 5 6 7
| private String getGroupName(int index) { return "第" + (index / 7 + 1) + "组"; }
private boolean isFirstOfGroup(int index) { return index % 7 == 0; }
|
然后我们先对每一组第一个item上部添加空间来绘制
1 2 3 4 5 6 7 8
| @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int index = parent.getChildAdapterPosition(view); if (isFirstOfGroup(index)) { outRect.top = mTopHeight; } }
|
这里mTopHeight是组名的高度,然后我们开始绘制
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
| @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight();
for (int i = 0; i != parent.getChildCount(); i++) { View child = parent.getChildAt(i); int index = parent.getChildAdapterPosition(child); if (isFirstOfGroup(index)) { int top = child.getTop() - mTopHeight; int bottom = child.getTop(); Paint paint = new Paint(); paint.setColor(Color.YELLOW); c.drawRect(left, top, right, bottom, paint); paint.setColor(Color.BLACK); paint.setTextSize(60); paint.setTextAlign(Paint.Align.LEFT); paint.setAntiAlias(true); c.drawText(getGroupName(index), left, bottom, paint); } } }
|
效果如下

添加顶部悬浮
我们先来分析一下,平常情况就是顶部绘制一个组名,我们可以通过onDrawOver来覆盖后面的内容达到悬浮的效果。当我们目前页面第二个item是下一组的第一个item的时候情况就不一样了(也就是下一个组马上就要到达顶部的时候),我们的顶部组就会向上发生偏移(像被顶上去的样子),这时候我们只需要计算绘制的bottom是多少就好了(top就是0)。我们可以这样来做
1 2 3 4 5 6 7 8
| int bottom = 0; if (isFirstOfGroup(index + 1)) { bottom = Math.min(child.getBottom(), mTopHeight); } else { bottom = mTopHeight; }
|
当下一个组马上到达顶部的时候,我们选择mTopHeight和child.getBottom()中比较小的来作为bottom,这样就可以保证,当悬浮组名bottom超过该组最后一个item的bottom的时候改为最后一个item的bottom,最后消失。
最后看一下效果
