Android Compose Layout
업데이트:
카테고리: Android
/태그: Animation, Compose Layout
레이아웃 구상할 때 만드는 순서는 요소를 쪼개서 제일 작은 부분부터 제작 후 감싸가는 형태로 만든다.
특정 형태가 반복된다면, 함수를 재활용해서 처리
화면을 구상하는 전체적인 레이아웃이 들어가도록… Scaffold에 간단하게 적용이 될 수 있도록 한다.
스타일 저장
Color
→ 색 저장Shape
→ 모양 지정- 모서리 둥글게 ( Modifier.background )
val shapes = Shapes( extraSmall = RoundedCornerShape(4.dp), small = RoundedCornerShape(8.dp), medium = RoundedCornerShape(16.dp), large = RoundedCornerShape(24.dp), extraLarge = RoundedCornerShape(32.dp), )
-
Type
→ 내부에 들어가는 글시체, 크기…val Typography = Typography( headlineSmall = TextStyle( fontWeight = FontWeight.SemiBold, fontSize = 24.sp, lineHeight = 32.sp, letterSpacing = 0.sp ), ... )
지정하지 않은 속성은 Default로 정해진 값을 따라간다.
-
Theme
→ 앱에서 사용하는 전체적인 Theme 설정@Composable fun AppTheme( useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit ) { // 다크 모드 설정 여부 val context = LocalContext.current val colors = when { (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) -> { if (useDarkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } useDarkTheme -> DarkColors else -> LightColors } // 상태바 설정 val view = LocalView.current if (!view.isInEditMode) { val window = (view.context as Activity).window window.statusBarColor = colors.primary.toArgb() WindowCompat .getInsetsController(window, view) .isAppearanceLightNavigationBars = useDarkTheme } MaterialTheme( colorScheme = colors, typography = Typography, shapes = shapes, content = content ) }
MaterialTheme
.colorScheme로 요청하면, 지정된 색상을 꺼내 사용할 수 있음
1. LazyColumn / Row (= RecyclerView)
스크롤이 가능한 목록을 보여주기 위해서 사용
많은 데이터를 미리 랜더링 해놓으면 성능상에 문제가 발생, RecyclerView와 달리 이전에 사용했던 요소를 재활용하지 않고 새로운 Composable을 방출한다.
private fun Greetings(
modifier: Modifier = Modifier,
names: List<String> = List(1000) {"$it"}
) {
LazyColumn{
items(items = names) {name ->
Greeting(name = name)
}
}
}
Lazy 요소는 내부에 items
를 통해서 리스트로 된 데이터를 리스트뷰로 보여준다.
Lazy Component의 스크롤 위치를 저장하고 싶다면 state: LazyListState = rememberLazyListState()
을 추가한다.
버튼을 클릭하여 특정로직을 구현한다면, 삭제기능을 가장 최상위 화면? 컴포넌트에 작성하고 그 하위로직에서는 삭제할 아이템의 아이디를 넘겨주고, 최 하위 로직에서는 어떤 버튼을 눌러야 로직이 트리거 되는지 설정한다.
아이템 삭제 로직
2. animation
화면이 펼쳐질 때 열리는 느낌의 애니메이션 적용
-
animate*AsState
컴포저블 사용*
에는 dp, color… 바꾸고 싶은 상태값이 들어감이렇게 작성하는 애들은 미리 외부에서 변수로
by animate...
으로 타입 설정을 해줘야함- 목표dp 까지 value가 업데이트 되는 객체를 반환
-
animationSpec
을 통해 세세하게 커스텀도 가능하다.val extraPadding by animateDpAsState( if (expanded) 48.dp else 0.dp, // spring 애니메이션 효과가 적용되어 띠용띠용 흔들린다. animationSpec = spring( dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = Spring.StiffnessLow ) )
-
[AnimatedVisibility()](https://developer.android.com/codelabs/jetpack-compose-animation?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fcompose%3Fhl%3Dko%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fjetpack-compose-animation#3)
는 if 문 대신 사용한다.AnimatedVisibility( visible = shown, enter = slideInVertically( initialOffsetY = { fullHeight -> -fullHeight } ), exit = slideOutVertically( targetOffsetY = { fullHeight -> -fullHeight } ) )
initialOffsetY
,targetOffsetY
의 변화값으로{ fullHeight -> -fullHeight }
을 입력하였는데, 변화하는 범위이고 왼쪽인자를 기준으로 오른쪽 인자까지 움직인다.이때 주의할 것은 왼쪽인자의 값을 0으로 기준 세우기 때문에 오른쪽 값을 0 으로 지정하면 움직임이 없다.
-
transition
1 번째 방법과 유사하게 변수로 animate 타입을 설정하듯이 사용할 수 있다.
큰 차이점은 transition으로 설정하는 값은 여러종류의 animation을 적용할 수 있다는 점
// page의 타입에 따라 animation 설정 val transition = updateTransition(tabPage, label = "Tab indicator") val indicatorLeft by transition.animateDp(label = "Indicator left") { page -> tabPositions[page.ordinal].left } val indicatorRight by transition.animateDp(label = "Indicator right") { page -> tabPositions[page.ordinal].right } val color by transition.animateColor(label = "Border color") { page -> if (page == TabPage.Home) Purple700 else Green800 }
-
animationSpec
으로 프레임별로 세세하게 위치나 투명도, 색깔을 지정할 수 있음animationSpec = infiniteRepeatable( animation = keyframes { // 한바퀴 굴리는데 걸리는 시간(ms) durationMillis = 1000 // 어느 시간때에 특정한 값을 갖는지 0.7f at 500 0.9f at 800 }, repeatMode = RepeatMode.Reverse )
3. TextField
editText 와 비슷하게 내부에 텍스트 입력을 받는다.
얘는 이자체만으로도 디자인을 끝낼 수 있다? 내부에 이미지 아이콘을 넣기 위해 ImageView를 추가로 넣거나 constrainlayout에 집어넣을 필요 없이 한 요소에서 처리가 가능하다.
TextField(value = "",
onValueChange ={},
modifier = modifier
.fillMaxWidth()
.heightIn(min = 56.dp),
// icon을 속성으로 넣을 수 있어, 따로 지정해주지 않아도 된다.
leadingIcon ={
Icon(
imageVector = Icons.Default.Search,
contentDescription = null)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = MaterialTheme.colors.surface
),
placeholder ={
Text(stringResource(R.string.placeholder_search))
},
)
4. Elevation
요소가 겹쳐있다면, 누가 제일 앞으로 나와있는지 결정하는 속성
위의 이미지 처럼 설정하여, 그림자 효과처럼 사용할 수 있다.
버튼이나, 위에 떠있는 Float 요소에 적용하면 더욱 입체적인 화면 구성을 가져갈 수 있다.
5. androidView
특정 뷰를 화면 전체에 꽉채워서 보여준다.
위에 어떤 요소를 위치시키고 싶다면
AndroidView(
factory = { context ->
MapView(context).apply {
// MapView 초기화 및 설정
}
TextView(modifier = Modifier.zIndex()) {}
}
)
동일 선상에서 위치시킨 뒤에, zIndex를 조정해주면 같은 레이어 위치할 수 있게 된다.