RecyclerView是很强大的控件,基本可以替代ListView和GridView。但是RecyclerView没有封装一些listview的功能,例如分割线,item点击事件等等,需要自己实现。item点击事件在ViewHolder中设置Click事件就行了。实现分割线功能,则需要使用addItemDecoration添加一个自定义的分割线。

我使用的RecyclerView的版本是26.1.0,以下都是基于这个版本来讲的,先看一下ItemDecoration(item 装饰器)这个类的注释及方法。

/*** An ItemDecoration allows the application to add a special drawing and layout offset* to specific item views from the adapter's data set. This can be useful for drawing dividers* between items, highlights, visual grouping boundaries and more.** <p>All ItemDecorations are drawn in the order they were added, before the item* views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}* and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,* RecyclerView.State)}.</p>*/public abstract static class ItemDecoration {/*** Draw any appropriate decorations into the Canvas supplied to the RecyclerView.* Any content drawn by this method will be drawn before the item views are drawn,* and will thus appear underneath the views.** @param c Canvas to draw into* @param parent RecyclerView this ItemDecoration is drawing into* @param state The current state of RecyclerView*/public void onDraw(Canvas c, RecyclerView parent, State state) {onDraw(c, parent);}/*** @deprecated* Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}*/@Deprecatedpublic void onDraw(Canvas c, RecyclerView parent) {}/*** Draw any appropriate decorations into the Canvas supplied to the RecyclerView.* Any content drawn by this method will be drawn after the item views are drawn* and will thus appear over the views.** @param c Canvas to draw into* @param parent RecyclerView this ItemDecoration is drawing into* @param state The current state of RecyclerView.*/public void onDrawOver(Canvas c, RecyclerView parent, State state) {onDrawOver(c, parent);}/*** @deprecated* Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}*/@Deprecatedpublic void onDrawOver(Canvas c, RecyclerView parent) {}/*** @deprecated* Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}*/@Deprecatedpublic void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {outRect.set(0, 0, 0, 0);}/*** Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies* the number of pixels that the item view should be inset by, similar to padding or margin.* The default implementation sets the bounds of outRect to 0 and returns.** <p>* If this ItemDecoration does not affect the positioning of item views, it should set* all four fields of <code>outRect</code> (left, top, right, bottom) to zero* before returning.** <p>* If you need to access Adapter for additional data, you can call* {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the* View.** @param outRect Rect to receive the output.* @param view    The child view to decorate* @param parent  RecyclerView this ItemDecoration is decorating* @param state   The current state of RecyclerView.*/public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),parent);}}

注释的大致意思:Item装饰器可以用来给item添加特殊的绘制图像和布局偏移(也就是padding)。可以实现分割线,高亮,视觉分组等等效果。3个方法的注释(3个过时方法对应3个不过时的方法不再讨论):

  1. onDraw:可以使用Canvas绘制各种装饰,在绘制视图之前触发,绘制的内容显示在视图下方(相当于背景)。
  2. onDrawOver:可以使用Canvas绘制各种装饰,在绘制视图之后触发,绘制的内容显示在视图上方。
  3. getItemOffsets:给outRect指定上下左右的边距大小,不指定默认为0,设置的大小就是item四周的padding。可以使用getChildAdapterPosition(View)来获取当前item的position。

关于onDraw,onDrawOver的使用可以参考hongyang大神的文章,这里主要讨论getItemOffsets设置边距。一般来说,设置分割线有以下几种,

  1. 左右相等,上下相等,每个item都设置的情况,不会导致item宽高不同,直接使用outRect.set(voffset, hoffset, voffset, hoffset)设置就可以了。
  2. 左右不同,上下不同,每个item都设置的情况,这时候也不会导致item的宽高不同,使用outRect.set(left, top, right, bottom)设置。
  3. 两边没有分割线中间有。这种情况常见于纵向的RecyclerView(GridLayoutManager,StaggeredGridLayoutManager),一般设置方式是:判断如果是第一列只设置右侧的间距,最后一列只设置左侧的间距,中间的设置左右两侧的间距。效果看上去是没问题的,但是实际上左右的两个item比较宽,宽了半个间距。
  4. 上下没有中间有,类似于3,上下两个item高了一个间距。
  5. 特殊的一种情况,左右或上下的分割线和中间的分割线相同。

先看正常的实现方式,代码如下

/*** 普通设置间距(纵向)*/public class NormalItemDecoration extends RecyclerView.ItemDecoration {private int spanCount; // 每行个数private int decoration; // 间距private boolean includeEdge; // 是否需要左右,上下分割线public NormalItemDecoration(int spanCount, int decoration, boolean includeEdge) {this.spanCount = spanCount;this.decoration = decoration;this.includeEdge = includeEdge;}@Overridepublic void getItemOffsets(@NonNull Rect outRect, @NonNull View view,@NonNull RecyclerView parent, @NonNull RecyclerView.State state) {int position = parent.getChildAdapterPosition(view);int column = position % spanCount;// 计算这个child 处于第几列if (column % spanCount == 0) {// 第一列if (includeEdge) {outRect.left = decoration;} else {outRect.left = 0;}outRect.right = decoration / 2;} else if ((column + 1) % spanCount == 0) {// 最后一列outRect.left = decoration / 2;if (includeEdge) {outRect.right = decoration;} else {outRect.right = 0;}} else {outRect.left = decoration / 2;outRect.right = decoration / 2;}if (includeEdge) {if (position < spanCount) { // 第一行设置topoutRect.top = decoration;}outRect.bottom = decoration; // 设置bottom} else {if (position >= spanCount) {outRect.top = decoration; // 非第一行设置top}}}}

看一下效果:无四周分割线时,左右的item会比较大,大了半个间隔。

有四周分割线时,左右的item比较小,小了一半的间隔。

当间距比较小时,肉眼看不出来,但是设置的间距比较大时就比较明显了。导致这个的原因就是间距就相当于padding,设置的padding不同,item的宽高就会不同。那么怎么解决呢,StackOverflow有这个解决方案,链接https://stackoverflow.com/a/30701422/4696538。以GridLayoutManager为例:间距设置为5dp,每行有4列,左右没有分割线,中间有分割线。第一列:左侧为0,右侧为4dp;第二列:左侧为1dp,右侧为3dp;第三列:左侧为2dp,右侧为2dp;第三列:左侧为3dp,右侧为1dp;第4列:左侧为4dp,右侧为0;这样就是分割线为5dp,而且宽度都相同。虽然如此,按照StackOverflow的解决方案来写的话也是会有一点儿小问题,因为间隔除以spanCount如果不是整数,可能导致item相差一两个像素。如下图

当然一两个像素,问题不算大,但是也可以解决,先计算出每个总的左右间距,每次只计算左边间距,右边间距则用总间距减去左侧获得,代码如下:

/*** 校准过的设置间距(纵向)*/public class GridItemDecoration extends RecyclerView.ItemDecoration {private int spanCount; // 每行个数private int decoration; // 间距private boolean includeEdge; // 是否需要左右,上下分割线private int total; // item总共的间隔private int[] left; // left 数组private int[] right; // right 数组public GridItemDecoration(int spanCount, int decoration, boolean includeEdge) {this.spanCount = spanCount;this.decoration = decoration;this.includeEdge = includeEdge;if (includeEdge) {total = decoration + Math.round(decoration * 1f / spanCount);} else {total = Math.round(decoration * (spanCount - 1) * 1f / spanCount);}left = new int[spanCount];right = new int[spanCount];for (int i = 0; i < spanCount; i++) {if (i == 0) {if (includeEdge) {left[i] = decoration;} else {left[i] = 0;}} else {left[i] = decoration - (total - left[i - 1]); // 后一列的left = decoration - (total - 上一列的left)}right[i] = total - left[i];}}@Overridepublic void getItemOffsets(@NonNull Rect outRect, @NonNull View view,@NonNull RecyclerView parent, @NonNull RecyclerView.State state) {int position = parent.getChildAdapterPosition(view);int column = position % spanCount;// 计算这个child 处于第几列outRect.left = left[column];outRect.right = right[column];if (includeEdge) {if (position < spanCount) { // 第一行设置topoutRect.top = decoration;}outRect.bottom = decoration; // 设置bottom} else {if (position >= spanCount) {outRect.top = decoration; // 非第一行设置top}}}}

就是以加减代替除法,消除可能的精度问题。

效果如下

最后:纵向时top和bottom不会影响控件的高度。同理横向时就需要动态设置top和bottom。

正确使用RecyclerView分割线相关推荐

  1. RecyclerView分割线的技巧

    RecyclerView分割线的技巧 真的很简单,因为方法别人都已经写好了,不多说了还是看源码: package com.xiayiye.yhsh.recyclerviewdemo;import an ...

  2. 万能RecyclerView分割线扩展

    该万能分割线参考自博客:RecyclerView的万能分割线_pengkv的博客-CSDN博客_android recyclerview 分割线 在他的基础上添加了距离左右边距的属性. recycle ...

  3. RecyclerView分割线

    闲来无事,把自己弄的一个RecyclerView分割线,整理一下,贴上来,当做笔记,方便自己以后查看. 使用方法: 一.添加默认分割线:默认纵向布局.高度为2.灰色, rv.addItemDecora ...

  4. Android零基础入门第65节:RecyclerView分割线开发技巧

    2019独角兽企业重金招聘Python工程师标准>>> 在上一期通过简单学习,已经领略到了RecyclerView的灵活性,当然都是一些最基础的用法,那么本期一起来学习Recycle ...

  5. Android 活用RecyclerView分割线

    1.ItemDecoration简介 Recyclerview是我们日常开发中使用频率比较高的的控件,而其中的ItemDecoration作为布局装饰又能很方便的帮助我们定义分割线,列表排行效果以及设 ...

  6. RecyclerView --- 分割线

    [记录]记录点滴 [需求]简单使用分割线与自定义分割线 1. 利用DividerItemDecoration, 简单实现分割线 2. 基于RecyclerView.ItemDecoration,自定义 ...

  7. RecyclerView 分割线和 Item默认增删动画

    虽然RecyclerView出现已经有一段时间了,但是还是想要自己总结一下,总的来说其基本使用方法: 你想要控制其显示的方式,请通过布局管理器LayoutManager; 你想要控制Item间的间隔( ...

  8. Android零基础入门第68节:完善RecyclerView,添加首尾视图

    2019独角兽企业重金招聘Python工程师标准>>> 在之前学习ListView的时候,有学习过如何给ListView添加列表头和列表尾.但是通过近几期的学习,发现Recycler ...

  9. android 表格控件点击事件,Android零基础入门|RecyclerView点击事件处理

    原标题:Android零基础入门|RecyclerView点击事件处理 前面两期学习了RecyclerView的简单使用,并为其item添加了分割线.在实际运用中,无论是List还是Grid效果,基本 ...

  10. RecyclerView加载了那么多图,为什么就是不崩呢?

    /   今日科技快讯   / 近日,有媒体近期透露,京东将在香港进行二次上市,计划于5月底开始新股申购.在今年4月底,有香港媒体援引路透社报道称,京东已以保密形式在香港提交上市申请,拟二次上市.京东可 ...

最新文章

  1. unix 查询进程并中止
  2. Redis数据结构之字符串
  3. JQuery DOM基本操作
  4. SendMessage CString
  5. 前端每日实战:114# 视频演示如何用纯 CSS 和混色模式创作一个 loader 动画
  6. python3能做什么_你都用 Python 来做什么?
  7. Dubbo的基本介绍和搭建一个Dubbo环境
  8. Java学生实训平台_基于jsp的学生实训平台-JavaEE实现学生实训平台 - java项目源码...
  9. 从yield 到yield from再到python协程
  10. java第一次作业0
  11. 2019.03.30 图解HTTP
  12. 小米盒子 计算机共享,教你用局域网共享文件为小米盒子装软件
  13. java多线程编程详细入门教程
  14. 计算机键盘盲打方法,电脑键盘盲打练习方法 盲打键盘指法练习技巧
  15. 51单片机开发板独立按键试水
  16. 华盛顿大学计算机科学,华盛顿大学UW(University of Washington)计算机科学Computer Science专业排名第21位(2021年THE世界大学商科排名)...
  17. winpe读取linux硬盘数据恢复,如何在WinPE环境下完成文件恢复
  18. 如何将word文件的大小进行压缩?
  19. make config解惑
  20. 视频直播声音不清晰的解决办法(小蜜蜂无线麦克风使用方式)

热门文章

  1. 【解决方案】windows7无法启动,报0xc000014c错误,系统注册表丢失或损坏
  2. Windows 使用 ssh 命令行 通过密钥连接到 云服务器
  3. 唯品会收购第三方支付牌照正式落槌 浙江贝付完成更名
  4. Arduino - 看门狗定时器的使用介绍
  5. android微信摇一摇(抽奖)
  6. https 配置自建ca
  7. Java课程设计题目七:魔板游戏
  8. Python_day19--HTML基础--文本标签、超链接标签、图片标签
  9. 设计模式-02原型模式
  10. 外卖CPS小程序外卖分销外卖推广系统外卖侠外卖探淘客源码