2024年10月23日水曜日

AutoScrollHorizontalPager を作る

  • (擬似的に)無限ループしたい
  • タップされているときは自動送りしない
  1. private const val PAGE_COUNT = 10_000  
  2. private const val INITIAL_PAGE = PAGE_COUNT / 2  
  3.   
  4. private fun Int.floorMod(other: Int): Int = when (other) {  
  5.     0 -> this  
  6.     else -> this - this.floorDiv(other) * other  
  7. }  
  8.   
  9. @Composable  
  10. fun AutoScrollHorizontalPager(  
  11.     itemSize: Int,  
  12.     modifier: Modifier = Modifier,  
  13.     autoScrollDuration: Long = 2_000L,  
  14.     onPageChanged: ((page: Int) -> Unit)? = null,  
  15.     pageContent: @Composable PagerScope.(page: Int) -> Unit,  
  16. ) {  
  17.     val pagerState = rememberPagerState(INITIAL_PAGE) { PAGE_COUNT }  
  18.   
  19.     fun Int.toIndex(): Int = (this - INITIAL_PAGE).floorMod(itemSize)  
  20.   
  21.     if (onPageChanged != null) {  
  22.         LaunchedEffect(onPageChanged) {  
  23.             snapshotFlow { pagerState.currentPage }  
  24.                 .collect { page -> onPageChanged(page.toIndex()) }  
  25.         }  
  26.     }  
  27.   
  28.     val dragged by pagerState.interactionSource.collectIsDraggedAsState()  
  29.     if (!dragged) {  
  30.         LaunchedEffect(Unit) {  
  31.             while (true) {  
  32.                 delay(autoScrollDuration)  
  33.   
  34.                 val nextPage = pagerState.currentPage + 1  
  35.                 if (nextPage < PAGE_COUNT) {  
  36.                     pagerState.animateScrollToPage(nextPage)  
  37.                 } else {  
  38.                     pagerState.scrollToPage(0)  
  39.                 }  
  40.             }  
  41.         }  
  42.     }  
  43.   
  44.     HorizontalPager(  
  45.         state = pagerState,  
  46.         modifier = modifier,  
  47.     ) { page ->  
  48.         pageContent(page.toIndex())  
  49.     }  
  50. }