024. Room(DB)の内容にもっとも簡素なPagingの機能を追加してみました
developerの説明は以下
https://developer.android.com/topic/libraries/architecture/paging/v3-overview?hl=ja
依存関係追加
以下をProject Structureなどでimplementationします
Paging3を使うためにandroidx.pagingは3.0.0以上を指定してください(ここでは3.2.0)
androidx.room:room-paging
androidx.paging:paging-runtime-ktx
androidx.paging:paging-compose
追加後以下のようにバージョンを指定しなおします
[versions]
androidx-room = "2.5.2"
androidx-paging = "3.2.0"
[libraries]
androidx-room-paging = { group = "androidx.room", name = "room-paging", version.ref = "androidx-room" }
androidx-paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "androidx-paging" }
androidx-paging-runtime-ktx = { group = "androidx.paging", name = "paging-runtime-ktx", version.ref = "androidx-paging" }
実装
以下を追加
@Dao
interface DataDao {
@Query("SELECT * FROM data_table ORDER BY id ASC")
fun pagingSource(): PagingSource<Int, Data>
...
}
interface DataRepository {
fun getPagingStream(): Flow<PagingData<Data>>
...
}
class DefaultDataRepository(private val dao: DataDao) : DataRepository {
override fun getPagingStream(): Flow<PagingData<Data>> {
return Pager(
config = PagingConfig(pageSize = PAGE_SIZE, enablePlaceholders = false),
pagingSourceFactory = { dao.pagingSource() }
).flow
}
...
companion object {
const val PAGE_SIZE = 20
}
}
class DataListViewModel(private val repo: DataRepository) : ViewModel() {
val pagingDataFlow = repo.getPagingStream().cachedIn(viewModelScope)
...
}
Viewを以下のように書き換えます
LazyColumnを一番下までスクロールさせるとitemCountが20ずつ増えることが確認できると思います
LazyColumnのitemsにitems(dataList)といった設定はPaging3.2.0でできなくなってます
https://developer.android.com/jetpack/androidx/releases/paging
@Composable
fun DataListScreen(
viewModel: DataListViewModel = androidx.lifecycle.viewmodel.compose.viewModel {
val application = checkNotNull(get(ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY) as Application)
DataListViewModel(application.container.dataRepository)
},
modifier: Modifier = Modifier
) {
val dataList = viewModel.pagingDataFlow.collectAsLazyPagingItems()
val names = listOf("alpha", "bravo", "charlie", "echo")
val listState = rememberLazyListState()
// テスト用にとりあえずDBに追加
LaunchedEffect(Unit) {
repeat(100) {
val index = (0..3).random()
viewModel.insertData(Data(name = names[index]))
}
}
LaunchedEffect(listState) {
snapshotFlow { listState.layoutInfo.visibleItemsInfo.size }
.collect {
Log.d("App", "${dataList.itemCount}")
}
}
Column(modifier) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(onClick = {
viewModel.deleteAll()
}) {
Text("Cleanup")
}
}
LazyColumn(
state = listState
) {
items(
count = dataList.itemCount,
key = dataList.itemKey { it.id },
) { index ->
val data = dataList[index]
ListItem(
headlineContent = {
Text("$index. ${data?.name}(${data?.id})")
},
)
}
}
}
}
検索対応
paging使っていても検索で絞り込みできます
pagingSourceのSQLにLIKEを追加してそれに対応した引数を各個に追加
検索用のUIを追加してます
@Dao
interface DataDao {
@Query("SELECT * FROM data_table WHERE name LIKE '%' || :search || '%' ORDER BY id ASC")
fun pagingSource(search: String): PagingSource<Int, Data>
}
class DataRepository(private val dao: DataDao) {
fun getPagingSource(search: String) = dao.pagingSource(search)
}
class DataListViewModel(private val repo: DataRepository) : ViewModel() {
private val _search = MutableStateFlow("")
private val search = _search.asStateFlow().stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = "",
)
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
val pagingDataFlow = search.debounce(300.milliseconds).flatMapLatest { query ->
Pager(
config = PagingConfig(pageSize = 10, enablePlaceholders = false),
pagingSourceFactory = { repo.getPagingSource(query) }
).flow.cachedIn(viewModelScope)
}
fun search(query: String) {
_search.value = query
}
}
@Composable
fun DataListScreen(
viewModel: DataListViewModel = androidx.lifecycle.viewmodel.compose.viewModel {
val application = checkNotNull(get(ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY) as Application)
DataListViewModel(application.container.dataRepository)
},
modifier: Modifier = Modifier
) {
val dataList = viewModel.pagingDataFlow.collectAsLazyPagingItems()
val names = listOf("alpha", "bravo", "charlie", "echo")
val listState = rememberLazyListState()
// テスト用にとりあえずDBに追加
LaunchedEffect(Unit) {
repeat(100) {
val index = (0..3).random()
viewModel.insertData(Data(name = names[index]))
}
}
var query by remember { mutableStateOf("") }
Column(modifier) {
Row(
Modifier.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
TextField(value = query, onValueChange = { query = it }, Modifier.weight(1f))
Button(onClick = {
viewModel.search(query)
}) {
Text("search")
}
}
LazyColumn(
state = listState
) {
items(
count = dataList.itemCount,
key = dataList.itemKey { it.id },
) { index ->
val data = dataList[index]
ListItem(
headlineContent = {
Text("$index. ${data?.name}(${data?.id})")
},
)
}
}
}
}
Android Studio Giraffe 2022.3.1 Patch 1 built on August 17, 2023