網(wǎng)絡(luò)推廣合同網(wǎng)站seo優(yōu)化服務(wù)商
遇到一個需求需要顯示多級列表,因為界面是在平板上的,所以層級是從左向右往下排的,類似于
我當時的寫法是在xml布局里一個個RecyclerView往下排的
當然前提是已經(jīng)規(guī)定好最大的層級我才敢如此去寫界面,如果已經(jīng)明確規(guī)定只有兩級或者三級,當然如果可以的話,不管多少級都這么寫也是沒毛病的
作為一名開發(fā)者,如果以后也用到類似的需求,并且級數(shù)不是固定的話,這么寫肯定是有很多瑕疵的,所以想著怎么樣去做一個輪子,以后遇到直接拿來用就行,不用每次都去搭界面,寫邏輯,當然網(wǎng)絡(luò)上也有類似的代碼,但是感覺和自己的有多少出入,那不如自己來寫一套吧
下面是效果圖,界面有些丑,沒有花時間去做一些美化,只是為了展示不同的效果,畢竟真正用起來界面樣式還是多樣化沒法固定的
首先的問題是如何去分級,一開始想法是把所有數(shù)據(jù)放進入,自己去解析數(shù)據(jù)進行分級,但是又可能涉及到部分數(shù)據(jù)需要網(wǎng)絡(luò)請求等復(fù)雜的環(huán)境
問題一: 如何解決一開始就分級還是點擊之后逐級顯示?
答: 自主觸發(fā),不自行解析添加,告訴我添加我再去添加
問題二: 不同條件下如何去分級?
答: 要定義一個自己的規(guī)則,可能不完善但是后面可以自己添加,否則出現(xiàn)要寫死或者部分要網(wǎng)絡(luò)請求的時候無法完整判斷,而且不同的數(shù)據(jù)結(jié)構(gòu)可能連參數(shù)名字都不一樣,所以需要自主分類并且添加數(shù)據(jù)
思路:
1.一開始傳入最大多少級,以及每一級的樣式(比如寬度,背景),動態(tài)創(chuàng)建對應(yīng)級數(shù)的列表并和適配器存儲起來(別忘了關(guān)閉界面的時候或者不使用的時候去銷毀),這樣就不會存在每次點擊都重新創(chuàng)建列表的情況,每次點擊只是對應(yīng)的顯示隱藏和替換數(shù)據(jù)而已
2.我需要創(chuàng)建第一級的時候,我將第一級的數(shù)據(jù)篩選出來,將步驟1存儲的第一級適配器拿出來塞數(shù)據(jù)
3.如果我一開始就要將第一級相關(guān)的全部顯示出來我只要自己去根據(jù)數(shù)據(jù)篩選出對應(yīng)的下一級,繼續(xù)往里面塞
4.如果我要點擊之后顯示,那我也和第三步驟一樣,點擊之后我再去篩選,再去塞數(shù)據(jù)
5.我點之后要將所有下面的級數(shù)隱藏,在去顯示對應(yīng)的層級,否則就會出現(xiàn)我已經(jīng)出現(xiàn)了三級,此時我點擊其他的層級沒有第三極,但是上次的第三極還在那顯示
6.樣式問題,不同的需求有不同的樣式,這點無能為力,只能自己去修改樣式,但是最基礎(chǔ)的每一級的寬度,間距,背景,以及每一個條目的字體大小,高度是必須的,我可以統(tǒng)一規(guī)定起來
具體實現(xiàn):
首先導(dǎo)入用到的三方依賴
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'
其中的適配器里的實體類要根據(jù)自己的情況去更改
1.創(chuàng)建多級列表:
xml布局里的主布局要用約束布局
將不同層級樣式給Util去創(chuàng)建
//這個集合存儲的是每一層級的樣式以及信息,會根據(jù)該集合的長度去創(chuàng)建最大多少級val mList: ArrayList<GetListUtils.GetListData> = arrayListOf()//第一級樣式mList.add(GetListUtils.GetListData(width = 200,itemSize = 16f,itemHeight = 160, bGround = "#CC00FFFF"))//第二級樣式mList.add(GetListUtils.GetListData(itemSize = 14f,itemHeight = 140,leftMargin = 60, bDrawable = getDrawable(R.mipmap.ic_launcher)))//第三極樣式mList.add(GetListUtils.GetListData(width = 180,itemSize = 12f,itemHeight = 120,leftMargin = 80,bGround = "#CC33ff00"))//第四級樣式mList.add(GetListUtils.GetListData(itemSize = 10f,itemHeight = 100,leftMargin = 30,bGround = "#CCFFFF33"))//第五級樣式mList.add(GetListUtils.GetListData(itemSize = 8f,itemHeight = 80,leftMargin = 70))//這里是布局里的主布局,要用約束布局!!!val clAll = findViewById<ConstraintLayout>(R.id.cl_all)//層級列表和主布局傳進去GetListUtils.find(clAll,context = this,mList)
2.自行分類,將數(shù)據(jù)塞給對應(yīng)的層級,這里是篩選出第一級數(shù)據(jù),并把數(shù)據(jù)給第一級
這里的模擬數(shù)據(jù)集合就需要替換成項目中真正的數(shù)據(jù)類了(TestData)
//模擬數(shù)據(jù) type當前類型 parentType 父級,默認parentType = 0 為一級val mListAll: ArrayList<GetListUtils.TestData> = arrayListOf()mListAll.add(GetListUtils.TestData(name = "美食",type = 1, parentType = 0))mListAll.add(GetListUtils.TestData("飲品",2,0))mListAll.add(GetListUtils.TestData("西瓜",3,1))mListAll.add(GetListUtils.TestData("火龍果",4,1))mListAll.add(GetListUtils.TestData("葡萄",5,1))mListAll.add(GetListUtils.TestData("橘子",6,1))mListAll.add(GetListUtils.TestData("可樂",7,2))mListAll.add(GetListUtils.TestData("雪碧",8,2))mListAll.add(GetListUtils.TestData("美年達",9,2))mListAll.add(GetListUtils.TestData("西瓜汁",10,3))mListAll.add(GetListUtils.TestData("冰鎮(zhèn)西瓜",11,3))mListAll.add(GetListUtils.TestData("火龍果塊",12,4))mListAll.add(GetListUtils.TestData("冰葡萄",13,5))mListAll.add(GetListUtils.TestData("橘子汁",14,6))mListAll.add(GetListUtils.TestData("無糖可樂",15,7))mListAll.add(GetListUtils.TestData("滿糖可樂",16,7))mListAll.add(GetListUtils.TestData("西瓜皮",17,11))mListAll.add(GetListUtils.TestData("西瓜籽",18,11))mListAll.add(GetListUtils.TestData("西瓜瓤",19,11))mListAll.add(GetListUtils.TestData("火龍果籽",20,12))mListAll.add(GetListUtils.TestData("橘子皮",21,14))mListAll.add(GetListUtils.TestData("橘子核",22,14))mListAll.add(GetListUtils.TestData("西瓜子",23,18))//篩選一級目錄val mList1: ArrayList<GetListUtils.TestData> = arrayListOf()mListAll.forEach {if (it.parentType == 0){mList1.add(it)}}//設(shè)置一級目錄GetListUtils.setListData(1,mList1,object :GetListUtils.OnListener{override fun onClick(l: GetListUtils.TestData) {}})
默認顯示對應(yīng)的層級,只需要繼續(xù)篩選,繼續(xù)塞數(shù)據(jù)即可
//篩選二級目錄val mList2: ArrayList<GetListUtils.TestData> = arrayListOf()mListAll.forEach {if (it.parentType == 1){mList1.add(it)}}GetListUtils.setListData(2,mList2,object :GetListUtils.OnListener{override fun onClick(l: GetListUtils.TestData) {}})
以上就是使用方式, setListData(當前層級,層級數(shù)據(jù),點擊事件回調(diào))
以下全部代碼,供參考
GetListUtils
/*** 動態(tài)添加集合* */
object GetListUtils {private val mRVList: ArrayList<RecyclerView> = arrayListOf()private val mAdapterList: ArrayList<QnZtListAdapter> = arrayListOf()private var mParentView: ConstraintLayout? = nullfun find(view: ConstraintLayout, //父控件context: Context, //上下文gradle: ArrayList<GetListData>, //幾級){clearList()mParentView = viewfor (i in 0 until gradle.size){val mGradle = gradle[i]val mRecyclerView = RecyclerView(context)mRecyclerView.id = View.generateViewId()mRVList.add(mRecyclerView)val layoutParams = ConstraintLayout.LayoutParams(mGradle.width,ViewGroup.LayoutParams.WRAP_CONTENT)if (i == 0){layoutParams.startToStart = view.idlayoutParams.topToTop = view.idif (mGradle.leftMargin != 0){layoutParams.leftMargin = mGradle.leftMargin}}else{layoutParams.startToEnd = mRVList[i-1].idlayoutParams.topToTop = mRVList[i-1].idif (mGradle.leftMargin != 0){layoutParams.leftMargin = mGradle.leftMargin}}//背景圖優(yōu)先級高if (mGradle.bDrawable != null){mRecyclerView.background = mGradle.bDrawable}else{if (mGradle.bGround.isNotEmpty()){mRecyclerView.setBackgroundColor(Color.parseColor(mGradle.bGround))}}mRecyclerView.layoutParams = layoutParamsmRecyclerView.layoutManager = LinearLayoutManager(context)mRecyclerView.itemAnimator = DefaultItemAnimator()val mAdapter = QnZtListAdapter(R.layout.item_get_list,mGradle)mRecyclerView.adapter = mAdaptermAdapterList.add(mAdapter)view.addView(mRecyclerView)}}//設(shè)置數(shù)據(jù)fun setListData(i: Int, l: ArrayList<TestData>,k: OnListener){val a = getAdapter(i)if (a != null){a.setList(l)a.setOnItemClickListener { adapter, view, position ->//點擊當前層級,隱藏下一層級for (j in 0 until mRVList.size){//這里減一是因為點擊的時候是i點擊的當前級數(shù),要從下一級開始if (j > i-1){mRVList[j].visibility = View.GONE}}//這里加一是因為i是當前級數(shù),要從下一級開始//下標從零開始為什么還要加一呢?因為獲取列表里面減一了if (getList(i+1) != null){getList(i+1)!!.visibility = View.VISIBLE}val mData = adapter.data as ArrayList<TestData>//回調(diào)k.onClick(mData[position])//更新選中狀態(tài)mData.forEach {it.select = false}mData[position].select = trueadapter.notifyDataSetChanged()}}}//獲取對應(yīng)層級的列表private fun getList(i: Int): RecyclerView?{var mL: RecyclerView? = nullif (mRVList.isNotEmpty() && i-1 < mRVList.size){try {mL = mRVList[i - 1]}catch (e: java.lang.Exception){e.printStackTrace()}}return mL}//獲取對應(yīng)層級的適配器private fun getAdapter(i: Int): QnZtListAdapter?{var mAdapter: QnZtListAdapter? = nullif (mAdapterList.isNotEmpty() && i-1 < mAdapterList.size){try {mAdapter = mAdapterList[i - 1]}catch (e: java.lang.Exception){e.printStackTrace()}}return mAdapter}//置空操作fun clearList(){mRVList.clear()mAdapterList.clear()if (mParentView != null){mParentView!!.removeAllViews()}}interface OnListener {fun onClick(l: TestData)}//需要的數(shù)據(jù)類data class GetListData(var width: Int = ViewGroup.LayoutParams.WRAP_CONTENT, //寬度var leftMargin: Int = 0, //左間距var bGround: String = "", //背景顏色var bDrawable: Drawable? = null, //背景圖片var itemSize: Float = 0f, //條目字體大小var itemHeight: Int = ViewGroup.LayoutParams.WRAP_CONTENT, //條目字體高度):java.io.Serializable//模擬數(shù)據(jù)data class TestData(var name: String? = "", //名稱var type: Int? = 0, //分類var parentType: Int? = 0, //父類var select: Boolean = false //是否選中):java.io.Serializableclass QnZtListAdapter : BaseQuickAdapter<GetListUtils.TestData, BaseViewHolder> {constructor(layoutResId: Int) : super(layoutResId) {}private var gradle: GetListUtils.GetListData? = nullconstructor(layoutResId: Int, data: GetListUtils.GetListData) : super(layoutResId) {this.gradle = data}override fun convert(helper: BaseViewHolder, item: GetListUtils.TestData) {//總布局val clGetList = helper.getView<ConstraintLayout>(R.id.cl_get_list)//第一條不顯示分割線if (helper.adapterPosition == 0){helper.setGone(R.id.view_line,true)}val tvGetTxt = helper.getView<TextView>(R.id.tv_get_txt)tvGetTxt.text = item.nameif (item.select){tvGetTxt.setTextColor(Color.RED)}else{tvGetTxt.setTextColor(Color.GRAY)}if (gradle != null){//界面高度if (gradle!!.itemHeight != 0){val lp: ViewGroup.LayoutParams = clGetList.layoutParamslp.height = gradle!!.itemHeightclGetList.layoutParams = lp}//字體大小if (gradle!!.itemSize != 0f){tvGetTxt.textSize = gradle!!.itemSize}}}}
}
item_get_list
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/cl_get_list"xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"xmlns:tools="http://schemas.android.com/tools"xmlns:app="http://schemas.android.com/apk/res-auto"><Viewandroid:id="@+id/view_line"android:layout_width="0dp"android:layout_height="1px"android:background="#CC666666"app:layout_constraintStart_toStartOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent"/><TextViewandroid:id="@+id/tv_get_txt"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintTop_toTopOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"tools:text = "哈哈哈"/></androidx.constraintlayout.widget.ConstraintLayout>
AddListActivity
/*** 動態(tài)創(chuàng)建多級列表*/
class AddListActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_add_list)//這個集合存儲的是每一層級的樣式以及信息,會根據(jù)該集合的長度去創(chuàng)建最大多少級val mList: ArrayList<GetListUtils.GetListData> = arrayListOf()//第一級樣式mList.add(GetListUtils.GetListData(width = 200,itemSize = 16f,itemHeight = 160, bGround = "#CC00FFFF"))//第二級樣式mList.add(GetListUtils.GetListData(itemSize = 14f,itemHeight = 140,leftMargin = 60, bDrawable = getDrawable(R.mipmap.ic_launcher)))//第三極樣式mList.add(GetListUtils.GetListData(width = 180,itemSize = 12f,itemHeight = 120,leftMargin = 80,bGround = "#CC33ff00"))//第四級樣式mList.add(GetListUtils.GetListData(itemSize = 10f,itemHeight = 100,leftMargin = 30,bGround = "#CCFFFF33"))//第五級樣式mList.add(GetListUtils.GetListData(itemSize = 8f,itemHeight = 80,leftMargin = 70))//這里是布局里的主布局,要用約束布局!!!val clAll = findViewById<ConstraintLayout>(R.id.cl_all)//層級列表和主布局傳進去GetListUtils.find(clAll,context = this,mList)//模擬數(shù)據(jù) type當前類型 parentType 父級,默認parentType = 0 為一級val mListAll: ArrayList<GetListUtils.TestData> = arrayListOf()mListAll.add(GetListUtils.TestData(name = "美食",type = 1, parentType = 0))mListAll.add(GetListUtils.TestData("飲品",2,0))mListAll.add(GetListUtils.TestData("西瓜",3,1))mListAll.add(GetListUtils.TestData("火龍果",4,1))mListAll.add(GetListUtils.TestData("葡萄",5,1))mListAll.add(GetListUtils.TestData("橘子",6,1))mListAll.add(GetListUtils.TestData("可樂",7,2))mListAll.add(GetListUtils.TestData("雪碧",8,2))mListAll.add(GetListUtils.TestData("美年達",9,2))mListAll.add(GetListUtils.TestData("西瓜汁",10,3))mListAll.add(GetListUtils.TestData("冰鎮(zhèn)西瓜",11,3))mListAll.add(GetListUtils.TestData("火龍果塊",12,4))mListAll.add(GetListUtils.TestData("冰葡萄",13,5))mListAll.add(GetListUtils.TestData("橘子汁",14,6))mListAll.add(GetListUtils.TestData("無糖可樂",15,7))mListAll.add(GetListUtils.TestData("滿糖可樂",16,7))mListAll.add(GetListUtils.TestData("西瓜皮",17,11))mListAll.add(GetListUtils.TestData("西瓜籽",18,11))mListAll.add(GetListUtils.TestData("西瓜瓤",19,11))mListAll.add(GetListUtils.TestData("火龍果籽",20,12))mListAll.add(GetListUtils.TestData("橘子皮",21,14))mListAll.add(GetListUtils.TestData("橘子核",22,14))mListAll.add(GetListUtils.TestData("西瓜子",23,18))//篩選一級目錄val mList1: ArrayList<GetListUtils.TestData> = arrayListOf()mListAll.forEach {if (it.parentType == 0){mList1.add(it)}}//設(shè)置一級目錄GetListUtils.setListData(1,mList1,object :GetListUtils.OnListener{override fun onClick(l: GetListUtils.TestData) {//點擊一級目錄后篩選二級val mList2: ArrayList<GetListUtils.TestData> = arrayListOf()mListAll.forEach {//還原選中狀態(tài),如果默認選中第一個用for循環(huán)去設(shè)置it.select = falseif (l.type == it.parentType){mList2.add(it)}}//設(shè)置二級目錄GetListUtils.setListData(2,mList2,object :GetListUtils.OnListener{override fun onClick(l: GetListUtils.TestData) {//點擊二級目錄后篩選三級val mList3: ArrayList<GetListUtils.TestData> = arrayListOf()mListAll.forEach {//還原選中狀態(tài),如果默認選中第一個用for循環(huán)去設(shè)置it.select = falseif (l.type == it.parentType){mList3.add(it)}}//設(shè)置三級目錄GetListUtils.setListData(3,mList3,object :GetListUtils.OnListener{override fun onClick(l: GetListUtils.TestData) {//點擊三級目錄篩選四級目錄val mList4: ArrayList<GetListUtils.TestData> = arrayListOf()mListAll.forEach {//還原選中狀態(tài),如果默認選中第一個用for循環(huán)去設(shè)置it.select = falseif (l.type == it.parentType){mList4.add(it)}}//設(shè)置四級目錄GetListUtils.setListData(4,mList4,object :GetListUtils.OnListener{override fun onClick(l: GetListUtils.TestData) {//點擊四級目錄篩選五級目錄val mList5: ArrayList<GetListUtils.TestData> = arrayListOf()mListAll.forEach {//還原選中狀態(tài),如果默認選中第一個用for循環(huán)去設(shè)置it.select = falseif (l.type == it.parentType){mList5.add(it)}}GetListUtils.setListData(5,mList5,object :GetListUtils.OnListener{override fun onClick(l: GetListUtils.TestData) {Toast.makeText(this@AddListActivity,l.name,Toast.LENGTH_SHORT).show()}})}})}})}})}})}override fun onDestroy() {super.onDestroy()GetListUtils.clearList()}
}
activity_add_list
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/cl_all"xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".AddListActivity"></androidx.constraintlayout.widget.ConstraintLayout>