pager auto swipe with compose

강현석·2023년 3월 7일
0

troubleshooting

목록 보기
4/7

배경

  • Compose로 Pager를 사용하여, 배너가 3초 후 자동으로 스와이프되는 기능을 개발
  • Pager 라이브러리는 accompanist-pager 사용

현상

  • 다음 페이지로 이동 시 아래와 같은 이미지가 될 것으로 예상
  • 하지만, 자동 스와이프가 완료됐음에도 불구하고 이전 배너가 일부 노출됨

코드

  • 가이드 문서에 따라 pagerState.animateScrollToPager(index) 함수 사용
  • LaunchedEffect를 활용하여 3초 후 자동 스와이프
    LaunchedEffect(key1 = pagerState.currentPage) {
        delay(3_000)
        pagerState.animateScrollToPage(pagerState.currentPage.inc())
    }

분석

  • anomateScrollToPage(index) 함수의 구현부를 살펴보자
suspend fun animateScrollToPage(
    @IntRange(from = 0) page: Int,
    @FloatRange(from = -1.0, to = 1.0) pageOffset: Float = 0f,
) {
    ...
	try {
        ...
    } finally {
        onScrollFinished()    
    }        
  • onScrollFinished() 함수에 아래와 같이 주석이 작성되어 있음

    We need to manually call this, as the animateScrollToItem call above will happen in 1 frame, which is usually too fast for the LaunchedEffect in Pager to detect the change. This is especially true when running unit tests.

    • 해석하자면, animateScrollToItem(index)는 1프레임에서 발생되므로 수동으로 호출하는 것을 권장하고, Pager의 변경 사항을 LaunchedEffect가 감지하기 너무 빠르다고 함
  • 정리하자면, LaunchedEffect에 Pager와 관련된 것을 Key로 사용하면 감지가 너무 빨라서 animateScrollToItem(index)가 끝나기 전에 다음 LaunchedEffect가 실행됨

해결

val isDraggedState: State<Boolean> =
	pagerState.interactionSource.collectIsDraggedAsState()
LaunchedEffect(key1 = isDraggedState) {
	snapshotFlow { isDraggedState.value }
    	.collectLatest { isDragged ->
    		if (isDragged) return@collectLatest
	        while (true) {
    	    	delay(3_000)
           		pagerState.animateScrollToPage(pagerState.currentPage.inc())
	        }
    	}
}
  • Pager의 drag 상태를 감지할 수 있음
  • LaunchedEffect가 빠르게 감지하더라도 dragged 상태라면 자동 스와이프가 동작하지 않도록 함

ref. https://github.com/google/accompanist/issues/1069

profile
볼링을 좋아하는 안드로이드 개발자

0개의 댓글