通過一個簡單例子理解 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.pswp.cn/news/390151.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/390151.shtml
英文地址,請注明出處:http://en.pswp.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…