paging 3
api
pageOffset是頁碼,pageSize是每頁大小。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| import com.example.coroutine.repository.ArticleList
import retrofit2.http.GET
import retrofit2.http.Query
interface ArticleApi {
@GET("article/list")
suspend fun list(@Query("pageOffset") pageOffset: Int,
@Query("pageSize") pageSize: Int): ArticleList
companion object {
val retrofit: ArticleApi by lazy {
RetrofitUtil.createService(ArticleApi::class.java)
}
}
}
|
PagingSource
第1個泛型參數Int是固定。
第2個泛型參數為回傳的物件的類別。
PagingSource<Int, ArticleItem>()
- prevKey 是上一頁,若是第1頁,它的上一頁是 null(沒有上一頁)。
- nextKey 是下一頁,若資料為空,就傳null(沒有下一頁)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.example.coroutine.api.ArticleApi
import com.example.coroutine.repository.ArticleItem
class ArticlePagingSource : PagingSource<Int, ArticleItem>() {
override fun getRefreshKey(state: PagingState<Int, ArticleItem>): Int? {
TODO("Not yet implemented")
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ArticleItem> {
// 若params.key 為空,設為第1頁
val page = params.key ?: 1
val pageSize = params.loadSize
return try {
val response = ArticleApi.retrofit.list(page, pageSize)
LoadResult.Page(
data = response.data,
prevKey = if (page == 1) null else page - 1,
nextKey = if (response.data.isEmpty()) null else page + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
|
ViewModel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.example.coroutine.paging.ArticlePagingSource
import com.example.coroutine.repository.ArticleItem
import kotlinx.coroutines.flow.Flow
class ArticleViewModelPage : ViewModel() {
private val articles by lazy {
Pager(
config = PagingConfig(pageSize = 10),
pagingSourceFactory = { ArticlePagingSource() }
).flow.cachedIn(viewModelScope)
}
fun loadArticle(): Flow<PagingData<ArticleItem>> = articles
}
|
PageAdapter
使用getItem()取得資料。
1
| val item = getItem(position)
|
繼承PagingDataAdapter,建構子參數傳入DiffUtil。
: PagingDataAdapter<ArticleItem, MyViewHolder>(DiffUtil)
DiffUtil 用來比對資料是否有更新,比對內容是否相同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import com.example.coroutine.databinding.ArticleItemBinding
import com.example.coroutine.repository.ArticleItem
class ArticlePageAdapter :
PagingDataAdapter<ArticleItem, MyViewHolder>(
object : DiffUtil.ItemCallback<ArticleItem>() {
override fun areItemsTheSame(
oldItem: ArticleItem,
newItem: ArticleItem
): Boolean {
return oldItem.title == newItem.title
}
override fun areContentsTheSame(
oldItem: ArticleItem,
newItem: ArticleItem
): Boolean {
return oldItem == newItem
}
}) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): MyViewHolder {
val binding = ArticleItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = getItem(position)
item?.let{
val binding = holder.binding as ArticleItemBinding
binding.itemTv.text = "${item.title} "
}
}
}
|
ViewHolder
1
2
3
4
5
6
| import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
class MyViewHolder(val binding: ViewBinding) :
RecyclerView.ViewHolder(binding.root) {
}
|
Activity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.coroutine.adapter.ArticlePageAdapter
import com.example.coroutine.adapter.LoadMoreAdapter
import com.example.coroutine.databinding.ActivityPageBinding
import com.example.coroutine.model.ArticleViewModelPage
import kotlinx.coroutines.launch
class PageActivity : AppCompatActivity() {
private val articleViewModelPage by viewModels<ArticleViewModelPage>()
private lateinit var binding : ActivityPageBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPageBinding.inflate(layoutInflater)
setContentView(binding.root)
val articlePageAdapter = ArticlePageAdapter()
binding.recyleView.layoutManager = LinearLayoutManager(this)
binding.recyleView.adapter = articlePageAdapter
lifecycleScope.launch {
articleViewModelPage.loadArticle().collect { value ->
articlePageAdapter.submitData(value)
}
}
}
}
|
xml
ViewHolder article_item.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/item_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
|
Activity RecyclerView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.PageActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyleView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
|
LoadMore
LoadMoreAdapter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.LoadState
import androidx.paging.LoadStateAdapter
import com.example.coroutine.databinding.LoadMoreBinding
class LoadMoreAdapter : LoadStateAdapter<MyViewHolder>() {
override fun onBindViewHolder(
holder: MyViewHolder,
loadState: LoadState
) {
}
override fun onCreateViewHolder(
parent: ViewGroup,
loadState: LoadState
): MyViewHolder {
val binding = LoadMoreBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Loading ......."
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar" />
</androidx.constraintlayout.widget.ConstraintLayout>
|
Activity
adapter換成withLoadStateFooter()
1
2
| // 換成withLoadStateFooter
binding.recyleView.adapter = articlePageAdapter.withLoadStateFooter(LoadMoreAdapter())
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.coroutine.adapter.ArticlePageAdapter
import com.example.coroutine.adapter.LoadMoreAdapter
import com.example.coroutine.databinding.ActivityPageBinding
import com.example.coroutine.model.ArticleViewModelPage
import kotlinx.coroutines.launch
class PageActivity : AppCompatActivity() {
private val articleViewModelPage by viewModels<ArticleViewModelPage>()
private lateinit var binding : ActivityPageBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPageBinding.inflate(layoutInflater)
setContentView(binding.root)
val articlePageAdapter = ArticlePageAdapter()
binding.recyleView.layoutManager = LinearLayoutManager(this)
// 換成withLoadStateFooter
binding.recyleView.adapter = articlePageAdapter.withLoadStateFooter(LoadMoreAdapter())
lifecycleScope.launch {
articleViewModelPage.loadArticle().collect { value ->
articlePageAdapter.submitData(value)
}
}
}
}
|