内容
TabLayoutとPagerの組み合わせはAndroidではお約束だが、Tab(TabRow, TabColumn)とPager(HorizontalPager, VerticalPager)それぞれのComposableにはページが変更された、といういい感じの関数をセットする方法がない。どうすればよいか。
対応
Tab側
TabはTabコンポサーブル自身がonClickを受け取るようになっているのでそれを使う。タブが再選択されたとかは自分で制御するしかなさそうだ。
// 実装例 TabRow(){ Tab(onClick = { // タブがクリックされた } ) }
Pager側
Pager側のイベント検知はPagerStateの状態変化をウォッチすることで行う。
val pagerState: PagerState = rememberPagerState(initialPage) { pageCount } LaunchedEffect(pagerState) { snapshotFlow { pagerState.currentPage }.collect { page -> // ページが(スワイプなどで)変更された } }
リファクタリング(Tab)
TabはTabが選択されたのと再選択を検知できるようにしてみる
// 適当。大体こんな感じで @Composable TabRowWrapper( pagerState: PagerState, onTabSelect: (Int) -> Unit = {}, onTabReselect: (Int) -> Unit = {}, ){ TabRow(indicator = { tabPositions -> TabRowDefaults.Indicator(Modifier.pagerTabIndicatorOffset(pagerState, tabPositions)) }){ Tab(onClick = { if (pagerState.currentPage == index) { onTabReselect(index) } else { onTabSelect(index) } } ) } }
呼び出し側でpagerState.animateScrollToPageしてあげる。
まあこの処理自体はTabRowWrapper
に直接書いてもいいかもしれない
TabRowWrapper( onTabSelect = { position -> coroutineScope.launch { pagerState.animateScrollToPage(position) } } )
リファクタリング(Pager)
各ScreenのComposableに上記を記述するのは 面倒なのでPagerStateを生成する処理をまとめて呼べるようにしてみる
@Composable fun createPagerState( initialPage: Int = 0, onTabChange: (Int) -> Unit = {}, pageCount: Int = 0 ): PagerState { val pagerState: PagerState = rememberPagerState(initialPage) { pageCount } LaunchedEffect(pagerState) { snapshotFlow { pagerState.currentPage }.collect { onTabChange(it) } } return pagerState }
これで呼び出し側は1行で書けるようになった。
val pagerState: PagerState = createPagerState(0, onTabChange, pageCount)