For the basic implementation of RecyclerView, refer to the previous article: https://blog.csdn.net/whjk20/article/details/106950422
The reality is that the recycle view's pull-up refresh loads more.
Basic implementation logic: the last entry has two statuses: one is prompt loading, the other is prompt retry if loading fails.
1. Add more layouts to load:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.cardview.widget.CardView app:cardUseCompatPadding="true" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:id="@+id/loading" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="90dp" > <ProgressBar android:id="@+id/progress_bar" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent"/> <TextView android:layout_width="0dp" android:layout_weight="1" android:text="Is playing with the load of life!!" android:textSize="15sp" android:gravity="center" android:layout_height="match_parent"/> </LinearLayout> <TextView android:id="@+id/reload" android:text="Loading failed, please try again" android:layout_width="match_parent" android:gravity="center" android:layout_height="90dp"/> </LinearLayout> </androidx.cardview.widget.CardView> </RelativeLayout>
Where id:loading is loading, id:reload means loading failed, please try again
2. Define data type
In fact, it's the same as the common data type before, but it's just convenient to distinguish
class UserDataLoadMore { var userName: String var userImageId: Int constructor(userNameId: String, userImageId: Int, state: Int) { this.userName = userNameId this.userImageId = userImageId } }
3. Modify Adapter
It's nothing more than adding a ViewHolder to load more items, modifying the type of items obtained, and then modifying the type returned by creating the ViewHolder,
Complete code directly
class ListViewLoadMoreNormalAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder> { private var onRefreshListener: OnRefreshListener ?=null private var datas: MutableList<UserDataLoadMore> constructor(datas: MutableList<UserDataLoadMore>) { this.datas = datas } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { var view: View if (viewType == TYPE_LOAD_MORE) { view = View.inflate(parent.context, R.layout.item_load_more, null) return LoadMoreViewHolder(view) } else { view = View.inflate(parent.context, R.layout.item_list_view, null) return NormalViewHolder(view) } } override fun getItemCount(): Int { return if (datas != null) { datas.size } else { 0 } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is NormalViewHolder) { holder.setData(datas[position]) } else if (holder is LoadMoreViewHolder) { holder.update(holder.LOAD_STATE_LOADING) } } override fun getItemViewType(position: Int): Int { return if (position == datas.size - 1) { TYPE_LOAD_MORE } else { TYPE_NORMAL } } class NormalViewHolder : RecyclerView.ViewHolder { private var image: ImageView private var nameText: TextView //private var state: Int = TYPE_NORMAL constructor(itemView: View) : super(itemView) { this.nameText = itemView.user_name this.image = itemView.user_image } fun setData(userData: UserDataLoadMore) { nameText.text = userData.userName image.setImageResource(userData.userImageId) } } inner class LoadMoreViewHolder : RecyclerView.ViewHolder { val LOAD_STATE_NORMAL = 0 val LOAD_STATE_LOADING = 1 val LOAD_STATE_RELOAD = 2 private var loadingLayout: LinearLayout private var reloadLayout: TextView constructor(itemView: View) : super(itemView) { this.loadingLayout = itemView.loading this.reloadLayout = itemView.reload this.reloadLayout.setOnClickListener{ update(LOAD_STATE_LOADING) } } fun update(state: Int){ //Initialize load state loadingLayout.visibility = View.GONE reloadLayout.visibility = View.GONE when(state) { LOAD_STATE_NORMAL -> { loadingLayout.visibility = View.GONE reloadLayout.visibility = View.GONE } LOAD_STATE_LOADING -> { loadingLayout.visibility = View.VISIBLE //Simulation data loading is also required startLoadMore(this) } LOAD_STATE_RELOAD -> { reloadLayout.visibility = View.VISIBLE //Click event needs to be responded here } } } private fun startLoadMore(callBack: LoadMoreViewHolder) { onRefreshListener?.onUpPullOnRefresh(callBack) } } interface OnRefreshListener{ fun onUpPullOnRefresh(callBack: LoadMoreViewHolder) } fun setOnRefreshListener(listener: OnRefreshListener) { this.onRefreshListener = listener } }
Among them,
(1) When getItemViewType gets the item type, the last item is set to load more states
(2) There are three internal states in loadmoreviewholder, indicating the Loading process, Normal indicating that the Loading has been completed, Loading indicating that the Loading is in progress, reload indicating that the Loading has failed.
The internal update() function indicates the update status and adjusts the load interface display (visible)
(3) To create a failed loading control, you need to add click listening to make it enter the loading state again.
(4) When Binding ViewHolder, the update status is Loading, and then simulate data update through callback. Activity registers to listen (setOnRefreshListener) and implements the callback
That is, the adapter provides a refresh interface, registers a refresh interface, and a refresh callback
private fun startLoadMore(callBack: LoadMoreViewHolder) { onRefreshListener?.onUpPullOnRefresh(callBack) }
4. Activity registration refresh listening
private fun initListener() { // Anonymous Inner Class loadMoreNormalAdapter.setOnRefreshListener(object : ListViewLoadMoreNormalAdapter.OnRefreshListener { // You also need to hold a reference to adapter viewholder to simulate a failed load state override fun onUpPullOnRefresh(callBack: ListViewLoadMoreNormalAdapter.LoadMoreViewHolder) { randomAddDatas(callBack) } }) }
(1) Where random add data means to simulate random add data, and then update the UI,
(2) When the new random number is 0, the ViewHolder status is set to reload, indicating that the loading failed, and the last entry status will be updated to reload.
Activity full code
class ListViewLoadMoreNormalActivity : AppCompatActivity() { private lateinit var loadMoreNormalAdapter: ListViewLoadMoreNormalAdapter private var datas = mutableListOf<UserDataLoadMore>() var random = Random(PIC_IDS.size) private lateinit var context: Context override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_load_more) context = this initDatas() loadMoreNormalAdapter = ListViewLoadMoreNormalAdapter(datas) initListener() showListView() } private fun initListener() { // Try using lazy expressions instead of anonymous inner classes loadMoreNormalAdapter.setOnRefreshListener(object : ListViewLoadMoreNormalAdapter.OnRefreshListener { // You also need to hold a reference to adapter viewholder to simulate a failed load state override fun onUpPullOnRefresh(callBack: ListViewLoadMoreNormalAdapter.LoadMoreViewHolder) { randomAddDatas(callBack) } }) } private fun showListView() { //2. Create and set up layout manager var layoutManager = LinearLayoutManager(context) layoutManager.orientation = LinearLayoutManager.VERTICAL recycler_view.layoutManager = layoutManager // 3. Create and set adapter recycler_view.adapter = loadMoreNormalAdapter } private fun initDatas() { datas.clear() addDatas(PIC_IDS.size) } private fun addDatas(addSize: Int) { if (addSize < 0) { return } if (addSize == 0) { } for (index in 0 until addSize) { var userData = UserDataLoadMore( context.resources.getString(CommonConstants.USER_NAMES[index % PIC_IDS.size]), PIC_IDS[index % PIC_IDS.size] ) datas.add(userData) } } fun randomAddDatas(callBack: ListViewLoadMoreNormalAdapter.LoadMoreViewHolder) { var handler = Handler() handler.postDelayed({ //Add data randomly - random starts from 1 every time, so you can't define random here (there will be 0 as well) // When 0, it is judged as failure??? var newSize = random.nextInt(PIC_IDS.size) Toast.makeText(context, "newSize = $newSize", Toast.LENGTH_SHORT) .show() if (newSize == 0) { callBack.update(callBack.LOAD_STATE_RELOAD) } else { addDatas(newSize) loadMoreNormalAdapter.notifyDataSetChanged() } }, 3000) } }
In addition, pull-up refresh can add an addonscrolllistener to the RecyclerView, and then update the simulation data through a callback,
It's more complicated. Further summary