RecyclerView分割线详解| 青训营笔记

228 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第3天

recyclerview画分割线的方式

一: 在recyclerview的item中增加分割线控件

在item的底部 增加一个控件

eg:

 <View
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:background="@android:color/black" />

二:自定义分割线

getItemOffset方法

自定义一个类 继承自RecyclerView.ItemDecoration

重写 getItemOffset方法就可以实现分割线

@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    super.getItemOffsets(outRect, view, parent, state);
}

其中重要的参数 是outRect

同过这个参数 我们可以设置recycleview 每个item间距,以此来实现分割线的效果

默认情况下 outRect的四个参数(left,right,top,bottom)均为 0,也就是不偏移。

要想设置偏移的话只需要调用

outRect.set(left,top,right,bottom);

这个矩形的四个参数分别代表这当前item偏移的方向和距离

一图胜千言好吧:(虽然有点抽象)

image-20220729105408256

如图为设置top为80的效果

image-20220729105829961

微信的通讯录就是这种上部偏移的效果哦

还有一个view参数

这个view返回的是 滑动之后现的那一个item,什么意思呢? 就是在滑动的时候即将出现的那一个item

除此之外,这个类能做的事情还有很多,不知道你有没有注意到,我们还有两个方法可以重写

接下来就来介绍

onDraw 和onDrawOver 方法

重写这两个方法,我们要注意 他们调用(绘制)的顺序

onDraw---> recyclerView的item --->onDrawOver

所以在onDraw方法绘制的内容会被recyclerView 的item 盖住

在onDarwOver 方法中绘制的内容会盖住recyclerview的 item

有一种情况特殊情况 :在item与item的间隔区域画内容

如图所示

对于这种情况,由于中间层的recyclerView没有遮挡效果,在 onDraw 和OnDrawOver方法中绘制效果一致

image-20220729111357192

对分割线进行定制

我们要想对分割线进行自我定制,其实就是找到具体每一个分割线(区域)的位置坐标,然后再onDraw或onDrawOver方法进行绘制(为什么两个方法都可以上文有讲解)

但是还有一个问题:我们绘制的内容不会和item一起移动怎么办?

我们对每一个方法都打印一下log,然后滑动屏幕

D/decoration: onDraw: 
D/decoration: onDrawOver: 
D/decoration: onDraw: 
D/decoration: onDrawOver:
.....

可以发现 onDraw 和 onDrawOver 方法是会 被不断调用的 (也就是说我们不用像自定义view那样手动调用invalidate方法),直接动态设置 draw方法里的坐标就可以了

获取动态坐标

注意到我们的两个方法中都有一个parent参数

通过这个参数,我们就可以拿到我们想要的位置和坐标信息

  • parent.getChildCount()

    此方法用于获取页面上能够显示的所有子view

    注意 并不是所有子 item

  • parent.getChildAdapterPosition(child)

    返回某一个子view的位置(position)

  • parent.getDecoratedBoundsWithMargins(child,mBounds)

    mBounds 是一个矩形的实例对象

    调用这样方法后会将 child ,这个view(包括marjin在内)的坐标信息赋值给mBounds

现在,获取到来了 item 的 position(可以根据数据源获取数据)和坐标信息(可以根据Draw 我们想要的)

for (int i = 0; i < parent.getChildCount(); i++) {
    final View child=parent.getChildAt(i);
  /*  final RecyclerView.LayoutParams params= (RecyclerView.LayoutParams) child.getLayoutParams();
    int position =params.getViewLayoutPosition();*/
    //此方法也是获取position信息,任选其一即可
    int position=parent.getChildAdapterPosition(child);
    parent.getDecoratedBoundsWithMargins(child,mBounds);
​
    if (position<5){
        c.drawText("xiao long"+position,0,mBounds.top+60,paint);
    }
    else {
        c.drawText("xiao long666"+position,0,mBounds.top+60,paint);
    }
}

最后在activity中

recyclerView.addItemDecoration(new MyItemDecoration(this));

当然如果你不想写这些麻烦的代码

你可以直接给RecyclerView的跟布局设置背景 ,这样分割线也自动被填充了

比如我们准备一张美女图片👧

image-20220729144157338

除此之外我们还可以对recyclerview添加标签

image-20220729145906326

下面贴部分关键代码

/**
 * @author :yinxiaolong
 * @describe :reycylerview分割线
 * @data:2022/7/29
 */
public class MyItemDecoration extends RecyclerView.ItemDecoration {
​
    private final Paint TextPaint =new Paint();
    private final Paint paint =new Paint();
​
  
    public MyItemDecoration(Context context){
        TextPaint.setColor(Color.parseColor("#a33656"));
        paint.setStyle(Paint.Style.STROKE);
    }
    private final Rect mBounds = new Rect();
    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(c, parent, state);
​
        Log.d("decoration", "onDraw: ");
​
        TextPaint.setTextSize(50);
        for (int i = 0; i < parent.getChildCount(); i++) {
            final View child=parent.getChildAt(i);
           /* final RecyclerView.LayoutParams params= (RecyclerView.LayoutParams) child.getLayoutParams();
            int position =params.getViewLayoutPosition();*/
            int position=parent.getChildAdapterPosition(child);
            parent.getDecoratedBoundsWithMargins(child,mBounds);
​
            if (position<5){
                c.drawText("xiao long"+position,0,mBounds.top+60, TextPaint);
            }
            else {
                c.drawText(position+"xiao long",0,mBounds.top+60, TextPaint);
            }
        }
      
    }
​
    //画标签
    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
​
        for (int i = 0; i < parent.getChildCount(); i++) {
            final View child=parent.getChildAt(i);
           /* final RecyclerView.LayoutParams params= (RecyclerView.LayoutParams) child.getLayoutParams();
            int position =params.getViewLayoutPosition();*/
            int position=parent.getChildAdapterPosition(child);
            parent.getDecoratedBoundsWithMargins(child,mBounds);
​
​
            c.drawRect(mBounds.right-110,mBounds.top+90,mBounds.right-5,mBounds.top+160, paint);
            if (position%2==0){
                c.drawText("大佬",mBounds.right-110,mBounds.top+150, TextPaint);
            }else {
                c.drawText("菜鸡",mBounds.right-110,mBounds.top+150, TextPaint);
            }
​
        }
​
        Log.d("decoration", "onDrawOver: ");
​
    }
​
    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        outRect.set(0,80,0,0);
    }
}

这里adapter 使用了DataBinding

Android dataBinding入门| 青训营笔记 - 掘金 (juejin.cn)

/**
 * @author yinxiaolong
 * @describe :recyclerviewAdapter
 * @data: 2022/7/29
 */
public class recyclerviewAdapter extends RecyclerView.Adapter<recyclerviewAdapter.MyViewHolder> {
​
​
    private List<String> lists;
    public recyclerviewAdapter(List<String> list) {
        this.lists=list;
    }
​
    @NonNull
    @Override
    public recyclerviewAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ActivityRecycylerviewItemBinding binding= DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.activity_recycylerview_item,parent,false);
​
        return new MyViewHolder(binding);
    }
​
    @Override
    public void onBindViewHolder(@NonNull recyclerviewAdapter.MyViewHolder holder, int position) {
        String s=lists.get(position);
        holder.binding.imageViewItem.setImageResource(R.drawable.ic_launcher_foreground);
        holder.getBinding().setString(s);//数据绑定
    }
​
    @Override
    public int getItemCount() {
        return lists.size();
    }
​
    public class MyViewHolder extends RecyclerView.ViewHolder {
        ActivityRecycylerviewItemBinding binding;
        public MyViewHolder(ActivityRecycylerviewItemBinding binding) {
            super(binding.getRoot());
            this.binding=binding;
        }
​
        public ActivityRecycylerviewItemBinding getBinding(){
            return binding;
        }
    }
}

Activity的代码

public class recyclerviewActivity extends AppCompatActivity {
​
    private ActivityRecyclerviewBinding binding;
    private List<String> list=new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding= DataBindingUtil.setContentView(this,R.layout.activity_recyclerview);
        LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
        binding.recyclerView.setLayoutManager(linearLayoutManager);
        binding.recyclerView.addItemDecoration(new MyItemDecoration(this));//关键
        for (int i = 0; i < 10; i++) {
            list.add("xiao long 666");
        }
        recyclerviewAdapter adapter=new recyclerviewAdapter(list);
        binding.recyclerView.setAdapter(adapter);
    }
}

写在最后

这里只写了linearLayoutManager的recyclerview,还有很多 布局有不同的方法需要进一步的学习

制造分割线的方法也不仅限于这两种,这只是两种可行的解决方法