通过一个简单例子理解 RecyclerView.ItemDecoration

一、前言

RecyclerView 是从5.0推出的 MD 风格的控件。RecyclerView 之前有 ListView、GridView,但是功能很有限,例如 ListView 只能实现垂直方向上的滑动等。但是存在则合理,ListView 却没有被官方标记为 @Deprecated,有兴趣的同学可以去找下相关资料,主要看下 RecyclerView 和 ListView 的布局重用机制。在 ListView 文档上可以发现一句话

For a more modern, flexible, and performant approach to displaying lists, use RecyclerView

翻译为:要获得更现代、更灵活、更高效的列表显示方法,请使用 RecyclerView 就是说 RecyclerView 很牛逼

A flexible view for providing a limited window into a large data set

本文主题是 RecyclerView#ItemDecoration。Decoration:装饰,装潢;装饰品;装饰器。顾名思义就是给 Item 一些打扮的。ItemDecoration 允许应用程序从适配器的数据集中为特定的 ItemViews 添加特殊的图形和布局偏移量。这对于在 Item 之间绘制分隔线,突出显示,分组等等非常有用。 下面进入主题。

二、效果

看图

描述:RecyclerView 最上面有一个块红色的条,滚动是红色条也跟着向上滚;除了最后一个每个 Item 都有一条分割线,并且分割线距离左边有一定的距离;前个 Item 右边有一个图标。

三、实现步骤

ItemDecoration 是一个抽象类一共有6个方法,其中三个标记为 @Deprecated, 所以真正用的方法是以下三个:

  1. getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State)
  2. onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State)
  3. onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State)

1、方法介绍

按照执行顺序先后: 1. getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) 为特定的 ItemView 设置偏移量,此方法在 RecyclerView 测量 ItemView 后执行。 参数说明: 1)outRect:ItemView 边界,可用理解为原来 ItemView padding。 例如:outRect.set(50, 50, 50, 50),参数顺序为 “左上右下”,原来的 ItemView 上下左右都会扩展 50 像素,如下图

 2)view:RecyclerView 的 ItemView(将被装饰的View),outRect.set() 设置的边界针对的是这个 View

 3)parent:RecyclerView

 4)state:当前 RecyclerView 的状态

2. onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) 此方法在 RecyclerView 的 onDraw(Canvas c) 方法中调用,在 ItemView 下层绘制内容,绘制的内容可能会被 ItemView 遮挡住 1)c:画布,和自定义 View 那样把内容绘制在画布上。 如图:假设只有一个 ItemView, 红色区域是绘制的内容,大小是 100x100 像素从顶点开始绘制 c.drawRect(Rect(0, 0, 100, 100), mPaint),在 getItemOffsets 设置 outRect.set(50, 50, 0, 0)

3. onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) 此方法在 RecyclerView 的 draw(Canvas c) 方法中调用和 onDraw(Canvas c) 一样,区别在于此方法绘制的内容有可能会覆盖 ItemView。 还是上面的例子,如果 c.drawRect(Rect(0, 0, 100, 100), mPaint) 放在 onDrawOver() 效果如下图:

ItemView 的三个方法就简单讲到这里,下面上代码。

2、分割线代码

新建一个类 ItemLineDivider.kt, 贴出部分代码

class ItemLineDivider(@RecyclerView.Orientation var orientation: Int = VERTICAL) : RecyclerView.ItemDecoration() {//边界private val mBounds: Rect = Rect()private val mPaint = Paint()@ColorIntvar dividerColor: Int = Color.GRAYset(value) {mPaint.color = value}private val defaultSize = 1//默认1像素var hasEndDivider = true//是否要最后一个item的分割线var dividerWidth = defaultSize//竖线宽度,单位pxvar dividerHeight = defaultSize//横线高度,单位px/**分割线左边间距*/var leftSpace: Int = 0/**分割线右边间距*/var topSpace: Int = 0/**分割线上方间距*/var rightSpace: Int = 0/**分割线下方间距*/var bottomSpace: Int = 0init {mPaint.color = dividerColormPaint.isAntiAlias = true}/*** 分割线绘制在ItemView 的下层,* 如果 getItemOffsets 中 outRect 四个参数都是 0, 则 ItemView 有背景的情况会把分割线遮挡*/override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {super.onDraw(c, parent, state)i("onDraw")if (orientation == VERTICAL) {drawVertical(c, parent)} else {drawHorizontal(c, parent)}}/*** 为分割线腾出位置* [outRect] ItemView 边距*/override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {super.getItemOffsets(outRect, view, parent, state)i("getItemOffsets")if (orientation == VERTICAL) {outRect.set(0, 0, 0, dividerHeight)} else {outRect.set(0, 0, dividerWidth, 0)}}/*** 绘制水平分割线*/private fun drawVertical(c: Canvas, parent: RecyclerView) {c.save()val left: Intval right: Intif (parent.clipToPadding) {left = parent.paddingLeft + leftSpace //左边坐标right = if (dividerWidth != defaultSize) {//右边坐标left + dividerWidth//设置宽度,以设置的宽度优先} else {parent.width - parent.paddingEnd - rightSpace}c.clipRect(left, parent.paddingTop, right, parent.height - parent.paddingBottom)} else {left = leftSpaceright = if (dividerWidth != defaultSize) {left + dividerWidth} else {parent.width - rightSpace}}var childCount = parent.childCountif (!hasEndDivider) {//最后一个 Item 不绘制分割线childCount -= 1}for (i in 0 until childCount) {val child = parent.getChildAt(i)parent.getDecoratedBoundsWithMargins(child, mBounds)val bottom: Int = mBounds.bottom + Math.round(child.translationY)val top: Int = bottom - dividerHeightval rect = Rect(left, top, right, bottom)c.drawRect(rect, mPaint)}c.restore()}}
复制代码

3、顶部条块代码

新建一个类 VerticalItemStartLine.kt

class VerticalItemStartLine : RecyclerView.ItemDecoration() {private val mBound = Rect()private val mPaint = Paint()private val defaultSize = 1var lineWidth = defaultSizevar lineHeight = defaultSize@ColorIntvar color = Color.GRAYset(value) {mPaint.color = value}override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {drawVertical(c, parent)}private fun drawVertical(c: Canvas, parent: RecyclerView) {c.save()val child = parent.getChildAt(0)val childIndex = parent.getChildAdapterPosition(child)if (childIndex == 0) {parent.getDecoratedBoundsWithMargins(parent.getChildAt(0), mBound)val left = mBound.leftval right = if (lineWidth == defaultSize) {parent.width} else {lineWidth}val top = mBound.topval bottom = lineHeight + topc.drawRect(Rect(left, top, right, bottom), mPaint)i(mBound.toShortString() + "\nleft=$left, top=$top, right=$right, bottom=$bottom")}c.restore()}override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {if (parent.getChildAdapterPosition(view) == 0) {//只在第一个头上添加outRect.set(0, lineHeight, 0, 0)}}}
复制代码

4、右边标签代码

新建一个类 TopThreeItemDrawOver.kt


class TopThreeItemDrawOver(val drawable: Drawable) : RecyclerView.ItemDecoration() {private val width = 100private val height = 100override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {i(drawable.bounds.toShortString())for (i in 0..2) {//把 drawable 画到前三个 itemView 上val child = parent.getChildAt(i)val index = parent.getChildAdapterPosition(child)val left = parent.width - 50 - widthval right = left + widthval space = (child.height - height) / 2val top = child.top + spaceval bottom = child.bottom - spaceif (index < 3) {drawable.setBounds(left, top, right, bottom)drawable.draw(c)}}}}
复制代码

5、把上面三个 ItemDecoration 添加到 RecyclerView

private fun init() {val myAdapter = MyAdapter(this, getData())val layoutManager = LinearLayoutManager(this)val itemDecoration = ItemLineDivider(RecyclerView.VERTICAL)itemDecoration.apply {dividerHeight = 5leftSpace = 140hasEndDivider = false}val startItemDecoration = VerticalItemStartLine()startItemDecoration.apply {lineHeight = 100color = Color.RED}val drawOver = TopThreeItemDrawOver(resources.getDrawable(R.drawable.ic_swap_horiz))recycler_view.apply {addItemDecoration(startItemDecoration)//头部条块addItemDecoration(itemDecoration)//分割线addItemDecoration(drawOver)//右边标签setHasFixedSize(true)setLayoutManager(layoutManager)adapter = myAdapter}myAdapter.notifyDataSetChanged()}
复制代码

四、总结

通过一个简单的例子,可以很好的理解高大上的 ItemDecoration,什么分割线啊也不用在 xml 布局文件里设置了。ItemDecoration 还可以实现时间轴、黏附等效果,这里就不举例了,根据上面的方法解析和例子,再加上自己的想法,可以在 RecyclerView 实现很多效果。我觉得刚开始的话重点去理解 Rect,坐标,偏移量,就可以很好把一个内容绘制到指定位置了。

参考: RecyclerView 文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390151.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Entity Framework Logging and Intercepting Database Operations (EF6 Onwards)

参考官方文档&#xff1a;https://msdn.microsoft.com/en-us/library/dn469464(vvs.113).aspx转载于:https://www.cnblogs.com/liandy0906/p/8473110.html

面试题 17.14. 最小K个数

面试题 17.14. 最小K个数 设计一个算法&#xff0c;找出数组中最小的k个数。以任意顺序返回这k个数均可。 示例&#xff1a; 输入&#xff1a; arr [1,3,5,7,2,4,6,8], k 4 输出&#xff1a; [1,2,3,4] 提示&#xff1a; 0 < len(arr) < 1000000 < k < min(1…

这是您现在可以免费获得的115张Coursera证书(在冠状病毒大流行期间)

At the end of March, the world’s largest Massive Open Online Course provider Coursera announced that they are offering 100 free courses in response to the impact of the COVID-19 pandemic. 3月底&#xff0c;全球最大的大规模在线公开课程提供商Coursera 宣布 &a…

由浅入深理解----java反射技术

java反射机制详解 java反射机制是在运行状态下&#xff0c;对任意一个类可以获取该类的属性和方法&#xff0c;对任意一个对象可以调用其属性和方法。这种动态的获取信息和调用对象的方法的功能称为java的反射机制 class<?>类&#xff0c;在java.lang包下面&#xff0c;…

【VMware vSAN 6.6】5.5.Update Manager:vSAN硬件服务器解决方案

目录 1. 简介 1.1.适用于HCI的企业级存储2. 体系结构 2.1.带有本地存储的服务器2.2.存储控制器虚拟系统套装的缺点2.3.vSAN在vSphere Hypervisor中自带2.4.集群类型2.5.硬件部署选项3. 启用vSAN 3.1.启用vSAN3.2.轻松安装3.3.主动测试4. 可用性 4.1.对象和组件安置4.2.重新构建…

5848. 树上的操作

给你一棵 n 个节点的树&#xff0c;编号从 0 到 n - 1 &#xff0c;以父节点数组 parent 的形式给出&#xff0c;其中 parent[i] 是第 i 个节点的父节点。树的根节点为 0 号节点&#xff0c;所以 parent[0] -1 &#xff0c;因为它没有父节点。你想要设计一个数据结构实现树里面…

了解如何通过Python使用SQLite数据库

SQLite is a very easy to use database engine included with Python. SQLite is open source and is a great database for smaller projects, hobby projects, or testing and development.SQLite是Python附带的非常易于使用的数据库引擎。 SQLite是开源的&#xff0c;是用于…

32位JDK和64位JDK

32位和64位系统在计算机领域中常常提及&#xff0c;但是仍然很多人不知道32位和64位的区别&#xff0c;所以本人在网上整理了一些资料&#xff0c;并希望可以与大家一起分享。对于32位和64位之分&#xff0c;本文将分别从处理器&#xff0c;操作系统&#xff0c;JVM进行讲解。 …

中小企业如何选择OA协同办公产品?最全的对比都在这里了

对于中小企业来说&#xff0c;传统的OA 产品&#xff0c;如泛微、蓝凌、致远、华天动力等存在价格高、使用成本高、二次开发难等特点&#xff0c;并不适合企业的协同管理。 国内OA市场也出现了一批轻便、低价的OA产品&#xff0c;本文针对以下几款适合中小企业的OA产品在功能、…

python缓冲区_如何在Python中使用Google的协议缓冲区

python缓冲区When people who speak different languages get together and talk, they try to use a language that everyone in the group understands. 当说不同语言的人聚在一起聊天时&#xff0c;他们会尝试使用小组中每个人都能理解的语言。 To achieve this, everyone …

PowerDesigner16中的对象无效,不允许有扩展属性 问题的解决

PowerDesigner16中的对象无效&#xff0c;不允许有扩展属性 消息 15135&#xff0c;级别 16&#xff0c;状态 1&#xff0c;过程 sp_addextendedproperty&#xff0c;第 37 行 对象无效。XXXXXXX 不允许有扩展属性&#xff0c;或对象不存在。 把 execute sp_addextendedpropert…

Elasticsearch学习(2)—— 常见术语

为什么80%的码农都做不了架构师&#xff1f;>>> cluster (集群)&#xff1a;一个或多个拥有同一个集群名称的节点组成了一个集群。每个集群都会自动选出一个主节点&#xff0c;如果该主节点故障&#xff0c;则集群会自动选出新的主节点来替换故障节点。 node (节点…

67. 二进制求和

67. 二进制求和 给你两个二进制字符串&#xff0c;返回它们的和&#xff08;用二进制表示&#xff09;。 输入为 非空 字符串且只包含数字 1 和 0。 示例 1: 输入: a “11”, b “1” 输出: “100” 示例 2: 输入: a “1010”, b “1011” 输出: “10101” 提示&…

前端开发有哪些技术栈要掌握_为什么要掌握前端开发的这四个主要概念

前端开发有哪些技术栈要掌握After working as a front-end developer for three years, I have been able to summarize what I feel are the four major concepts of front-end development. Knowing and studying these four areas will make you stand out from the crowd.在…

python中的序列化与反序列化

之前&#xff0c;在学习python时&#xff0c;一直弄不明白pickle和json模块的序列化和反序例化之间的区别和用法&#xff0c;最近闲来有时间&#xff0c;重新研究了这两个模块&#xff0c;也算是基本搞明白他们之中的区别了。 用于序列化的两个模块&#xff0c; json&#xff0…

1114. 按序打印

1114. 按序打印 我们提供了一个类&#xff1a; public class Foo { public void first() { print(“first”); } public void second() { print(“second”); } public void third() { print(“third”); } } 三个不同的线程 A、B、C 将会共用一个 Foo 实例。 一个将会调用 …

2018年应用交付控制器市场将发生重大变化

应用交付控制器&#xff08;ADC&#xff09;一直以来都是基础设施的关键部分。它们位于应用程序和基础架构之间&#xff0c;是唯一可以同时使用应用程序和网络语言的技术。IT行业正在经历一个快速的现代化进程&#xff0c;包含诸如软件定义的网络、云、容器等其他计划对基础设施…

如何测试一个水杯

关于一个水杯如何测试&#xff1f;这个被认为是测试界最为经验的面试题了&#xff0c;下面是我的回答思路&#xff1a; 对于一个软件的测试&#xff0c;重点是测试的思路以及测试的全面性的体现。 软件测试应该先重点再次重点&#xff0c;对于软件而言重点自然在于功能测试&…

1115. 交替打印FooBar

1115. 交替打印FooBar 我们提供一个类&#xff1a; class FooBar {public void foo() {for (int i 0; i < n; i) {print("foo");}}public void bar() {for (int i 0; i < n; i) {print("bar");}} }两个不同的线程将会共用一个 FooBar 实例。其中…

IntelliJ IDEA 运行 Maven 项目

1.官方文档说IntelliJ IDEA已经自身集成了maven&#xff0c;则不用劳心去下载maven 2.导入一个程序&#xff0c;看是否是maven程序的关键在于工程之中有没有pom.xml这个文件&#xff0c;比如这里 3.为这个工程配置好服务器3.1 点击“Edit Configurations”3.2 进入Run/Debug C…