当前位置:   article > 正文

Android使用RecyclerView实现日期分组以及时间轴显示_sectionedrecyclerviewadapter相册时间轴

sectionedrecyclerviewadapter相册时间轴

抽空介绍一下如何使用RecyclerView来实现分组列表以及时间轴的显示,先看下效果图:

作为Android的小伙伴,在需求方面上,难免遇到实现类似的功能实现,实现起来有一定的难度,RecyclerView进行分组,和时间轴的显示。

重点讲下,RecyclerView如何进行分组,其实就是对数据源集合进行分组:

BuildListDataUtil.kt

  1. object BuildListDataUtil {
  2. /**
  3. * 集合进行分组
  4. */
  5. fun buildListData(sourceList:MutableList<AppInfo>?): MutableList<AppInfo>{
  6. val tempData = ArrayList<AppInfo>()
  7. val appInfo = createEmptyObject(sourceList!![0])
  8. tempData.add(appInfo)
  9. tempData.add(sourceList[0])
  10. var preDate = DateUtils.getTimeStampConvertToDate(sourceList[0]
  11. .orderInfoApp?.publicFirstTime!!, DateUtils.PARAMETER_ALL_DATE_TYPE)
  12. for (index in 1 until sourceList.size) {
  13. val curDate = DateUtils.getTimeStampConvertToDate(sourceList[index]
  14. .orderInfoApp?.publicFirstTime!!, DateUtils.PARAMETER_ALL_DATE_TYPE)
  15. //日期一致的话,就添加至集合
  16. if (TextUtils.equals(preDate, curDate)) {
  17. tempData.add(sourceList[index])
  18. } else {
  19. // 日期不一致,则创建新的对象并添加到集合中
  20. val curAppInfo = createEmptyObject(sourceList[index])
  21. tempData.add(curAppInfo)
  22. tempData.add(sourceList[index])
  23. preDate = curDate
  24. }
  25. }
  26. return tempData
  27. }
  28. /**
  29. * 创建空对象
  30. */
  31. private fun createEmptyObject(appInfo: AppInfo): AppInfo {
  32. var tempInfo = AppInfo()
  33. var orderInfoApp = OrderInfoApp()
  34. orderInfoApp.publicFirstTime = appInfo.orderInfoApp?.publicFirstTime!!
  35. tempInfo.orderInfoApp = orderInfoApp
  36. return tempInfo
  37. }
  38. }

我封装了BuildListDataUtil工具类,主要是对数据源集合逻辑进行分组处理,首先根据集合的索引值拿到第一个对象,进行创建空的对象,同时根据时间戳进行赋值,并返回空的对象作为用来展示标题布局的日期标题,并添加到临时的集合中,根据当前的对象的时间戳返回当前的preDate变量,接下来sourceList进行遍历,遍历下一个对象的时间戳返回当前的curDate 变量,和上一个preDate变量进行对比,如果日期一致,就添加到同一集合里面,否则就创建空的对象并添加到集合里面,并且把curDate值赋值给preDate,以此类推。

我打了断点,如下:

接下来就是把集合分好组的tempData变量返回给外部调用。

重点看下RecycleView的Adapter做了什么。

BuildGroupDateAdapter.kt

  1. class BuildGroupDateAdapter constructor(datas:MutableList<AppInfo>?): Adapter<ViewHolder>() {
  2. //标题
  3. val ITEM_TITLE_TYPE = 1
  4. //内容
  5. val ITEM_CONTENT_TYPE = 2
  6. private var datas:MutableList<AppInfo>? = null
  7. private lateinit var itemTitleBing :ItemTitleLayoutBinding
  8. private lateinit var itemContentBinding: ItemContentLayoutBinding
  9. init {
  10. this.datas = datas
  11. }
  12. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
  13. var view :View
  14. return when(viewType) {
  15. ITEM_TITLE_TYPE -> {
  16. itemTitleBing = ItemTitleLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
  17. view = itemTitleBing.root
  18. TitleViewHolder(view)
  19. }
  20. else -> {
  21. itemContentBinding = ItemContentLayoutBinding
  22. .inflate(LayoutInflater.from(parent.context), parent, false)
  23. view = itemContentBinding.root
  24. ContentViewHolder(view)
  25. }
  26. }
  27. }
  28. override fun onBindViewHolder(holder: ViewHolder, position: Int) {
  29. when(holder) {
  30. is TitleViewHolder -> {
  31. val titleDate = getTimeStampConvertToDate(
  32. datas
  33. ?.get(position)!!.orderInfoApp!!.publicFirstTime,
  34. DateUtils.PARAMETER_ALL_DATE_TYPE
  35. )
  36. itemTitleBing.tvTitle.text = titleDate
  37. dealWithTitleTimeLine(position)
  38. }
  39. is ContentViewHolder -> {
  40. itemContentBinding.tvContent.text = datas!![position].name
  41. dealWithContentTimeLine(position)
  42. }
  43. }
  44. }
  45. /**
  46. * 标题时间轴
  47. */
  48. private fun dealWithTitleTimeLine(index: Int) {
  49. when(index) {
  50. 0 -> {
  51. itemTitleBing.aboveLineTitle.visibility = View.INVISIBLE
  52. itemTitleBing.belowLineTitle.visibility = View.VISIBLE
  53. }
  54. else -> {
  55. itemTitleBing.aboveLineTitle.visibility = View.VISIBLE
  56. itemTitleBing.belowLineTitle.visibility = View.VISIBLE
  57. }
  58. }
  59. }
  60. /**
  61. * 内容时间轴
  62. */
  63. private fun dealWithContentTimeLine(index: Int) {
  64. when(index) {
  65. datas!!.size - 1 -> {
  66. itemContentBinding.timeLineContent.renderBg(R.color.time_line_color, true)
  67. }
  68. else -> {
  69. itemContentBinding.timeLineContent.renderBg(R.color.time_line_color, false)
  70. }
  71. }
  72. }
  73. override fun getItemCount(): Int {
  74. return datas!!.size
  75. }
  76. override fun getItemViewType(position: Int): Int {
  77. var id = datas!![position].id
  78. //id是空的 说明该对象是用来展示标题item布局的
  79. if(id.isNullOrEmpty()) {
  80. return ITEM_TITLE_TYPE
  81. }
  82. return ITEM_CONTENT_TYPE
  83. }
  84. class TitleViewHolder(view: View) : ViewHolder(view)
  85. class ContentViewHolder(view: View) : ViewHolder(view)
  86. }

上面代码逻辑不是很复杂,其实就是创建了两个ViewHolder,一个是用来展示标题的ViewHolder,一个是来展示内容的ViewHolder。

重点看下这个

根据position获取当前对象的id,id是空的用来展示标题item布局的。因为刚讲过, 

 

创建空对象的时候,仅仅是把时间戳赋值给它,并没有赋值给id,因此id是空的话,是用来展示标题的type。

BuildGroupDateActivity.kt

  1. class BuildGroupDateActivity : AppCompatActivity() {
  2. lateinit var adapter: BuildGroupDateAdapter
  3. lateinit var binding: ActivityMainBuildGroupDateBinding
  4. override fun onCreate(savedInstanceState: Bundle?) {
  5. super.onCreate(savedInstanceState)
  6. binding = ActivityMainBuildGroupDateBinding.inflate(layoutInflater)
  7. setContentView(binding.root)
  8. adapter = BuildGroupDateAdapter(getDatas())
  9. binding.baseRecyclerView.layoutManager = LinearLayoutManager(this)
  10. binding.baseRecyclerView.adapter = adapter
  11. }
  12. private fun getDatas() : MutableList<AppInfo> {
  13. return BuildListDataUtil.buildListData(TestBuildData.datas())
  14. }
  15. }

运行此项目,界面上就可以正常展示RecyclerView日期分组了。

接下来,我们看下时间轴怎么实现。

在adapter里面创建了两个ViewHolder,每个ViewHolder创建了item布局,时间线的显示其实就是在布局里面做。

item_title_layout.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. xmlns:app="http://schemas.android.com/apk/res-auto">
  6. <LinearLayout
  7. android:layout_width="match_parent"
  8. android:layout_height="36dp"
  9. android:orientation="horizontal"
  10. app:layout_constraintTop_toTopOf="parent"
  11. >
  12. <RelativeLayout
  13. android:layout_width="30dp"
  14. android:layout_height="match_parent"
  15. >
  16. <View
  17. android:id="@+id/above_line_title"
  18. android:layout_width="1dp"
  19. android:layout_height="18dp"
  20. android:background="@color/time_line_color"
  21. android:layout_centerHorizontal="true"
  22. android:layout_above="@+id/middle_line"
  23. />
  24. <View
  25. android:id="@+id/middle_line"
  26. android:layout_width="5dp"
  27. android:layout_height="5dp"
  28. android:background="@drawable/time_line_round_bg"
  29. android:layout_centerInParent="true"
  30. />
  31. <View
  32. android:id="@+id/below_line_title"
  33. android:layout_width="1dp"
  34. android:layout_height="18dp"
  35. android:layout_below="@id/middle_line"
  36. android:layout_centerHorizontal="true"
  37. android:background="@color/time_line_color" />
  38. </RelativeLayout>
  39. <TextView
  40. android:id="@+id/tvTitle"
  41. android:layout_width="match_parent"
  42. android:layout_height="wrap_content"
  43. android:text=""
  44. android:layout_gravity="center_vertical"
  45. />
  46. </LinearLayout>
  47. </androidx.constraintlayout.widget.ConstraintLayout>

item_content_layout.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. xmlns:app="http://schemas.android.com/apk/res-auto">
  6. <LinearLayout
  7. android:layout_width="match_parent"
  8. android:layout_height="48dp"
  9. android:orientation="horizontal"
  10. app:layout_constraintTop_toTopOf="parent"
  11. >
  12. <RelativeLayout
  13. android:layout_width="30dp"
  14. android:layout_height="match_parent"
  15. >
  16. <com.xilianke.mainapp_master.view.TimeLineView
  17. android:id="@+id/time_line_content"
  18. android:layout_width="1dp"
  19. android:layout_height="match_parent"
  20. android:layout_centerHorizontal="true"
  21. android:background="@color/teal_700" />
  22. </RelativeLayout>
  23. <TextView
  24. android:id="@+id/tvContent"
  25. android:layout_width="wrap_content"
  26. android:layout_height="wrap_content"
  27. android:text=""
  28. android:layout_gravity="center_vertical"
  29. />
  30. </LinearLayout>
  31. </androidx.constraintlayout.widget.ConstraintLayout>

在该布局里面需要自定义一个View,也就是TimeLineView,需要处理时间线渐变。

TimeLineView.kt

  1. class TimeLineView : View {
  2. constructor(context: Context) : this(context, null)
  3. constructor(context: Context, attributeSet: AttributeSet?) :this (context, attributeSet, 0)
  4. constructor(context: Context, attributeSet: AttributeSet?, def: Int):super(context, attributeSet, def)
  5. fun renderBg(color: Int,lastItem:Boolean) {
  6. if (lastItem) {
  7. val gradient = gradientDrawable(color)
  8. background = gradient
  9. } else {
  10. background = context.resources.getDrawable(color)
  11. }
  12. post {
  13. invalidate()
  14. }
  15. }
  16. private fun gradientDrawable(arg: Int): GradientDrawable {
  17. val colors = intArrayOf(
  18. // arg and 0xFFFFFFFF.toInt(),//0%
  19. // arg and 0x7FFFFFFF,//50%
  20. arg and 0x33FFFFFF,//80%
  21. arg and 0x00FFFFFF,//100%
  22. )
  23. val gradientDrawable = GradientDrawable()
  24. gradientDrawable.colors = colors
  25. gradientDrawable.orientation = GradientDrawable
  26. .Orientation.TOP_BOTTOM//从上到下渐变
  27. return gradientDrawable
  28. }
  29. }

运行此项目,最终的效果就是文章开头的效果图。

我把这次案例进行总结,方便后续可能会使用到,难免会使用RecyclerView分组实现,例如RecyclerView联系人分组实现。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/174935
推荐阅读
相关标签
  

闽ICP备14008679号