赞
踩
抽空介绍一下如何使用RecyclerView来实现分组列表以及时间轴的显示,先看下效果图:
作为Android的小伙伴,在需求方面上,难免遇到实现类似的功能实现,实现起来有一定的难度,RecyclerView进行分组,和时间轴的显示。
重点讲下,RecyclerView如何进行分组,其实就是对数据源集合进行分组:
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 {
- // 日期不一致,则创建新的对象并添加到集合中
- val curAppInfo = createEmptyObject(sourceList[index])
- tempData.add(curAppInfo)
- tempData.add(sourceList[index])
- preDate = curDate
- }
- }
- return tempData
- }
-
- /**
- * 创建空对象
- */
- private fun createEmptyObject(appInfo: AppInfo): AppInfo {
- var tempInfo = AppInfo()
- var orderInfoApp = OrderInfoApp()
- orderInfoApp.publicFirstTime = appInfo.orderInfoApp?.publicFirstTime!!
- tempInfo.orderInfoApp = orderInfoApp
- return tempInfo
- }
- }
我封装了BuildListDataUtil工具类,主要是对数据源集合逻辑进行分组处理,首先根据集合的索引值拿到第一个对象,进行创建空的对象,同时根据时间戳进行赋值,并返回空的对象作为用来展示标题布局的日期标题,并添加到临时的集合中,根据当前的对象的时间戳返回当前的preDate变量,接下来sourceList进行遍历,遍历下一个对象的时间戳返回当前的curDate 变量,和上一个preDate变量进行对比,如果日期一致,就添加到同一集合里面,否则就创建空的对象并添加到集合里面,并且把curDate值赋值给preDate,以此类推。
我打了断点,如下:
接下来就是把集合分好组的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)
- }
上面代码逻辑不是很复杂,其实就是创建了两个ViewHolder,一个是用来展示标题的ViewHolder,一个是来展示内容的ViewHolder。
重点看下这个
根据position获取当前对象的id,id是空的用来展示标题item布局的。因为刚讲过,
创建空对象的时候,仅仅是把时间戳赋值给它,并没有赋值给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日期分组了。
接下来,我们看下时间轴怎么实现。
在adapter里面创建了两个ViewHolder,每个ViewHolder创建了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
- }
- }
运行此项目,最终的效果就是文章开头的效果图。
我把这次案例进行总结,方便后续可能会使用到,难免会使用RecyclerView分组实现,例如RecyclerView联系人分组实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。