前言:繼《RecycleView懶加載失效問題》之后,再介紹另一個場景,如下圖,兩層RecycleView,外層是豎向串列樣式,內層是網格樣式,由于內層網格個數不固定,需要內層RecycleView的根布局高度為wrap_content(注:這是導致內層RecycleView懶加載失效的原因)

問題:盡管第2項只顯示了一小部分,但仍加載了全部資料,內層懶加載失效,當內層資料量很大,一次要全部加載,會感覺到卡頓,
分析:首先,分析為什么外層的RecycleView懶加載正常,而內層的RecycleView失效了呢?區別在于外層RecycleView高度是確定的,至少不會超過父控制元件,而內層根布局高度為wrap_content,即item沒有對最大高度做限制,內層需要多大就賦予多大,于是就加載了全部內層的資料,我們可以先將內層的根布局高度設定為固定值驗證一下,發現高度固定后內層也支持懶加載了,畢竟RecycleView高度限制了,超過高度的item就不加載了,但是這個就不滿足我們的需求了,內層資料少的時候會留白,資料多的時候顯示不全,而且只是外層回應滑動事件,內層無法回應,所有我們只能將內層根布局高度設定為wrap_content,
1、外層配接器
class TestPercentAdapter : RecyclerView.Adapter<TestPercentAdapter.TestPercentViewHolder>(){
private val testPercentList = arrayListOf<TestPercentBean>()
init {
for (i in 1..20){
testPercentList.add(TestPercentBean("外層第${i}項"))
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestPercentViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_test, parent, false)
return TestPercentViewHolder(view)
}
override fun getItemCount(): Int {
return testPercentList.size
}
override fun onBindViewHolder(holder: TestPercentViewHolder, position: Int) {
holder.tvItem.text = testPercentList[position].mTitle
holder.adapterTwo.testChildrenList = testPercentList[position].mChildrenList
holder.adapterTwo.notifyDataSetChanged()
Log.e("aa", "***************${testPercentList[position].mTitle}")
}
inner class TestPercentViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tvItem = view.findViewById<TextView>(R.id.tv_item)
val rvItem = view.findViewById<RecyclerView>(R.id.rv_item)
val adapterTwo = TestChildrenAdapter()
init {
rvItem.layoutManager = GridLayoutManager(rvItem.context, 2)
rvItem.adapter = adapterTwo
}
}
}
2、外層布局
<?xml version="1.0" encoding="utf-8"?>
<com.hualala.myapplication.MyLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_item"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:textColor="@color/colorAccent"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.hualala.myapplication.MyLinearLayout>
3、內層配接器
class TestChildrenAdapter : RecyclerView.Adapter<TestChildrenAdapter.TestChildrenViewHolder>(){
var testChildrenList = arrayListOf<TestPercentBean.TestChildrenBean>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestChildrenViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_test_two, parent, false)
return TestChildrenViewHolder(view)
}
override fun getItemCount(): Int {
return testChildrenList.size
}
override fun onBindViewHolder(holder: TestChildrenViewHolder, position: Int) {
holder.tv_test.text = testChildrenList[position].mTitle
Log.e("aa", "***********${testChildrenList[position].mTitle}")
}
class TestChildrenViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tv_test = itemView.findViewById<TextView>(R.id.tv_test)
}
}
4、內層布局
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_test"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"/>
5、主頁布局
<?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="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_test"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_percent="0.5"/>
<View
android:layout_width="1px"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="#666666"/>
<Button
android:id="@+id/bt_update"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/rv_test"
android:layout_marginStart="10dp"
android:layout_marginRight="10dp"
app:layout_constraintEnd_toEndOf="parent"
android:gravity="center"
android:text="更新"/>
</androidx.constraintlayout.widget.ConstraintLayout>
6、加載資料
rv_test.layoutManager = LinearLayoutManager(this)
rv_test.adapter = TestPercentAdapter()
bt_update.setOnClickListener { (rv_test.adapter as TestPercentAdapter).notifyDataSetChanged() }
8、物體類
class TestPercentBean constructor(title: String){
val mTitle = title
val mChildrenList = ArrayList<TestChildrenBean>()
init {
for (i in 1..(Math.random() * 100).toInt()){
mChildrenList.add(TestChildrenBean("內層第{$i}項"))
}
}
class TestChildrenBean constructor(title: String){
val mTitle = title
}
}
方案:分析了原因,有了初步的方案,需要滿足這兩個條件即可解決:1、給內部RecycleView設定最大高度為外層RecycleView高度(解決懶加載,最多加載條數進行了限制,不會無限大) 2、滑動沖突(這部分太過于復雜,導致最終放棄了該方案,可以想一下我點擊在第一項內層的RecycleView上向上滑動,由于已經都顯示了無需再加載了需要把事件交給外層RecycleView,但當第2項內層RecycleView全部顯示出來時,又要將事件給第2項內層RecycleView繼續加載剩下的資料,全部顯示后再將事件交給外層RecycleView——即內層RecycleView布局未全部顯示出來時外層回應滑動,全部顯示則內層回應,如果內層滑動到第1item或最后item,則再由外層回應)
簡單方案:考慮上面的方案復雜,那就換一種思路,用單層RecycleView實作,則需要網格支持動態分配列數,當為外層資料時為1列,為內層資料時為2列,
通過GridLayoutManager.SpanSizeLookup實作動態列數
1、GridLayoutManager第2個引數spanCount為每行列數的最小公倍數
2、getSpanSize回傳值為spanCount除以該行列數
1、配接器
class TestAdapter : RecyclerView.Adapter<TestAdapter.TestViewHolder>() {
var testList = ArrayList<TestBean>()
init {
for (i in 1..20) {
testList.add(TestBean("外層第${i}項", true))
for (j in 1..(Math.random() * 100).toInt()) {
testList.add(TestBean("內層第${j}項", false))
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.item_test_two, parent, false)
return TestViewHolder(view)
}
override fun getItemCount(): Int {
return testList.size
}
override fun onBindViewHolder(holder: TestViewHolder, position: Int) {
holder.tv_test.text = testList[position].mTitle
if (testList[position].mIsPercent){
holder.tv_test.setTextColor(holder.tv_test.resources.getColor(R.color.colorAccent))
}else{
holder.tv_test.setTextColor(holder.tv_test.resources.getColor(R.color.colorPrimaryDark))
}
Log.e("aa", "***********${testList[position].mTitle}")
}
class TestViewHolder constructor(view: View) : RecyclerView.ViewHolder(view) {
val tv_test = itemView.findViewById<TextView>(R.id.tv_test)
}
}
2、子項布局
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_test"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"/>
3、物體類
class TestBean(title: String, isPercent: Boolean) {
val mTitle = title
val mIsPercent = isPercent
}
4、加載資料
/* 1、GridLayoutManager第2個引數spanCount為每行列數的最小公倍數
* 2、getSpanSize回傳值為spanCount除以該行列數
* */
val gridLayoutManager = GridLayoutManager(this, 2)
val testAdapter = TestAdapter()
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup(){
override fun getSpanSize(position: Int): Int {
return if (testAdapter.testList[position].mIsPercent) 2 else 1
}
}
rv_test.layoutManager = gridLayoutManager
rv_test.adapter = testAdapter
bt_update.setOnClickListener { (rv_test.adapter as TestAdapter).notifyDataSetChanged() }
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/286967.html
標籤:其他
