Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jetpack ComposeとPaging3で実現する Endless Grid Design

Jetpack ComposeとPaging3で実現する Endless Grid Design

以下のイベントで発表した資料になります。

YUMEMI.grow Mobile #9
https://yumemi.connpass.com/event/302462/

potatotips #83 iOS/Android開発Tips共有会
https://potatotips.connpass.com/event/287244/

akkiee76

July 21, 2023
Tweet

More Decks by akkiee76

Other Decks in Technology

Transcript

  1. YUMEMI.grow Mobile #9 ページングライブラリの主な機能 • ページングデータに対するメモリ内キャッシュ • リクエスト重複排除 • 画面スクロールに連動した際に自動的なリクエスト

    • ページングの状態管理 • Coroutine、Flow、LiveData、RxJava に対応 リソースを効率的にしたページングデータの操作を実現 5
  2. YUMEMI.grow Mobile #9 ページングライブラリの主なコンポーネント • PagingData • Pager • PagingSource

    ◦ 特定のページクエリのデータチャンクを読み込むためのクラス • PagingConfig • LazyPagingItems 9
  3. YUMEMI.grow Mobile #9 ページングライブラリの主なコンポーネント • PagingData • Pager • PagingSource

    • PagingConfig ◦ ページング動作を決定するパラメータを定義するクラス • LazyPagingItems 10
  4. YUMEMI.grow Mobile #9 ページングライブラリの主なコンポーネント • PagingData • Pager • PagingSource

    • PagingConfig • LazyPagingItems ◦ PagingData のフローから取得したデータを表示するためのクラス 11
  5. YUMEMI.grow Mobile #9 サンプルアプリ 主に使用しているライブラリなど • paging 3 • paging-compose

    3 • coil • Unsplash Image API https://github.com/akkie76/Paging-App-Sample 12
  6. YUMEMI.grow Mobile #9 Repository class UnsplashSearchRepositoryImpl @Inject constructor( private val

    service: UnsplashService ): UnsplashSearchRepository { override fun searchPhotos(query: String): Flow<PagingData<UnsplashPhoto>> { return Pager( config = PagingConfig(enablePlaceholders = false, pageSize = PAGE_SIZE), pagingSourceFactory = { UnsplashPagingSource(service, query) } ).flow } companion object { private const val PAGE_SIZE = 25 } } 15
  7. YUMEMI.grow Mobile #9 Repository class UnsplashPagingSource( private val service: UnsplashService,

    private val query: String ) : PagingSource<Int, UnsplashPhoto>() { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, UnsplashPhoto> { val page = params.key ?: UNSPLASH_STARTING_PAGE_INDEX return try { val response = service.searchPhotos(query, page, params.loadSize) val photos = response.results LoadResult.Page( data = photos, prevKey = if (page == UNSPLASH_STARTING_PAGE_INDEX) null else page - 1, nextKey = if (page == response.totalPages) null else page + 1 ) } catch (exception: Exception) { LoadResult.Error(exception) } } 16
  8. YUMEMI.grow Mobile #9 ViewModel @HiltViewModel class MainViewModel @Inject constructor( searchRepository:

    UnsplashSearchRepository ): ViewModel() { val photos = searchRepository.searchPhotos(QUERY).cachedIn(viewModelScope) companion object { private const val QUERY = "fruit" } } 18
  9. YUMEMI.grow Mobile #9 UI Layer @Composable fun MainScreen(viewModel: MainViewModel) {

    Scaffold { padding -> val lazyPagingItems = viewModel.photos.collectAsLazyPagingItems() LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 128.dp) ) { items( count = lazyPagingItems.itemCount, key = lazyPagingItems.itemKey() ) { index -> val photo = lazyPagingItems[index] ?: return@items AsyncImage( model = photo.urls.small, contentDescription = null, modifier = Modifier.fillMaxWidth().height(128.dp), contentScale = ContentScale.FillWidth ) } } 20
  10. YUMEMI.grow Mobile #9 ページングライブラリの状態管理 CombinedLoadStates 型で「読み込み状態」にアクセスすることが可能 • LoadStates.append ◦ ユーザーの現在位置より後に取得されるアイテムの

    LoadState • LoadStates.prepend ◦ ユーザーの現在位置より前に取得されるアイテムの LoadState • LoadStates.refresh ◦ 初期読み込みの LoadState 22
  11. YUMEMI.grow Mobile #9 ページングライブラリの状態管理 LoadState から取得できる状態 • LoadState.Loading ◦ アイテムを読み込み中

    • LoadState.NotLoading ◦ アイテムを読み込んでいない(初期状態も含まれる) • LoadState.Error ◦ 読み込みエラーが発生 23
  12. YUMEMI.grow Mobile #9 UI Layer(CircularProgress) when (lazyPagingItems.loadState.refresh) { is LoadState.Loading

    -> { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { Text( modifier = Modifier.padding(8.dp), text = "Loading" ) CircularProgressIndicator( modifier = Modifier.padding(top = 8.dp), color = Color.LightGray ) } } else -> {} } 24