[Android] JetPack Compose 4 : Surface, Scaffold, Modifier - 예제 Crane.MainScreen

이석규·2023년 7월 9일
0

이번 포스팅에서는 [Android] JetPack Compose 3 : 예제 Crane - Navigation에 이어서 Crane 프로젝트를 분석하고 입력해보며 알게된 부분들을 정리해보고자 한다.


화면을 구성하는 Compose 구조

MainScreen

MainContent를 가지는, 네비게이션의 첫번째 목적지로 설정되어 있는 MainScreen이다.

@VisibleForTesting
@Composable
fun MainScreen(
    widthSize: WindowWidthSizeClass,
    onExploreItemClicked: OnExploreItemClicked,
    onDateSelectionClicked: () -> Unit,
    mainViewModel: MainViewModel
) {
    Surface(
        modifier = Modifier.windowInsetsPadding(
            WindowInsets.navigationBars.only(WindowInsetsSides.Start + WindowInsetsSides.End)
        ),
        color = MaterialTheme.colors.primary
    ) {
        val transitionState = remember { MutableTransitionState(mainViewModel.shownSplash.value) }
        val transition = updateTransition(transitionState, label = "splashTransition")
        val splashAlpha by transition.animateFloat(
            transitionSpec = { tween(durationMillis = 100) },
            label = "splashAlpha"
        ) {
            if (it == SplashState.Shown) 1f else 0f
        }
        val contentAlpha by transition.animateFloat(
            transitionSpec = { tween(durationMillis = 300) },
            label = "contentAlpha"
        ) {
            if (it == SplashState.Shown) 0f else 1f
        }
        val contentTopPadding by transition.animateDp(
            transitionSpec = { spring(stiffness = StiffnessLow) },
            label = "contentTopPadding"
        ) {
            if (it == SplashState.Shown) 100.dp else 0.dp
        }

        Box {
            LandingScreen(
                modifier = Modifier.alpha(splashAlpha),
                onTimeout = {
                    transitionState.targetState = SplashState.Completed
                    mainViewModel.shownSplash.value = SplashState.Completed
                }
            )

            MainContent(
                modifier = Modifier.alpha(contentAlpha),
                topPadding = contentTopPadding,
                widthSize = widthSize,
                onExploreItemClicked = onExploreItemClicked,
                onDateSelectionClicked = onDateSelectionClicked,
                viewModel = mainViewModel
            )
        }
    }
}
  • Surface
  • modifier
  • Box

이 코드에서는 이렇게 3가지 설정하는 것을 살펴보자.

Surface

주로 단일 요소의 배경 모양, 색, 테두리 등을 설정하는 Composable이다. Surface를 사용해야 하는 이유는 코드를 더욱 쉽게 만들고, Material Surface를 사용함을 명시적으로 나타낸다.

해당 이미지를 구현하는 데에 surface를 쓰고 안 쓰고는 다음과 같은 차이를 불러온다.

// Surface를 사용한 경우
Surface(
    color = MaterialTheme.colors.primarySurface,
    border = BorderStroke(1.dp, MaterialTheme.colors.secondary),
    shape = RoundedCornerShape(8.dp),
    elevation = 8.dp
) {
    Text(
        text = "example",
        modifier = Modifier.padding(8.dp)
    )
}

// Surface를 사용하지 않은 경우
val shape = RoundedCornerShape(8.dp)
val shadowElevationPx = with(LocalDensity.current) { 2.dp.toPx() }
val backgroundColor = MaterialTheme.colors.primarySurface

Text(
    text = "example",
    color = contentColorFor(backgroundColor),
    modifier = Modifier
        .graphicsLayer(shape = shape, shadowElevation = shadowElevationPx)
        .background(backgroundColor, shape)
        .border(1.dp, MaterialTheme.colors.secondary, shape)
        .padding(8.dp)
)

Surface 명세에서 이외의 파라미터들을 확인해 볼 수 있다.

Scaffold

앱의 기본적인 레이아웃 구조를 정의하고 다양한 UI 구성 요소를 통합하는 데 사용된다. 일반적으로 앱의 최상위 컨테이너 역할을 하고 다음과 같은 주요 구성 요소로 구성될 수 있다.

TopAppBar: 상단의 앱 바를 나타내는 구성 요소. 주로 앱의 제목, 액션 버튼 등을 포함
BottomAppBar: 하단의 앱 바를 나타내는 구성 요소. 주로 앱의 하단 탐색 기능을 포함
FloatingActionButton: 부동 액션 버튼을 나타내는 구성 요소. 주로 액션을 트리거하는 버튼으로 사용
Drawer: 네비게이션 드로어를 나타내는 구성 요소. 주로 앱의 네비게이션 기능을 포함
Content: 앱의 본문 컨텐츠를 나타내는 구성 요소. 일반적으로 앱의 주요 내용이 여기에 포함.

Scaffold를 사용하면 위의 구성 요소를 조합하여 앱의 기본 레이아웃을 구성하고, 표준화된 Material Design 스타일을 적용할 수 있다.

Modifier

Compose의 수정자인 Modifier는 Composable를 꾸미거나 더욱 강조한다.

  • 컴포저블의 크기, 레이아웃, 동작 및 모양 변경
  • 접근성 라벨과 같은 정보 추가
  • 사용자 입력 처리
  • 요소를 클릭 가능, 스크롤 가능, 드래그 가능 또는 확대/축소 가능하게 만드는 것과 같은 높은 수준의 상호작용 추가

Modifier의 중요한 특징은 순서에 민감하다는 것이다.

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

위 코드는 다음과 같은 결과를 낸다.

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .padding(padding)
            .clickable(onClick = onClick)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

반면 위의 코드는 다음과 같은 결과를 낸다.

둘의 차이점은 clickable의 등장 순서인데, 이는 결과에서 확인할 수 있듯이 clickable 뒤에 등장하는 패딩 값은 클릭의 영역에 영향을 받지 않는다. 그렇기 때문에 순서에 주의가 필요하다.

Box는 다음 포스팅에서 Compose의 레이아웃과 함께 알아보도록 하자.


출처 :

https://hanyeop.tistory.com/260
https://minhyuuk.tistory.com/entry/AndroidCompose-Jetpack-Compose%EC%9D%98-%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C

profile
안드안드안드

0개의 댓글