[Android] Jetpack Compose - 2. Compose 기본사항 (2)

문승연·2023년 8월 28일
0

이 포스트는 Android 공식 홈페이지의 Jetpack Compose 튜토리얼 을 기반으로 작성되었습니다.

3. Material Design

ComposeMaterial Design 원칙을 지원하도록 빌드되었다. 따라서 Compose UI 요소가 Material Design을 구성할 수 있도록 구현한다.

Material Design 사용

이전 포스트에서 구현한 MessageCard 컴포저블의 디자인을 개선해보자.

먼저 ComposeTutorialThemeSurfaceMessageCard 함수를 래핑한다. @Preview 함수와 @setContent 함수에서도 같은 작업을 실행한다.

이렇게 하면 컴포저블이 앱 테마에 정의된 스타일을 상속하여 일관성이 보장된다.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTutorialTheme {
                Surface(modifier = Modifier.fillMaxSize()) {
                    MessageCard(Message("Android", "Jetpack Compose"))
                }
            }
        }
    }
}

@Preview
@Composable
fun PreviewMessageCard() {
    ComposeTutorialTheme {
        Surface {
            MessageCard(
                msg = Message("Colleague", "Take a look at Jetpack Compose, it's great!")
            )
        }
    }
}

색상, 서체, 도형

Material 의 색상, 서체, 도형 스타일은 각각 MaterialTheme.colors, MaterialTheme.typography, MaterialTheme.shapes 에서 가져와 적용할 수 있다.

// ...
import androidx.compose.material.Surface

@Composable
fun MessageCard(msg: Message) {
   Row(modifier = Modifier.padding(all = 8.dp)) {
       Image(
           painter = painterResource(R.drawable.profile_picture),
           contentDescription = null,
           modifier = Modifier
               .size(40.dp)
               .clip(CircleShape)
               .border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)
       )
       Spacer(modifier = Modifier.width(8.dp))

       Column {
           Text(
               text = msg.author,
               color = MaterialTheme.colors.secondaryVariant,
               style = MaterialTheme.typography.subtitle2
           )

           Spacer(modifier = Modifier.height(4.dp))

           Surface(shape = MaterialTheme.shapes.medium, elevation = 1.dp) {
               Text(
                   text = msg.body,
                   modifier = Modifier.padding(all = 4.dp),
                   style = MaterialTheme.typography.body2
               )
           }
       }
   }
}

4. 목록 및 애니메이션

메시지 목록 만들기

목록을 표현하기 위해 메시지를 2개 이상 포함하도록 레이아웃을 수정하자.

먼저 여러 메시지를 표현하는 Conversation 함수를 만든다. 여기서 LazyColumnLazyRow 를 사용하는데 이 컴포저블은 화면에 표시되는 요소만 렌더링하므로 긴 목록 등을 표시할 때 매우 효율적이다.

LazyColumn 하위 요소로 items 라는 요소가 있다. 이는 List를 매개변수로 가져오고 리스트의 각 객체 요소 Message 의 인스턴스인 message 를 가져와 처리할 수 있게 해준다.

@Composable
fun Conversation(messages: List<Message>) {
    LazyColumn {
        items(messages) { message ->
            MessageCard(message)
        }
    }
}

@Preview
@Composable
fun PreviewConversation() {
    ComposeTutorialTheme {
        Conversation(SampleData.conversationSample)
    }
}

메시지에 애니메이션 적용

긴 메시지의 전체 내용을 보여줄 때 메시지를 확장하는 애니메이션을 추가한다.

UI가 접혔는지 확장되었는지 상태를 저장하려면 상태 값을 추적해야한다. 이를 위해서는 remembermutableStateOf 함수를 사용해야한다.

remeber 함수를 사용하면 메모리에 로컬 UI 상태를 저장하고 mutableStateOf 에 전달된 값의 변경사항을 추적할 수 있다.

이 상태를 추적하고 해당 상태값이 업데이트되면 자동으로 UI가 다시 그려지는데 이를 재구성(Recomposition) 이라고 한다.

class MainActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           ComposeTutorialTheme {
               Conversation(SampleData.conversationSample)
           }
       }
   }
}

@Composable
fun MessageCard(msg: Message) {
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painter = painterResource(R.drawable.profile_picture),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .border(1.5.dp, MaterialTheme.colors.secondaryVariant, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))

        // We keep track if the message is expanded or not in this
        // variable
        var isExpanded by remember { mutableStateOf(false) }

        // We toggle the isExpanded variable when we click on this Column
        Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
            Text(
                text = msg.author,
                color = MaterialTheme.colors.secondaryVariant,
                style = MaterialTheme.typography.subtitle2
            )

            Spacer(modifier = Modifier.height(4.dp))

            Surface(
                shape = MaterialTheme.shapes.medium,
                elevation = 1.dp,
            ) {
                Text(
                    text = msg.body,
                    modifier = Modifier.padding(all = 4.dp),
                    // If the message is expanded, we display all its content
                    // otherwise we only display the first line
                    maxLines = if (isExpanded) Int.MAX_VALUE else 1,
                    style = MaterialTheme.typography.body2
                )
            }
        }
    }
}

이제 메시지를 클릭하면 isExpanded에 따라 메시지 콘텐츠의 배경을 변경할 수 있다.
clickable modifier(수정자)를 사용하여 컴포저블의 클릭 이벤트를 처리한다. 단순히 Surface의 배경색을 전환하는 대신 MaterialTheme.colors.surface 에서 MaterialTheme.colors.primary 로 또는 그 반대로 값을 점진적으로 수정하여 배경색에 애니메이션을 적용할 수 있다. 이를 위해 animateColorAsState 함수를 사용한다. 마지막으로 animateContentSize 수정자를 사용하여 메시지 컨테이너 크기에 부드럽게 애니메이션을 적용한다.

profile
"비몽(Bemong)"이라는 앱을 개발 및 운영 중인 안드로이드 개발자입니다.

0개의 댓글