正确使用RecyclerView分割线
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个不过时的方法不再讨论):
- onDraw:可以使用Canvas绘制各种装饰,在绘制视图之前触发,绘制的内容显示在视图下方(相当于背景)。
- onDrawOver:可以使用Canvas绘制各种装饰,在绘制视图之后触发,绘制的内容显示在视图上方。
- getItemOffsets:给outRect指定上下左右的边距大小,不指定默认为0,设置的大小就是item四周的padding。可以使用getChildAdapterPosition(View)来获取当前item的position。
关于onDraw,onDrawOver的使用可以参考hongyang大神的文章,这里主要讨论getItemOffsets设置边距。一般来说,设置分割线有以下几种,
- 左右相等,上下相等,每个item都设置的情况,不会导致item宽高不同,直接使用outRect.set(voffset, hoffset, voffset, hoffset)设置就可以了。
- 左右不同,上下不同,每个item都设置的情况,这时候也不会导致item的宽高不同,使用outRect.set(left, top, right, bottom)设置。
- 两边没有分割线中间有。这种情况常见于纵向的RecyclerView(GridLayoutManager,StaggeredGridLayoutManager),一般设置方式是:判断如果是第一列只设置右侧的间距,最后一列只设置左侧的间距,中间的设置左右两侧的间距。效果看上去是没问题的,但是实际上左右的两个item比较宽,宽了半个间距。
- 上下没有中间有,类似于3,上下两个item高了一个间距。
- 特殊的一种情况,左右或上下的分割线和中间的分割线相同。
先看正常的实现方式,代码如下
/*** 普通设置间距(纵向)*/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分割线相关推荐
- RecyclerView分割线的技巧
RecyclerView分割线的技巧 真的很简单,因为方法别人都已经写好了,不多说了还是看源码: package com.xiayiye.yhsh.recyclerviewdemo;import an ...
- 万能RecyclerView分割线扩展
该万能分割线参考自博客:RecyclerView的万能分割线_pengkv的博客-CSDN博客_android recyclerview 分割线 在他的基础上添加了距离左右边距的属性. recycle ...
- RecyclerView分割线
闲来无事,把自己弄的一个RecyclerView分割线,整理一下,贴上来,当做笔记,方便自己以后查看. 使用方法: 一.添加默认分割线:默认纵向布局.高度为2.灰色, rv.addItemDecora ...
- Android零基础入门第65节:RecyclerView分割线开发技巧
2019独角兽企业重金招聘Python工程师标准>>> 在上一期通过简单学习,已经领略到了RecyclerView的灵活性,当然都是一些最基础的用法,那么本期一起来学习Recycle ...
- Android 活用RecyclerView分割线
1.ItemDecoration简介 Recyclerview是我们日常开发中使用频率比较高的的控件,而其中的ItemDecoration作为布局装饰又能很方便的帮助我们定义分割线,列表排行效果以及设 ...
- RecyclerView --- 分割线
[记录]记录点滴 [需求]简单使用分割线与自定义分割线 1. 利用DividerItemDecoration, 简单实现分割线 2. 基于RecyclerView.ItemDecoration,自定义 ...
- RecyclerView 分割线和 Item默认增删动画
虽然RecyclerView出现已经有一段时间了,但是还是想要自己总结一下,总的来说其基本使用方法: 你想要控制其显示的方式,请通过布局管理器LayoutManager; 你想要控制Item间的间隔( ...
- Android零基础入门第68节:完善RecyclerView,添加首尾视图
2019独角兽企业重金招聘Python工程师标准>>> 在之前学习ListView的时候,有学习过如何给ListView添加列表头和列表尾.但是通过近几期的学习,发现Recycler ...
- android 表格控件点击事件,Android零基础入门|RecyclerView点击事件处理
原标题:Android零基础入门|RecyclerView点击事件处理 前面两期学习了RecyclerView的简单使用,并为其item添加了分割线.在实际运用中,无论是List还是Grid效果,基本 ...
- RecyclerView加载了那么多图,为什么就是不崩呢?
/ 今日科技快讯 / 近日,有媒体近期透露,京东将在香港进行二次上市,计划于5月底开始新股申购.在今年4月底,有香港媒体援引路透社报道称,京东已以保密形式在香港提交上市申请,拟二次上市.京东可 ...
最新文章
- unix 查询进程并中止
- Redis数据结构之字符串
- JQuery DOM基本操作
- SendMessage CString
- 前端每日实战:114# 视频演示如何用纯 CSS 和混色模式创作一个 loader 动画
- python3能做什么_你都用 Python 来做什么?
- Dubbo的基本介绍和搭建一个Dubbo环境
- Java学生实训平台_基于jsp的学生实训平台-JavaEE实现学生实训平台 - java项目源码...
- 从yield 到yield from再到python协程
- java第一次作业0
- 2019.03.30 图解HTTP
- 小米盒子 计算机共享,教你用局域网共享文件为小米盒子装软件
- java多线程编程详细入门教程
- 计算机键盘盲打方法,电脑键盘盲打练习方法 盲打键盘指法练习技巧
- 51单片机开发板独立按键试水
- 华盛顿大学计算机科学,华盛顿大学UW(University of Washington)计算机科学Computer Science专业排名第21位(2021年THE世界大学商科排名)...
- winpe读取linux硬盘数据恢复,如何在WinPE环境下完成文件恢复
- 如何将word文件的大小进行压缩?
- make config解惑
- 视频直播声音不清晰的解决办法(小蜜蜂无线麦克风使用方式)