最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android如何實(shí)現(xiàn)時(shí)間線效果(下)

 更新時(shí)間:2021年11月29日 14:46:40   作者:成活  
上一篇文章我們講了Android如何實(shí)現(xiàn)時(shí)間線效果,今天計(jì)息上一回的文章圍繞Android實(shí)現(xiàn)時(shí)間線效果內(nèi)容展開更多,需要的朋友可以參考一下

1、前言

上回Android如何實(shí)現(xiàn)時(shí)間線效果 說(shuō)到,小莊吭哧吭哧的擼完了需求,雖然功能上可以應(yīng)付過(guò)去了,但他總覺得什么地方還可以再優(yōu)化一下,可以搞一個(gè)較為通用的組件,順便還能鍛煉一下自己的編碼設(shè)計(jì)能力,豈不美哉!一起看看他今天又要搞什么幺蛾子唄?

2、分析

2.1提出功能

這回小莊自己當(dāng)上產(chǎn)品了,他上上下下的看了幾遍這個(gè)時(shí)間線,親自提出了幾點(diǎn)不滿可以優(yōu)化的地方:

  • 最好可以根據(jù)位置決定顯示什么東西,比如第一個(gè)/最后一個(gè)一般最重要,要跟別的不一樣!
  • 我要把圓點(diǎn)改成圖片,根據(jù)狀態(tài)展示不同的圖片!
  • 不,我要圓點(diǎn)和圖片交錯(cuò)著來(lái),就像某寶物流信息那樣!

產(chǎn)品小莊被程序員小莊關(guān)了小黑屋。程序員小莊覺得這幾點(diǎn)需求就暫時(shí)夠了,可以先著手嘗試一下了

2.2需求分析

  • 從需求上來(lái)看,變的主要都是圓點(diǎn)(以下稱結(jié)點(diǎn))的部分,畫線的部分應(yīng)該不會(huì)有什么改動(dòng),可以沿用
  • 結(jié)點(diǎn)部分不僅會(huì)變,還會(huì)變的多種多樣,千奇百怪,這里的靈活性要求較高
  • 原來(lái)只有根據(jù)狀態(tài)決定結(jié)點(diǎn)樣式,現(xiàn)在還要根據(jù)位置,也要加入考慮

2.3方案設(shè)想

是什么讓我們可以實(shí)現(xiàn)代碼復(fù)用(線),又可以實(shí)現(xiàn)靈活定制(結(jié)點(diǎn))呢?是繼承呀!(當(dāng)然也可以考慮組合)綜合考慮,畫線的部分可以放在父類中,畫結(jié)點(diǎn)的部分則可以設(shè)計(jì)一個(gè)抽象方法,交給子類自由實(shí)現(xiàn)

3、編碼

重構(gòu)的過(guò)程很快樂(lè)(不是),但是步子太大卻容易拉胯。小莊打算先改動(dòng)代碼結(jié)構(gòu),并保持原來(lái)效果可以運(yùn)行

3.1第三版

說(shuō)干就干,小莊決定抽取一個(gè)drawNode抽象方法,讓子類去畫圓點(diǎn),而在父類中繼續(xù)保留畫線的操作。

  • 子類圓點(diǎn)的顏色其實(shí)可以復(fù)用父類中原本的color屬性,但是以防萬(wàn)一,我們還是重新定義一個(gè)dotColor屬性
  • 但是由于父類現(xiàn)在不畫結(jié)點(diǎn)了,它怎么知道,現(xiàn)在結(jié)點(diǎn)寬度是多少呢?整個(gè)軸線的x坐標(biāo)在哪里呢?不知道這些,沒法畫線呀
  • 為解決這個(gè)問(wèn)題,小莊決定再定義一個(gè)nodeWidth屬性,用來(lái)設(shè)置結(jié)點(diǎn)的寬度還有一個(gè)問(wèn)題,不知道結(jié)點(diǎn)的高度,就不知道上線應(yīng)該在什么y坐標(biāo)停,下線應(yīng)該從什么y坐標(biāo)開始

嗯?你說(shuō)一條線從頂部畫到底部?這也太粗暴了,如果圓點(diǎn)是空心的就暴露了呀,為解決這個(gè)問(wèn)題,小莊只能再定義一個(gè)nodeHeight屬性,用來(lái)設(shè)置結(jié)點(diǎn)的高度?,F(xiàn)在這個(gè)類里面的屬性越來(lái)越多了,小莊感覺這離自己追求的優(yōu)雅相去甚遠(yuǎn)

abstract class ThirdVerTimeline<T> : RecyclerView.ItemDecoration() {

    // 不重要的屬性...

    var color: (item: T) -> Int = { _ -> Color.GRAY }

    var nodeWidth = 30

    var nodeHeight = 30

    

    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 xPosition = (nodeWidth / 2 + paddingLeft).toFloat() //-->這里有修改,用nodeWidth計(jì)算x坐標(biāo)

            // 畫上線和下線...//-->這里有修改,用nodeHeight參與計(jì)算

        }

        // 子類畫結(jié)點(diǎn)!

        drawNode(c, parent, state)

    }



    // 由子類實(shí)現(xiàn)

    protected abstract fun drawNode(c: Canvas, parent: RecyclerView, state: RecyclerView.State)

    

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {

        super.getItemOffsets(outRect, view, parent, state)

        outRect.left = paddingLeft + paddingRight + nodeWidth //-->這里有修改,用nodeWidth計(jì)算item的偏移量

    }

}



class DotTimeline<T> :ThirdVerTimeline<T>(){

    // 這里是子類自己的paint,不復(fù)用父類

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    val radius = 10f

    var dotColor: (item: T) -> Int = { _ -> Color.GRAY }



    override fun drawNode(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {

        val count = parent.childCount

        for (i in 0 until count) {

            // 【不重要的變量】...

            // 畫圓點(diǎn)!

            paint.color = dotColor(item)

            c.drawCircle(xPosition, itemView.top + offset + radius, radius, paint)

        }

    }

}

運(yùn)行起來(lái)完全沒問(wèn)題!不過(guò)觀眾姥爺們肯定發(fā)現(xiàn)了一個(gè)致命問(wèn)題,那就是:為什么在父類和子類里都要各自做循環(huán)呢??這是妥妥的代碼重復(fù)呀!哎,不著急,為了解決這個(gè)問(wèn)題,我們需要重新設(shè)計(jì)一下drawNode方法。

你可能會(huì)想,把drawNode直接放進(jìn)循環(huán)里不就行了嗎?的確,但是在那之前,我們先來(lái)看一下【不重要的變量】里都有哪些變量:

// 獲取當(dāng)前的itemView

val itemView = parent.getChildAt(i)

// 當(dāng)前項(xiàng)的x坐標(biāo),整個(gè)軸線的x坐標(biāo)都是相同的

val xPosition = (nodeWidth / 2 + paddingLeft).toFloat()

// 當(dāng)前項(xiàng)的真正位置

val adapterPosition = parent.getChildAdapterPosition(itemView)

// 當(dāng)前項(xiàng)的數(shù)據(jù)源

val item = data[adapterPosition]

子類中是不是真的都需要父類傳給它這些變量呢?讓我們來(lái)研究一下:

  • itemView:子類中畫結(jié)點(diǎn)需要確定自身的位置等信息 --> 需要
  • xPosition:雖然子類可以自己計(jì)算,但是由父類傳給子類豈不是更能保證它們都在同一條直線上?--> 需要
  • adapterPosition:需求中說(shuō)到要根據(jù)位置繪制結(jié)點(diǎn) --> 需要
  • item:子類自然要根據(jù)數(shù)據(jù)的狀態(tài)來(lái)判斷畫什么 --> 需要

其實(shí)說(shuō)來(lái)說(shuō)去,這些變量子類中都可以計(jì)算得到,之所以要由父類傳給子類,大致有以下幾個(gè)理由:

  • 減少重復(fù)代碼量,遵循DRY原則(Don't repeat yourself)
  • 防止父類和子類的數(shù)據(jù)獲取有出入,子類不按照父類的規(guī)則來(lái)計(jì)算
  • 某一天又要改規(guī)則了,只需改動(dòng)父類就可以了,無(wú)需改動(dòng)多處

至于一個(gè)方法這么多個(gè)參數(shù)真的好嗎?這~我暫時(shí)也無(wú)能為力啦。修改后的代碼如下:

abstract class ThirdVerTimeline<T> : RecyclerView.ItemDecoration() {

    // 不重要的屬性...

    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) {

            // 【重要的變量】...

            // 畫上線和下線...

            

            // 子類畫結(jié)點(diǎn)!

            drawNode(c, parent, state, xPosition, item, itemView, adapterPosition)

        }

    }



    // 由子類實(shí)現(xiàn)

    protected abstract fun drawNode(c: Canvas, parent: RecyclerView, state: RecyclerView.State,

                                xPosition: Float, item: T, itemView: View, adapterPosition: Int)

    

    // getItemOffsets...

}



class DotTimeline<T> :ThirdVerTimeline<T>(){

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val radius = 10f



    override fun drawNode(c: Canvas, parent: RecyclerView, state: RecyclerView.State,

                        xPosition: Float, item: T, itemView: View, adapterPosition: Int) {

        // 簡(jiǎn)簡(jiǎn)單單畫圓即可

        paint.color = color(item)

        c.drawCircle(xPosition, itemView.top + offset + radius, radius, paint)

    }

}

搞了好大一圈,才只是實(shí)現(xiàn)了原本的功能而已,由于效果都是一模一樣的,我就不貼圖了。但是接下來(lái)我們就可以隨心所欲對(duì)父類進(jìn)行擴(kuò)展啦

不知道有沒有小伙伴有疑問(wèn),這不是也類似于Java中的模板模式嗎?你怎么不用上一章說(shuō)的kotlin的函數(shù)類型啦?

的確用函數(shù)類型可以幫我們減少這些子類的數(shù)量,使用時(shí)只要新建一個(gè)ItemDecoration,并且設(shè)置drawNode屬性怎么畫就可以了,想想就美滋滋。然而我不這么做至少有兩個(gè)理由:

  • 實(shí)現(xiàn)一個(gè)DotTimeline,可以復(fù)用,不至于每個(gè)要用到的地方都寫一遍drawNode的內(nèi)容(當(dāng)然也可能目前只有一處用到,但是未來(lái)是很長(zhǎng)的~)
  • 使DotTimeline的使用者關(guān)心的事情更少,職責(zé)更單一(其實(shí)我也是寫一半才想起的,也許并沒有人有這個(gè)疑問(wèn)吧哈哈哈)

3.2第四版

第四版基于第三版的父類,實(shí)現(xiàn)個(gè)圖片的需求~

  • 類似于之前提到的color屬性,這里我們也定義一個(gè)drawableRes屬性,用它來(lái)設(shè)置圖片選擇策略
  • 同時(shí)我們還可以用nodeWidthnodeHeight用來(lái)控制圖片的大小,畢竟我們并不是總能找到尺寸合適的圖片
class PicTimeline<T>(private val context: Context) : ThirdVerTimeline<T>() {

    lateinit var drawableRes: (item: T) -> Int



    override fun drawNode(c: Canvas, parent: RecyclerView, state: RecyclerView.State,

                        xPosition: Float, item: T, itemView: View, adapterPosition: Int) {

        // 從圖片選擇策略中獲得bitmap

        val bitmap = BitmapFactory.decodeResource(context.resources, drawableRes(item))

        val src = Rect(0, 0, bitmap.width, bitmap.height)

        val left = xPosition - nodeWidth / 2

        val top = (itemView.top + offset).toFloat()

        val dst = RectF(left, top, left + nodeWidth, top + nodeHeight)

        // 畫圖片

        c.drawBitmap(bitmap, src, dst, Paint()

        )

    }

}

在使用的時(shí)候,要設(shè)置一下drawableRes屬性(記得也要設(shè)置color屬性,這個(gè)屬性現(xiàn)在代表線的顏色)

picTimeline.drawableRes = { item ->

    when (item.status) {

        1 -> R.drawable.ic_checked

        else -> R.drawable.ic_uncheck

    }

}

rv_timeline4.addItemDecoration(picTimeline)

然后就可以運(yùn)行一下看看效果~

是真的!可以誒!小莊在成功的大路上豬突猛進(jìn)!

至于圖片和圓點(diǎn)交錯(cuò)的效果,現(xiàn)在看起來(lái)也就比較簡(jiǎn)單了,只要把第三版的圓點(diǎn)和第四版的圖片中的代碼混搭在一起就可以了。這邊就不貼代碼了,萬(wàn)一有感興趣的朋友可以到代碼倉(cāng)庫(kù)中查看。直接來(lái)看看效果如何吧

還可以還可以

3.3最終版

其實(shí)小莊還有很多功能想實(shí)現(xiàn)的,但是好像沒必要再寫下去了,無(wú)非就是一些屬性的修修改改罷遼。而且實(shí)際項(xiàng)目中可能也用不到這么多功能,沒有實(shí)際需求指導(dǎo),越擴(kuò)展反而越復(fù)雜、越難用。

最后放一張全家福,有需要的朋友可以參考一下我的思路,或者選取合適的部分代碼食用,每一個(gè)版本的示例代碼都在這里!

如圖,最后三個(gè)是最終的版本。在最終版中,我又實(shí)現(xiàn)了:

  • 時(shí)間線的左右位置
  • 圓點(diǎn)的類型:實(shí)心和邊框
  • 結(jié)點(diǎn)的不同大小
  • 線的寬度
  • item的間距(贈(zèng)送的)

并且在顏色color/圖片drawableRes選擇策略中加入adapterPosition參數(shù),可以根據(jù)位置進(jìn)行設(shè)置策略啦

到此這篇關(guān)于 Android如何實(shí)現(xiàn)時(shí)間線效果(下)的文章就介紹到這了,更多相關(guān) Android時(shí)間線效果內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

屏东市| 商河县| 榆林市| 闽侯县| 海淀区| 怀仁县| 焉耆| 吴桥县| 江源县| 唐海县| 八宿县| 巢湖市| 博乐市| 潼关县| 黎平县| 东山县| 盘锦市| 邻水| 太和县| 潜山县| 时尚| 钟祥市| 耿马| 屯昌县| 恭城| 汝州市| 江达县| 北京市| 安图县| 金平| 唐海县| 桐城市| 长治市| 五寨县| 龙岩市| 肥东县| 田阳县| 腾冲县| 巨鹿县| 称多县| 申扎县|