[Composa] 터치 - scroll

panax·2024년 1월 21일
0

Compose

목록 보기
4/4
post-thumbnail

🧊Scroll

컴포즈에서 스크롤 동작을 처리하는 방법에 대한 소개

컴포즈에서 스크롤은 다음 Modifier 속성을 사용해서 구현할 수 있다.
horizontalScroll, verticalScroll, scrollable, nestedScroll

🧊horizontalScroll

가로 스크롤을 활성화하는 속성
자식 컴포넌트가 부모 컴포넌트보다 크면, 스크롤을 할 수 있게 해준다.

@Composable
private fun HorizontalScroll() {
    Row(
        modifier = Modifier
            .size(100.dp)
            .background(color = Color.LightGray)
            .horizontalScroll(state = rememberScrollState())
    ) {
        repeat(20) {
            Text(text = "Item $it ")
        }
    }
}

🧊verticalScroll

세로 스크롤을 활성화하는 속성
방향이 세로인 점만 빼면 horizontalScroll과 동일하다.
실제로 내부 코드를 보면 둘 다 scrollable을 사용하고 있다.

@Composable
private fun VerticalScroll() {
    Column(
        modifier = Modifier
            .size(100.dp)
            .background(color = Color.LightGray)
            .verticalScroll(state = rememberScrollState())
    ) {
        repeat(20) {
            Text(text = "Item $it")
        }
    }
}

🧊scrollable

horizontalScrollverticalScroll은 내부적으로 scrollable을 사용하고 있다.

하지만 scrollable은 스크롤 애니메이션을 만들지 않는다.
내부 코드를 보면 스크롤 애니메이션스크롤 계산을 따로 하는 것을 볼 수 있다.
그래서 scrollable을 사용해도 스크롤이 되지 않는 것을 볼 수 있다.

대신 scrollable은 사용자의 스크롤 동작에 대한 자세한 정보를 얻을 수 있다.
horizontalScroll과 verticalScroll은 현재 스크롤 위치진행 상태만 확인할 수 있다.
반면 scrollable은 스크롤 동작에 대한 delta 값을 받을 수 있다.

스크롤 애니메이션은 필요없지만, 사용자의 스크롤 동작을 알아야할 때 사용할 수 있다.

@Composable
private fun ScrollableScroll() {
    var offset by remember { mutableFloatStateOf(0f) }
    val scrollState = rememberScrollableState { delta ->
        offset += delta
        delta
    }

    Column(
        modifier = Modifier
            .size(100.dp)
            .background(color = Color.LightGray)
            .scrollable(
                orientation = Orientation.Vertical,
                state = scrollState
            ),
        verticalArrangement = Arrangement.Center,
    ) {
        Text(text = "$offset")
    }
}

🧊nestedScroll

nestedScroll은 조금 특이한 속성으로 다른 스크롤의 정보를 처리할 수 있는 속성이다.

nestedScroll은 스크롤을 2가지 방법으로 처리할 수 있다.
NestedScrollConnection을 사용해서 자식 컴포넌트의 스크롤을 가로채는 방법과
NestedScrollDispatcher를 사용해서 부모 컴포넌트에서 스크롤 이벤트를 전달하는 방법이 있다.

NestedSCrollConnection을 사용하면 자식 컴포넌트가 스크롤하는 걸 알 수 있다.
이걸 활용하면 스크롤에 따라 다른 UI가 변하게 만들 수 있다.
아래 코드는 스크롤에 따라 상단에 있는 UI가 변하는 코드로 이런 식으로 활용할 수 있다.

@Composable
private fun NestedScroll() {
    var offset by remember { mutableFloatStateOf(0f) }
    val scrollState = remember {
        object : NestedScrollConnection {
            override fun onPreScroll(
                available: Offset,
                source: NestedScrollSource
            ): Offset {
                val newOffset = (offset + available.y).coerceIn(-20f, 0f)
                offset = newOffset

                return super.onPreScroll(available, source)
            }
        }
    }

    Column(
        modifier = Modifier
            .size(100.dp)
            .nestedScroll(connection = scrollState)
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(20.dp)
                .offset(y = offset.dp)
                .background(color = Color.Green),
            content = {},
        )
        VerticalScroll()
    }
}

☕NestedScrollConnection

NestedScrollConnection은 인터페이스지만, 메소드가 구현되어 있어서 필요한 메소드만 구현하면 된다.

onPreScroll
자식 컴포넌트가 스크롤 이벤트를 처리하기 전에 발생
return으로 처리에 사용한 스크롤 값을 보낼 수 있다.

만약 파라미터로 받은 스크롤 가능한 값을 전부 return하면
스크롤을 전부 처리했다고 보기 때문에 자식 컴포넌트가 스크롤되지 않는다.

onPostScroll
자식 컴포넌트가 스크롤 이벤트를 처리하고 발생
자식 컴포넌트가 사용한 스크롤 값과 남은 스크롤 값을 받을 수 있다.

onPreFling
자식 컴포넌트에서 fling 이벤트를 처리하기 전에 발생
fling 이벤트는 스크롤과 달리 짧은 순간에 발생하기 때문에 1번만 호출된다.
해당 메소드는 suspend로 처리된다.

onPostFling
자식 컴포넌트에서 fling 이벤트를 처리하고 발생

☕NestedScrollDispatcher

NestScrollDispatcher은 구현이 복잡해서 예제가 있는 공식 문서를 참고하는 걸 추천

profile
안드로이드 개발자

0개의 댓글