從新定義RecyclerView Adapter的封裝,追求既簡單又實用,結合Kotlin的高級特性,優化代碼書寫方式,真正作到高內聚低耦合android
設計圖 github
如今有ArrayListAdapter,AnkoListAdapter,將來會有SortedListAdapter,PagingAdapter,爲啥這麼設計呢?bash
你是否是以爲少了點什麼功能?空佈局,上拉加載,下拉加載,拖動,頭佈局,腳佈局,展開摺疊,分割線,動畫等等,這些後期都會帶着大家去實現,上面的規劃更偏向於底層框架的封裝,而這些功能更偏向業務組件,方向不一樣,不要着急哦,帶着大家一步步完善,來體驗封裝Adapter中的樂趣app
名字 | release aar size | 其餘 |
---|---|---|
Core | 25kb | 核心庫目前包含ArrayListAdapter的實現 |
Anko | 12kb | anko擴展庫包含AnkoListAdapter |
Sorted | 0kb | 待實現 |
.. | .. | 待實現 |
建立xml佈局,和以前同樣的佈局方式框架
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_margin="5dp">
<LinearLayout
android:background="?attr/selectableItemBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorPrimary"
android:textSize="22sp" />
<TextView
android:id="@+id/tv_subTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorAccent"
android:textSize="18sp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
複製代碼
定義ViewModel及Model,能夠看到,邏輯簡單明瞭,刷新本身的時候只須要更新Model,並reBindView便可,刷新別人的話,須要經過Adapter去更新,複雜頁面只須要再新建一個ArrayItemViewModel的子類便可,並建立一個新的XML佈局,從這裏的代碼能夠看出,一樣一個ViewModel將來能夠複用不少XML佈局,徹底作到了ViewModel、View、Model三個角色的任意複用。爲業務多樣化提供最底層的支持。dom
/**
* Model
*/
data class ModelTest(var title: String, var subTitle: String)
/**
* ViewModel
*/
class ArrayViewModelTest : ArrayItemViewModel<ModelTest>() {
var index = 0
override fun onBindView(adapter: ArrayListAdapter?) {
viewHolder.itemView.apply {
tv_title.text = model.title
tv_subTitle.text = model.subTitle
cardItem.setOnClickListener {
model.title = "${index++}"
reBindView()
}
}
}
override fun getLayoutRes() = R.layout.item_test
}
複製代碼
複用邏輯以下圖: ide
Activity 中增刪改,增刪改都是對ViewModel層的操做,簡單實用。佈局
/**
* Activity
*/
class ArrayListActivity : AppCompatActivity() {
private val mArrayListAdapter by lazy {
ArrayListAdapter()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_array_list)
rv_list.bindListAdapter(mArrayListAdapter)
// 新增一個
new_add.setText("新增").setOnClickListener {
mArrayListAdapter.add(ArrayViewModelTest().apply {
model = ModelTest("標題", "副標題")
})
}
// 刪除第一個
delete.setText("刪除").setOnClickListener {
if (mArrayListAdapter.size > 0)
mArrayListAdapter.removeAt(0)
else
toast("請添加新用例後再試")
}
// 隨機更新
var updateSize = 0
update.setText("更新").setOnClickListener {
updateSize++
if (mArrayListAdapter.size > 0) {
val randomInt = Random.nextInt(0, mArrayListAdapter.size)
mArrayListAdapter.set(randomInt, ArrayViewModelTest().apply {
model = ModelTest("標題$updateSize", "副標題$updateSize")
})
} else {
toast("請添加新用例後再試")
}
}
}
}
複製代碼
定義AnkoLayoutpost
/**
* AnkoItemView
*/
class AnkoItemView(val itemClick: () -> Unit) : AnkoComponent<ViewGroup> {
var tvTitle: TextView? = null
var tvSubTitle: TextView? = null
var view: View? = null
@SuppressLint("ResourceType")
override fun createView(ui: AnkoContext<ViewGroup>) = with(ui) {
cardView {
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
margin = dip(5)
}
verticalLayout {
setOnClickListener {
itemClick()
}
val typedValue = TypedValue()
context.theme
.resolveAttribute(android.R.attr.selectableItemBackground, typedValue, true)
val attribute = intArrayOf(android.R.attr.selectableItemBackground)
val typedArray =
context.theme.obtainStyledAttributes(typedValue.resourceId, attribute)
background = typedArray.getDrawable(0)
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
padding = dip(10)
}
tvTitle = textView {
textSize = px2dip(60)
textColorResource = R.color.colorPrimary
}.lparams(matchParent, wrapContent)
tvSubTitle = textView {
textSize = px2dip(45)
textColorResource = R.color.colorAccent
}.lparams(matchParent, wrapContent)
}
}
}
}
複製代碼
定義ViewModel,Model,這裏有個細節須要說一下,在ArrayListAdapter的例子中我是在onBindView裏設置的點擊事件,這樣就有個壞處就是致使每次從新onBindView都會致使設置點擊事件,這樣其實很很差,因此在Anko版本里我作了優化,在onCreateView處理點擊事件,這裏就作到了設置一次。
/**
* Model
*/
data class ModelTest(var title: String, var subTitle: String)
/**
* ViewModel
*/
class AnkoViewModelTest : AnkoItemViewModel<ModelTest, AnkoItemView>() {
var index = 0
override fun onBindView(adapter: AnkoListAdapter) {
ankoView.tvTitle?.text = model.title
ankoView.tvSubTitle?.text = model.subTitle
}
override fun onCreateView(): AnkoItemView {
return AnkoItemView{
model.title = "${index++}"
reBindView()
}
}
}
複製代碼
Activity 中增刪改
/**
* Activity
*/
class AnkoLayoutActivity : AppCompatActivity() {
private val mAnkoListAdapter by lazy {
AnkoListAdapter()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AnkoLayoutComponent(mAnkoListAdapter).setContentView(this).apply {
// 新增一個
new_add.setText("新增").setOnClickListener {
mAnkoListAdapter.add(AnkoViewModelTest().apply {
model = ModelTest("標題", "副標題")
})
}
// 刪除第一個
delete.setText("刪除").setOnClickListener {
if (mAnkoListAdapter.size > 0)
mAnkoListAdapter.removeAt(0)
else
toast("請添加新用例後再試")
}
// 隨機更新
var updateSize = 0
update.setText("更新").setOnClickListener {
updateSize++
if (mAnkoListAdapter.size > 0) {
val randomInt = Random.nextInt(0, mAnkoListAdapter.size)
mAnkoListAdapter.set(randomInt, mAnkoListAdapter.getItem(randomInt).apply {
model.also {
it as ModelTest
it.title = "$updateSize"
}
})
} else {
toast("請添加新用例後再試")
}
}
}
}
}
/**
* View
*
*/
class AnkoLayoutComponent(private val ankoListAdapter: AnkoListAdapter) : AnkoComponent<AnkoLayoutActivity> {
override fun createView(ui: AnkoContext<AnkoLayoutActivity>) = with(ui) {
verticalLayout {
recyclerView {
bindListAdapter(ankoListAdapter)
}.lparams(matchParent) {
weight = 1F
}
// Anko 兼容 xml佈局的加載
include<View>(R.layout.include_button_bottom)
}
}
}
複製代碼
一個資深的Android是否是應該學會本身作一個超級的RecyclerView.Adapter