Android如何實現(xiàn)時間線效果
1、背景
這天下班前,老板找到小莊:“有個頁面要優(yōu)化,小需求,你跟進一下。”
小莊:“好的老板!”他看了看時間,忐忑地翻出原型,看到了這樣一個頁面:
需要優(yōu)化頁面的原型圖:
思索片刻后,小莊熟練地打開了某搜索引擎,沒有找到合適的輪子,小莊知道軟件開發(fā)的第一步必須是先進行需求分析和設計,而不是擼起袖子一把梭。于是他決定先分析下功能并整理思路。
2、分析
2.1功能分析
頁面的大致功能:
- 該頁面是個展示了某種流程的列表,每個列表項有不同的狀態(tài)(已完成、進行中、未開始)
- 在列表的一側有個類似時間線的
view
,根據每個項的狀態(tài)不同,展示不同顏色的圓點和豎線
2.2細節(jié)分析
對于其中一個項的時間線view
,有哪些細節(jié)呢?
首先發(fā)現(xiàn),這個時間線view是由兩個大部分組成的,分別是:圓、線
然后我們自然可以注意到,在一個項的時間線中,又出現(xiàn)了兩種顏色:圓上面的線(以下簡稱為上線)是綠色,圓本身和圓下面的線(以下簡稱為下線)又是紅色
- 也就是說,這個view不僅要知道自身的顏色,還得知道上一個item是什么顏色的
- 也就是說,這個view的繪制應該分成三個部分,分別是:上線、圓、下線
這是一個普通的中間的item。然而對于第一個item和最后一個item來說,它們是分別沒有上線和下線的
2.3方案設想
小莊的腦海里迅速地閃過了幾個想法:
第一個想法是根據數據的狀態(tài),在adapter中設置顏色和是否線顯示。
- 但是這么簡單的圓和線還要找設計師要圖么?這樣豈不是顯得他很菜。那要用Drawable?然而將來要改顏色什么的,也是麻煩,而且要寫好幾個文件。所以這個想法很快就被pass了
第二個想法就是使用自定義view,在每一個item中畫出圓和線,然后用自定義屬性設置顏色。
- 他馬上寫了個
demo
嘗試了一下,結果是他自定義view學藝不精,遇到了難以解決的問題[注],所以只能哭著放棄了
也許是命中注定他將推開一扇大門:旁友,也許你聽說過RecyclerView.ItemDecoration嗎?
注:2000 years later,我發(fā)現(xiàn)我根本復現(xiàn)不出來那問題,也許這就是緣分吧
RecyclerView.ItemDecoration簡介
這是一款功能強大的神器,用來給列表添加分隔線只是它最常見又最普通的能力。這里簡單介紹一下,不是本文的主要內容。因為它能實現(xiàn)的效果太多太厲害了,我學不過來(?_?)
實現(xiàn)自定義的一個ItemDecoration,需要繼承它并按需重寫以下兩個方法:
onDraw
:用于具體的繪制內容
- 方法有個參數是
parent
:RecyclerView
,即列表本身,所以我們可以從這里獲取每個子項的內容 - 要注意的是這個方法里的繪制維度是整個列表,所以我們需要遍歷列表,為每一個子項進行計算位置和繪制
getItemOffsets
:用于控制item的四周的偏移量,onDraw
中繪制的內容會在這些留白上畫出來
- 然而這個方法的繪制維度又是針對每一個itemView,所以設置的是每個item的上下左右邊距
3、編碼
小莊現(xiàn)在已經有了基本的思路和知識儲備,他打開IDE準備動手編碼了。不過軟件開發(fā)是迭代的過程,即使是這樣的一個小需求,他也打算先從實現(xiàn)一個簡單的版本開始。
3.1第一版
第一個版本,小莊打算只實現(xiàn)畫出圓和線的形狀,沒有狀態(tài)也沒有顏色,主要為了驗證自己的想法是否可行,
具體的實現(xiàn)需要做以下幾個內容:
準備定義兩個重要屬性,它們將會參與計算位置和繪制內容
radius
:用于確定圓的半徑offset
:用于表示圓點到item頂部的距離
并且在getItemOffsets
中留出繪制整個時間線的空間,即item的左邊距
最重要的工作內容是我們計算并繪制了圓和線(具體的計算可以看代碼)
class FirstVerTimeline : RecyclerView.ItemDecoration() { private val paint = Paint(Paint.ANTI_ALIAS_FLAG) var radius = 8f var offset = 15 override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { super.onDraw(c, parent, state) val count = parent.childCount for (i in 0 until count) { // 獲取當前的itemView val itemView = parent.getChildAt(i) // 整個軸線的x坐標都是相同的 val xPosition = radius // 畫上線。第一個item不畫 if (i != 0) { c.drawLine(xPosition, itemView.top.toFloat(), xPosition, itemView.top.toFloat() + offset, paint) } // 畫下線。最后一個item不畫 if (i != count - 1) { c.drawLine(xPosition, itemView.top + radius * 2 + offset, xPosition, itemView.bottom.toFloat(),paint) } // 畫圓 c.drawCircle(xPosition, itemView.top + offset + radius, radius, paint) } } override fun getItemOffsets(outRect: Rect, view: View, : RecyclerView, state: RecyclerView.State) { super.getItemOffsets(outRect, view, parent, state) // 設置item在左邊的偏移量 outRect.left = radius.toInt() * 2 } }
現(xiàn)在我們可以來定義一個虛擬的數據源Record
,把這個ItemDecoration
應用到一個RecyclerView上康康效果:
rv_timeline1.adapter = RecordAdapter(ArrayList<Record>())// 省略構造假數據 rv_timeline1.addItemDecoration(FirstVerTimeline())
已經初具規(guī)模了!只是時間線和文字之間擠了一點,我們只需要加上一些合適的padding,換一下測試數據,看起來就會像真的一樣了!
為了從圖1到達圖2,我們需要做:
定義paddingLeft和paddingRight屬性,用來表示軸線的左右padding
- 修改
getItemOffsets
為outRect.left = paddingLeft + paddingRight + radius.toInt() * 2
,留出偏移量的位置 - 修改xPosition的初始值為
radius + paddingLeft
,改變軸線的x坐標
到這里第一個版本就算完成啦,第二個版本會有什么新功能呢↓↓↓
3.2第二版
小莊打算在第二版里實現(xiàn)狀態(tài)的不同顏色。為了實現(xiàn)這個需求,他陷入了深深的沉思:
- 數據類中肯定不可能耦合顏色這種UI實現(xiàn),所以需要一個由狀態(tài)獲取顏色的辦法
- 由于畫一個
item
還需要知道上一個item的顏色,干脆直接把整個數據源列表data傳入ItemDecoration
好了 - 結合以上兩點,我們可以定義一個函數類型的屬性
var color: (item: T) -> Int
,實現(xiàn)這個屬性就可以讓使用者通過數據狀態(tài)設置想要的顏色了
函數類型是kotlin(或者說函數式編程)的特性之一。如果是Java的話可以考慮用模板模式實現(xiàn),即定義一個抽象方法讓子類去重寫
class SecondVerTimeline<T> : RecyclerView.ItemDecoration() { // 其他屬性... var data: List<T> = ArrayList() //-->這里有更新,定義了數據源 var color: (item: T) -> Int = { _ -> Color.GRAY } //-->這里有更新,通過這個屬性設置顏色選擇策略 override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { super.onDraw(c, parent, state) val count = parent.childCount for (i in 0 until count) { // ... val adapterPosition = parent.getChildAdapterPosition(itemView) //-->這里有更新,獲取當前項的真正位置 val item = data[adapterPosition] //-->這里有更新,獲取當前項的數據源 // 畫上線。第一個item不畫 if (adapterPosition != 0) { paint.color = color(data[adapterPosition - 1]) //-->這里有更新,設置上線的顏色 c.drawLine(...) } paint.color = color(item) //-->這里有更新,設置圓和下線的顏色 // 畫下線。最后一個item不畫 if (adapterPosition != data.size - 1) {//-->這里有更新,改用數據源的大小判斷是否為最后一個item c.drawLine(...) } // 畫圓... } } // getItemOffsets... }
代碼中可能需要注意的點:
- 繪制上線前,需要通過data數據源獲取到上一個item,并用color屬性獲得其狀態(tài)對應的顏色
- 繪制圓和下線前,同樣需要改變到這一個item的顏色
- 用
parent.childCount
獲取到的子項數量指的是屏幕中可見的部分,必須要用parent.getChildAdapterPosition
獲取到該項在列表中的真正位置,才能確定下線要不要畫。否則會出現(xiàn)【當前屏幕上可見的最后一項不是真正的最后一項,但它卻沒有下線,但向下滑動后它又有下線了】的尷尬場景 - 注意到此時用于判斷是否為最后一個item的方法,從count - 1變?yōu)榱薲ata.size - 1,用數據源的大小判斷,比count更加準確(原因同上一條)
使用時也需要有一些變化:
- 把
data
設置給ItemDecoration
- 通過
color
屬性設置顏色策略
val secondVerTimeline = SecondVerTimeline<Record>() secondVerTimeline.data = records secondVerTimeline.color = { item -> when (item.status) { 1 -> color1 2 -> color2 ... } } rv_timeline2.addItemDecoration(secondVerTimeline)
然后就可以運行看一下效果了:
哇哦,鵝妹子嚶,這樣就已經實現(xiàn)根據狀態(tài)轉變顏色的功能了!第二版的功能也圓滿實現(xiàn)!
4、結語
后來小莊又根據UI一頓修修改改,很快就完成了這個需求~但是小莊是一個有追求的程序員,他開始思考起了這個代碼的擴展性和通用性如何。不想不要緊,一想發(fā)現(xiàn)根本沒有鴨!如果產品想要把圓點變成圖片怎么辦?或者產品想要更隨風飛翔自由是方向呢?
于是他想找個時間完善改進一下這個ItemDecoration,最好能應對產品的所有需求!具體升級內容請看下集~
到此這篇關于 Android如何實現(xiàn)時間線效果的文章就介紹到這了,更多相關 Android實現(xiàn)時間線效果內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
ScrollView與ListView合用(正確計算Listview的高度)的問題解決
最近做項目中用到ScrollView和ListView一起使用的問題,顯示的時候ListView不能完全正確的顯示,查了好多資料終于成功解決:2013-05-05Android Studio 升級到3.0 提示 java.lang.NoClassDefFoundError的解決方法
這篇文章主要介紹了Android Studio 升級到3.0 提示 java.lang.NoClassDefFoundError的解決方法,需要的朋友可以參考下2017-12-12Android使用onCreateOptionsMenu()創(chuàng)建菜單Menu的方法詳解
這篇文章主要介紹了Android使用onCreateOptionsMenu()創(chuàng)建菜單Menu的方法,結合實例形式較為詳細的分析了Android基于onCreateOptionsMenu創(chuàng)建菜單的具體步驟與相關操作技巧,需要的朋友可以參考下2016-11-11Android實現(xiàn)簡單的自定義ViewGroup流式布局
本文我們將一起復習一下ViewGroup的測量布局方式。然后會以入門級的 FlowLayout 為例,來看看流式布局是如何測量與布局的,感興趣的可以了解一下2022-12-12Android通過Handler與AsyncTask兩種方式動態(tài)更新ListView(附源碼)
這篇文章主要介紹了Android通過Handler與AsyncTask兩種方式動態(tài)更新ListView的方法,結合實例形式分析了ListView動態(tài)更新的常用技巧,并附上完整實例源碼供讀者下載,需要的朋友可以參考下2015-12-12音量控制鍵控制的音頻流(setVolumeControlStream)描述
當開發(fā)多媒體應用或者游戲應用的時候,需要使用音量控制鍵來設置程序的音量大小,在Android系統(tǒng)中有多種音頻流,感興趣的朋友可以了解下2013-01-01