당겨서 새로고침

새로고침을 당기기 구성요소를 사용하면 사용자가 앱 콘텐츠의 시작 부분에서 아래로 드래그하여 데이터를 새로고침할 수 있습니다.

API 노출 영역

스크롤 가능한 콘텐츠의 컨테이너 역할을 하는 풀투리프레시를 구현하려면 PullToRefreshBox 컴포저블을 사용하세요. 다음 주요 매개변수는 새로고침 동작 및 모양을 제어합니다.

  • isRefreshing: 새로고침 작업이 현재 진행 중인지 여부를 나타내는 불리언 값입니다.
  • onRefresh: 사용자가 새로고침을 시작할 때 실행되는 람다 함수입니다.
  • indicator: 풀업 새로고침에 그려지는 표시기를 맞춤설정합니다.

기본 예

이 스니펫은 PullToRefreshBox의 기본 사용법을 보여줍니다.

@Composable
fun PullToRefreshBasicSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

코드 관련 핵심 사항

  • PullToRefreshBox는 문자열 목록을 표시하는 LazyColumn를 래핑합니다.
  • PullToRefreshBox에는 isRefreshingonRefresh 매개변수가 필요합니다.
  • PullToRefreshBox 블록 내의 콘텐츠는 스크롤 가능한 콘텐츠를 나타냅니다.

결과

이 동영상에서는 위의 코드에서 기본 풀-투-리프레시 구현을 보여줍니다.

그림 1 항목 목록의 기본 스크롤하여 새로고침 구현입니다.

고급 예: 표시기 색상 맞춤설정

@Composable
fun PullToRefreshCustomStyleSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            Indicator(
                modifier = Modifier.align(Alignment.TopCenter),
                isRefreshing = isRefreshing,
                containerColor = MaterialTheme.colorScheme.primaryContainer,
                color = MaterialTheme.colorScheme.onPrimaryContainer,
                state = state
            )
        },
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

코드 관련 핵심 사항

  • 표시기 색상은 indicator 매개변수의 containerColorcolor 속성을 통해 맞춤설정됩니다.
  • rememberPullToRefreshState()는 새로고침 작업의 상태를 관리합니다. 이 상태는 indicator 매개변수와 함께 사용합니다.

결과

이 동영상에서는 색상 표시기가 있는 풀업 새로고침 구현을 보여줍니다.

그림 2. 맞춤 스타일을 사용한 풀투리프레시 구현

고급 예: 완전히 맞춤설정된 표시기 만들기

기존 컴포저블과 애니메이션을 활용하여 복잡한 맞춤 표시기를 만들 수 있습니다. 이 스니펫은 풀-투-리프레시 구현에서 완전히 맞춤설정된 표시기를 만드는 방법을 보여줍니다.

@Composable
fun PullToRefreshCustomIndicatorSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            MyCustomIndicator(
                state = state,
                isRefreshing = isRefreshing,
                modifier = Modifier.align(Alignment.TopCenter)
            )
        }
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

// ...
@Composable
fun MyCustomIndicator(
    state: PullToRefreshState,
    isRefreshing: Boolean,
    modifier: Modifier = Modifier,
) {
    Box(
        modifier = modifier.pullToRefreshIndicator(
            state = state,
            isRefreshing = isRefreshing,
            containerColor = PullToRefreshDefaults.containerColor,
            threshold = PositionalThreshold
        ),
        contentAlignment = Alignment.Center
    ) {
        Crossfade(
            targetState = isRefreshing,
            animationSpec = tween(durationMillis = CROSSFADE_DURATION_MILLIS),
            modifier = Modifier.align(Alignment.Center)
        ) { refreshing ->
            if (refreshing) {
                CircularProgressIndicator(Modifier.size(SPINNER_SIZE))
            } else {
                val distanceFraction = { state.distanceFraction.coerceIn(0f, 1f) }
                Icon(
                    imageVector = Icons.Filled.CloudDownload,
                    contentDescription = "Refresh",
                    modifier = Modifier
                        .size(18.dp)
                        .graphicsLayer {
                            val progress = distanceFraction()
                            this.alpha = progress
                            this.scaleX = progress
                            this.scaleY = progress
                        }
                )
            }
        }
    }
}

코드 관련 핵심 사항

  • 이전 스니펫에서는 라이브러리에서 제공하는 Indicator를 사용했습니다. 이 스니펫은 MyCustomIndicator라는 맞춤 표시기 컴포저블을 만듭니다. 이 컴포저블에서 pullToRefreshIndicator 수정자는 새로고침 위치 지정 및 트리거를 처리합니다.
  • 이전 스니펫과 마찬가지로 PullToRefreshState 인스턴스가 추출되었으므로 동일한 인스턴스를 PullToRefreshBoxpullToRefreshModifier 모두에 전달할 수 있습니다.
  • 컨테이너 색상과 위치 기준점은 PullToRefreshDefaults 클래스에서 사용합니다. 이렇게 하면 Material 라이브러리의 기본 동작과 스타일을 재사용하면서 관심 있는 요소만 맞춤설정할 수 있습니다.
  • MyCustomIndicatorCrossfade를 사용하여 클라우드 아이콘과 CircularProgressIndicator 간에 전환합니다. 클라우드 아이콘은 사용자가 당기면 확대되고 새로고침 작업이 시작되면 CircularProgressIndicator로 전환됩니다.
    • targetStateisRefreshing를 사용하여 표시할 상태 (구름 아이콘 또는 원형 진행률 표시기)를 결정합니다.
    • animationSpec는 지정된 재생 시간 CROSSFADE_DURATION_MILLIS로 전환을 위한 tween 애니메이션을 정의합니다.
    • state.distanceFraction는 사용자가 아래로 당긴 거리를 나타내며 0f (당기지 않음)에서 1f (완전히 당김)까지입니다.
    • graphicsLayer 수정자는 크기와 투명도를 수정합니다.

결과

이 동영상에서는 위의 코드에 있는 맞춤 표시기가 나와 있습니다.

그림 3. 맞춤 표시기가 있는 풀-투-리프레시 구현

추가 리소스