Kotlin_RecyclerView_ Pull up refresh 1

Keywords: Android xml encoding

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

Posted by jeroom on Fri, 26 Jun 2020 20:09:57 -0700