實現上拉載入更多主要有3步
程式碼如下(範例):
class MyAdapter(val data : List<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
private val footView = 1 // 定義變數,對應不同的情況
private val normalView = 0
private val footStart = 2
private val footEnd = 3
private val footNoMore = 4
private var footState = 0
class MyVH(view: View):RecyclerView.ViewHolder(view) { // 普通的ViewHolder
val textNormal = view.findViewById<TextView>(R.id.textView)
}
class FootVH(view: View): RecyclerView.ViewHolder(view) { // 顯示"正在載入中"介面的ViewHolder
val textFoot = view.findViewById<TextView>(R.id.tv_my_more)
val progressBar = view.findViewById<ProgressBar>(R.id.progressBar)
}
override fun getItemViewType(position: Int): Int { // 返回應該載入哪種型別的ViewHodler
if (position + 1 == itemCount) { // 說明已經滑動到最底部了
return footView
} else {
return normalView
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { // 建立不同的ViewHodler,但是都是繼承自RecyclerView.ViewHolder
if (viewType == normalView) {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false) // 正常顯示內容的 view
return MyVH(view)
}else {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_more, parent, false) // footView
return FootVH(view)
}
}
override fun getItemCount(): Int { // 返回資料個數加1,因為多一個用來顯示 「正在載入中」介面的item
return data.size + 1
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is MyVH) {
holder.textNormal.text = data[position]
} else if (holder is FootVH) {
if (footState == 2) { // 正在載入中
holder.textFoot.visibility = View.VISIBLE
holder.progressBar.visibility = View.VISIBLE
}else if (footState == 3) { // 載入結束
holder.textFoot.visibility = View.GONE
holder.progressBar.visibility = View.GONE
}else if (footState == 4) { // 沒有更多資料可以載入了
holder.progressBar.visibility = View.GONE
holder.textFoot.text = "沒有更多資料啦"
}
}
}
fun setFootState(state: Int) { // 設定不同的 footState
footState = state
notifyDataSetChanged() // 更新 Adapter 資料
}
}
定義一個抽象類,減少Activity中對RecyclerView的監聽程式碼
程式碼如下(範例):
abstract class EndRecyclerOnScrollListener: RecyclerView.OnScrollListener(){
private var flag = 0
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
val layout = recyclerView.layoutManager as LinearLayoutManager
val lastPositionCompletely = layout.findLastCompletelyVisibleItemPosition()
if (lastPositionCompletely == layout.itemCount - 1 && flag == 0) {
loadMore()
}
}
abstract fun loadMore()
fun setFlag(flag: Int) { // 設定標記防止多次向上滑動,多次呼叫 loadMore()
this.flag = flag
}
}
class MainActivity : AppCompatActivity() {
private val data = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.layoutManager = LinearLayoutManager(this)
getData()
val adapter = MyAdapter(data)
recyclerView.adapter = adapter
recyclerView.addOnScrollListener(object : EndRecyclerOnScrollListener() {// 匿名內部類實現介面
override fun loadMore() { // 具體獲取資料的邏輯
setFlag(1) // 設定flag = 1,向上滑動,監聽事件會繼續觸發,但是不會繼續載入資料
adapter.setFootState(2) // 設定 FootView 的初始狀態
Timer().schedule(object : TimerTask() { // 延時執行
override fun run() {
if (adapter.itemCount < 52) {
runOnUiThread{
getData()
adapter.setFootState(3) // 載入完成
setFlag(0) // 此次資料載入完畢,設定flag = 0,以便下次資料可以載入
}
}else {
runOnUiThread {
adapter.setFootState(4) // 沒有更多資料載入了
}
}
}
}, 1000)
}
})
}
fun getData() {
var s = 'A'
for (i in 0..25) {
data.add(s.toString())
s++
}
}
}
至此已經實現了上拉載入更多的功能,不過此時如果其他 RecyclerView 也要實現上拉載入更多,就要寫許多重複程式碼在 Adapter 中,為了減少重複程式碼,下面對 Adapter 進行封裝。
class LoadMoreWrapper(private val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { // 因為不同Adapter的ViewHolder型別是不同的,但是都是繼承自RecyclerView.ViewHolder,所以泛型指定為RecyclerView.ViewHolder
private val footView = 1
private val normalView = 0
private val footStart = 2
private val footEnd = 3
private val footNoMore = 4
private var footState = 0
class FootVH(view: View) : RecyclerView.ViewHolder(view) {
val textFoot = view.findViewById<TextView>(R.id.tv_my_more)
val progressBar = view.findViewById<ProgressBar>(R.id.progressBar)
}
override fun getItemViewType(position: Int): Int {
if (position + 1 == itemCount) {
return footView
} else {
return normalView
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder{
if (viewType == normalView) {
return adapter.onCreateViewHolder(parent, viewType) // 呼叫adapter的onCreateViewHolder返會正常佈局
} else {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.item_more, parent, false)
return FootVH(view)
}
}
override fun getItemCount(): Int {
return adapter.itemCount + 1
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is FootVH) {
if (footState == 2) {
holder.textFoot.visibility = View.VISIBLE
holder.progressBar.visibility = View.VISIBLE
} else if (footState == 3) {
holder.textFoot.visibility = View.GONE
holder.progressBar.visibility = View.GONE
} else if (footState == 4) {
holder.progressBar.visibility = View.GONE
holder.textFoot.text = "沒有更多資料啦"
}
} else {
adapter.onBindViewHolder(holder, position)
}
}
fun setFootState(state: Int) {
footState = state
notifyDataSetChanged()
}
}
然後Adapter中的寫法就是一般的寫法了
class MyAdapter(val data : List): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
class MyVH(view: View):RecyclerView.ViewHolder(view) {
val textNormal = view.findViewById<TextView>(R.id.textView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyVH {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false)
return MyVH(view)
}
override fun getItemCount(): Int {
return data.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder as MyVH
holder.textNormal.text = data[position]
}
}
class MainActivity : AppCompatActivity() {
private val data = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.layoutManager = LinearLayoutManager(this)
getData()
val adapter = MyAdapter(data)
val loadMoreWrapper = LoadMoreWrapper(adapter) // 封裝後的Adapter
recyclerView.adapter = loadMoreWrapper
recyclerView.addOnScrollListener(object : EndRecyclerOnScrollListener() { // 新增監聽事件
override fun loadMore() { // 載入邏輯
setFlag(1) // 設定flag = 1,向上滑動,監聽事件會繼續觸發,但是不會繼續載入資料
loadMoreWrapper.setFootState(2)
Timer().schedule(object : TimerTask() {
override fun run() {
if (adapter.itemCount < 100) {
runOnUiThread{
getData()
loadMoreWrapper.setFootState(3)
setFlag(0) // 此次資料載入完畢,設定flag = 0,以便下次資料可以載入
}
}else {
runOnUiThread{
loadMoreWrapper.setFootState(4)
}
}
}
}, 1000)
}
})
}
fun getData() {
var s = 'A'
for (i in 0..25) {
data.add(s.toString())
s++
}
}
}