본문 바로가기

안드로이드

Android - MovableContentOf in Compose

반응형

최근 Stable로 올라온 Compose 1.2 버전에서
movableContentOfmovableContentWithReceiverOf 라는 새로운 두 함수가 추가되었다.

 

기존에는 동일한 Content이라도 부모가 바뀌게 되면 기존의 Content 없애고 새로운 Content을 구성하는 형태로 동작하였다.
이제는 movableContentOf 을 사용하여 기존의 Content를 재구성 없이 그대로 이동시킬 수 있고, 재구성에 필요한 불필요한 비용 줄일 수 있다.

사용 방법은 mutableStateOf 와 동일하게 remember 로 감싸주면 되고 내부에 Content를 넣어주면 된다.

1.  movableContentOf

@Composable
fun MovableContentTest() {
    val boxs = remember {
        listOf(
            "A" to Color.Blue,
            "B" to Color.DarkGray,
            "C" to Color.Magenta,
            "D" to Color.Green
        ).map { (text, color) ->
            movableContentOf {
                Log.d("two22", "movableContentOf $text")
                Box(
                    modifier = Modifier
                        .padding(20.dp)
                        .size(80.dp)
                        .background(color, RoundedCornerShape(percent = 25)),
                    contentAlignment = Alignment.Center
                ) {
                    Text(
                        text = text,
                        fontSize = 40.sp
                    )
                }
            }
        }
    }

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        val position = remember { mutableStateListOf(0, 1, 2, 3) }

        Button(onClick = {
            position.shuffle()
        }) {
            Text("Shuffle")
        }

        Row {
            boxs[position[0]]()
            boxs[position[1]]()
        }
        Row {
            boxs[position[2]]()
            boxs[position[3]]()
        }
    }
}

movableContentOf 을 이용해서 4개의 box을 하나의 boxs 변수에 담았다.

그리고 생성될때마다 로그를 찍도록 하고, shuffle 이 될 때에는 다른 곳으로 이동하도록 만들었다.

 

로그를 확인하면 제일 초기 생성에서만 로그가 찍혔고, Content 가 이동되고 있을 때는 아무런 로그도 찍히지 않았다.

 

2. movableContentWithReceiverOf

movableContentWithReceiverOf 의 사용법은 movableContentOf 와 동일하고 추가로 사용 영역을 제한할 수 있다.

@Composable
fun MovableContentWithReceiverTest() {
    val boxs = remember {
        listOf(
            "A" to Color.Blue,
            "B" to Color.DarkGray,
            "C" to Color.Magenta,
            "D" to Color.Green
        ).map { (text, color) ->
            movableContentWithReceiverOf<RowScope> {
                Log.d("two22", "movableContentOf $text")
                Box(
                    modifier = Modifier
                        .weight(1f)
                        .padding(20.dp)
                        .size(80.dp)
                        .background(color, RoundedCornerShape(percent = 25)),
                    contentAlignment = Alignment.Center
                ) {
                   ...
                }
            }
        }
    }

    ...
}

movableContentOf 를 사용하던 곳을 movableContentWithReceiverOf 로 바꾸고,

RowScope 에서만 사용 가능하도록 제한을 걸었다.

 

RowScope 로 제한했기 때문에 RowScope 에 정의된 weight 함수를 호출할 수 있게 된다.

 

동일하게 로그를 찍어보면 이전과 다르게 로그가 계속해서 찍히는 걸 볼 수 있다.

 

movableContentWithReceiverOf 의 차이가 아닌 weight 함수를 호출했기 때문이다.

 

Content의 이동으로 인해 부모가 바뀌었고, 부모의 영향을 받는 weight 함수는 다시 실행될 필요가 있다.

movableContentWithReceiverOf 로 최적화를 하려면 부모 scope의 modifier 직접 사용하지 않고,
아래 방법을 사용하는게 좋다.

 

3. movableContentWithReceiverOf parameter

movableContentWithReceiverOf 는 인자로 최대 3개의 parameter를 넘길 수 있다

@Composable
fun MovableContentWithReceiverTest() {
    val boxs = remember {
        listOf(
           ...
        ).map { (text, color) ->
            movableContentWithReceiverOf<RowScope, Modifier> { modifier ->
                Log.d("two22", "movableContentOf $text")
                Box(
                    modifier = modifier
                        .padding(20.dp)
                        .size(80.dp)
                        .background(color, RoundedCornerShape(percent = 25)),
                    contentAlignment = Alignment.Center
                ) {
                    ...
                }
            }
        }
    }

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        ...

        Row {
            boxs[position[0]](Modifier.weight(1f))
            boxs[position[1]](Modifier.weight(1f))
        }
        Row {
            boxs[position[2]](Modifier.weight(1f))
            boxs[position[3]](Modifier.weight(1f))
        }
    }
}

위에 코드는 Modifier를 호출 paremeter에 담아서 넘겨주는 방식으로 작성한 코드이다.

 

이렇게 하면 부모가 변경되어도 Content를 재구성하지 않고 이동시키는 것이 가능하다.

Row {
    boxs[position[0]](Modifier.weight(1f))
    boxs[position[1]](Modifier.align(Alignment.CenterVertically))
}
Row {
    boxs[position[2]](Modifier.background(Color.Black))
    boxs[position[3]](Modifier.weight(1f))
}

대신 위에 처럼 Modifier 가 각각 다르고 이걸로 인해 다시 그려져야 하는 뷰들은 재구성이 진행된다.

반응형

'안드로이드' 카테고리의 다른 글

Android - Compose 1.3 PullRefresh  (0) 2022.11.01
Android - Compose 1.2 바뀐 것  (0) 2022.08.11
Compose 1.1 정리  (0) 2022.02.16
compose 넣은 xml 불러오면 에러날 때  (2) 2021.06.22
오픈소스 라이선스 만들기  (0) 2021.02.23