Android使用RecyclerView實現(xiàn)日期分組以及時間軸顯示

抽空介紹一下如何使用RecyclerView來實現(xiàn)分組列表以及時間軸的顯示,先看下效果圖:


Video_231202152515.gif

作為Android的小伙伴,在需求方面上,難免遇到實現(xiàn)類似的功能實現(xiàn),實現(xiàn)起來有一定的難度,RecyclerView進行分組,和時間軸的顯示。
重點講下,RecyclerView如何進行分組,其實就是對數(shù)據(jù)源集合進行分組:
BuildListDataUtil.kt

object BuildListDataUtil {

    /**
     * 集合進行分組
     */
    fun buildListData(sourceList:MutableList<AppInfo>?): MutableList<AppInfo>{
        val tempData = ArrayList<AppInfo>()
        val appInfo = createEmptyObject(sourceList!![0])
        tempData.add(appInfo)
        tempData.add(sourceList[0])
        var preDate = DateUtils.getTimeStampConvertToDate(sourceList[0]
            .orderInfoApp?.publicFirstTime!!, DateUtils.PARAMETER_ALL_DATE_TYPE)
        for (index in 1 until sourceList.size) {
            val curDate = DateUtils.getTimeStampConvertToDate(sourceList[index]
                .orderInfoApp?.publicFirstTime!!, DateUtils.PARAMETER_ALL_DATE_TYPE)
            //日期一致的話,就添加至集合
            if (TextUtils.equals(preDate, curDate)) {
                tempData.add(sourceList[index])
            } else {
                // 日期不一致,則創(chuàng)建新的對象并添加到集合中
                val curAppInfo = createEmptyObject(sourceList[index])
                tempData.add(curAppInfo)
                tempData.add(sourceList[index])
                preDate = curDate
            }
        }
        return tempData
    }

    /**
     * 創(chuàng)建空對象
     */
    private fun createEmptyObject(appInfo: AppInfo): AppInfo {
        var tempInfo = AppInfo()
        var orderInfoApp = OrderInfoApp()
        orderInfoApp.publicFirstTime = appInfo.orderInfoApp?.publicFirstTime!!
        tempInfo.orderInfoApp = orderInfoApp
        return tempInfo
    }
}

我封裝了BuildListDataUtil工具類,主要是對數(shù)據(jù)源集合邏輯進行分組處理,首先根據(jù)集合的索引值拿到第一個對象,進行創(chuàng)建空的對象,同時根據(jù)時間戳進行賦值,并返回空的對象作為用來展示標題布局的日期標題,并添加到臨時的集合中,根據(jù)當前的對象的時間戳返回當前的preDate變量,接下來sourceList進行遍歷,遍歷下一個對象的時間戳返回當前的curDate 變量,和上一個preDate變量進行對比,如果日期一致,就添加到同一集合里面,否則就創(chuàng)建空的對象并添加到集合里面,并且把curDate值賦值給preDate,以此類推。
我打了斷點,如下:


圖片1.png

圖片2.png

接下來就是把集合分好組的tempData變量返回給外部調用。
重點看下RecycleView的Adapter做了什么。
BuildGroupDateAdapter.kt

class BuildGroupDateAdapter constructor(datas:MutableList<AppInfo>?): Adapter<ViewHolder>() {
    //標題
    val ITEM_TITLE_TYPE = 1
    //內容
    val ITEM_CONTENT_TYPE = 2
    private var datas:MutableList<AppInfo>? = null
    private lateinit var itemTitleBing :ItemTitleLayoutBinding
    private lateinit var itemContentBinding: ItemContentLayoutBinding

    init {
        this.datas = datas
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
      var view :View
        return when(viewType) {
            ITEM_TITLE_TYPE -> {
                itemTitleBing = ItemTitleLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                view = itemTitleBing.root
                TitleViewHolder(view)
            }
            else -> {
                itemContentBinding = ItemContentLayoutBinding
                    .inflate(LayoutInflater.from(parent.context), parent, false)
                view = itemContentBinding.root
                ContentViewHolder(view)
            }
        }
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        when(holder) {
            is TitleViewHolder -> {
                val titleDate = getTimeStampConvertToDate(
                    datas
                        ?.get(position)!!.orderInfoApp!!.publicFirstTime,
                    DateUtils.PARAMETER_ALL_DATE_TYPE
                )
                itemTitleBing.tvTitle.text = titleDate
                dealWithTitleTimeLine(position)
            }
            is ContentViewHolder -> {
                itemContentBinding.tvContent.text = datas!![position].name
                dealWithContentTimeLine(position)
            }
        }
    }

    /**
     * 標題時間軸
     */
    private fun dealWithTitleTimeLine(index: Int) {
        when(index) {
            0 -> {
                itemTitleBing.aboveLineTitle.visibility = View.INVISIBLE
                itemTitleBing.belowLineTitle.visibility = View.VISIBLE
            }
            else -> {
                itemTitleBing.aboveLineTitle.visibility = View.VISIBLE
                itemTitleBing.belowLineTitle.visibility = View.VISIBLE
            }
        }
    }

    /**
     * 內容時間軸
     */
    private fun dealWithContentTimeLine(index: Int) {
        when(index) {
            datas!!.size - 1  -> {
                itemContentBinding.timeLineContent.renderBg(R.color.time_line_color, true)
            }
            else -> {
                itemContentBinding.timeLineContent.renderBg(R.color.time_line_color, false)
            }
        }
    }

    override fun getItemCount(): Int {
       return datas!!.size
    }

    override fun getItemViewType(position: Int): Int {
        var id = datas!![position].id
        //id是空的 說明該對象是用來展示標題item布局的
        if(id.isNullOrEmpty()) {
          return ITEM_TITLE_TYPE
        }
        return ITEM_CONTENT_TYPE
    }

    class TitleViewHolder(view: View) : ViewHolder(view)

    class ContentViewHolder(view: View) : ViewHolder(view)
}

上面代碼邏輯不是很復雜,其實就是創(chuàng)建了兩個ViewHolder,一個是用來展示標題的ViewHolder,一個是來展示內容的ViewHolder。
重點看下這個,


圖片3.png

根據(jù)position獲取當前對象的id,id是空的用來展示標題item布局的。因為剛講過,


圖片4.png

創(chuàng)建空對象的時候,僅僅是把時間戳賦值給它,并沒有賦值給id,因此id是空的話,是用來展示標題的type。
BuildGroupDateActivity.kt
class BuildGroupDateActivity : AppCompatActivity() {

    lateinit var adapter: BuildGroupDateAdapter
    lateinit var binding: ActivityMainBuildGroupDateBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBuildGroupDateBinding.inflate(layoutInflater)
        setContentView(binding.root)
        adapter = BuildGroupDateAdapter(getDatas())
        binding.baseRecyclerView.layoutManager = LinearLayoutManager(this)
        binding.baseRecyclerView.adapter = adapter
    }

    private fun getDatas() : MutableList<AppInfo> {
        return BuildListDataUtil.buildListData(TestBuildData.datas())
    }
}

運行此項目,界面上就可以正常展示RecyclerView日期分組了。
接下來,我們看下時間軸怎么實現(xiàn)。
在adapter里面創(chuàng)建了兩個ViewHolder,每個ViewHolder創(chuàng)建了item布局,時間線的顯示其實就是在布局里面做。
item_title_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="36dp"
        android:orientation="horizontal"
        app:layout_constraintTop_toTopOf="parent"
        >
        <RelativeLayout
            android:layout_width="30dp"
            android:layout_height="match_parent"
            >
            <View
                android:id="@+id/above_line_title"
                android:layout_width="1dp"
                android:layout_height="18dp"
                android:background="@color/time_line_color"
                android:layout_centerHorizontal="true"
                android:layout_above="@+id/middle_line"
                />
            <View
                android:id="@+id/middle_line"
                android:layout_width="5dp"
                android:layout_height="5dp"
                android:background="@drawable/time_line_round_bg"
                android:layout_centerInParent="true"
                />

            <View
                android:id="@+id/below_line_title"
                android:layout_width="1dp"
                android:layout_height="18dp"
                android:layout_below="@id/middle_line"
                android:layout_centerHorizontal="true"
                android:background="@color/time_line_color" />
        </RelativeLayout>
    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text=""
        android:layout_gravity="center_vertical"
        />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

item_content_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:orientation="horizontal"
        app:layout_constraintTop_toTopOf="parent"
        >
        <RelativeLayout
            android:layout_width="30dp"
            android:layout_height="match_parent"
            >
            <com.xilianke.mainapp_master.view.TimeLineView
                android:id="@+id/time_line_content"
                android:layout_width="1dp"
                android:layout_height="match_parent"
                android:layout_centerHorizontal="true"
                android:background="@color/teal_700" />
        </RelativeLayout>
        <TextView
            android:id="@+id/tvContent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            android:layout_gravity="center_vertical"
            />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

在該布局里面需要自定義一個View,也就是TimeLineView,需要處理時間線漸變。
TimeLineView.kt

class TimeLineView : View {

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attributeSet: AttributeSet?) :this (context, attributeSet, 0)

    constructor(context: Context, attributeSet: AttributeSet?, def: Int):super(context, attributeSet, def)

    fun renderBg(color: Int,lastItem:Boolean) {
        if (lastItem) {
          val gradient = gradientDrawable(color)
            background = gradient
        } else {
            background = context.resources.getDrawable(color)
        }
        post {
            invalidate()
        }
    }

    private fun gradientDrawable(arg: Int): GradientDrawable {
      val colors = intArrayOf(
//          arg and 0xFFFFFFFF.toInt(),//0%
//          arg and 0x7FFFFFFF,//50%
          arg and 0x33FFFFFF,//80%
          arg and 0x00FFFFFF,//100%
      )
        val gradientDrawable = GradientDrawable()
        gradientDrawable.colors = colors
        gradientDrawable.orientation = GradientDrawable
            .Orientation.TOP_BOTTOM//從上到下漸變
        return gradientDrawable
    }
}
圖片5.png

運行此項目,最終的效果就是文章開頭的效果圖。
我把這次案例進行總結,方便后續(xù)可能會使用到,難免會使用RecyclerView分組實現(xiàn),例如RecyclerView聯(lián)系人分組實現(xiàn)。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容