ItemDecoration是用来给RecyclerView添加分隔线的,我们可以通过自定义ItemDecoration来实现各种效果,我们先来看一下基本用法。
首先我们继承ItemDecoration,要实现三个方法
@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);
}
在RecyclerView绘制的时候,首先调用onDraw绘制分割线,然后调用自身的onDraw绘制自己(各种item),最后调用onDrawOver绘制,三个绘制用的一个Canvas,所以最后绘制的会覆盖之前的。
刚看理论肯定云里雾里,这时候就需要敲代码来加深理解和记忆了,我们要完成的效果如下
data
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
RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
@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
<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和返回改组的组名
private String getGroupName(int index) {
return "第" + (index / 7 + 1) + "组";
}
private boolean isFirstOfGroup(int index) {
return index % 7 == 0;
}
然后我们先对每一组第一个item上部添加空间来绘制
@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是组名的高度,然后我们开始绘制
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// 得到item真实的left和right(减去parent的padding)
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
for (int i = 0; i != parent.getChildCount(); i++) {
// 直接获得的child只有当前显示的,所以就算i是0的index也只是当前第一个,而不是所有第一个
View child = parent.getChildAt(i);
int index = parent.getChildAdapterPosition(child);
if (isFirstOfGroup(index)) {
// 每组第一个item都留有空间来绘制
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)。我们可以这样来做
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,最后消失。
最后看一下效果