CH.25 Row/Column 이용해 레이아웃 구성
< Row & Column 컴포저블 >
- Row : 자식 컴포넌트 수평 방향 나열
- Column : 자식 컴포넌트 수직 방향 나열
- Row 안에 Column을 넣거나, Column 안에 Row를 넣을 수 있다
Row {
TextCell("1")
TextCell("2")
TextCell("3")
}
Column {
TextCell("1")
TextCell("2")
TextCell("3")
}
Column {
Row {
Column { TextCell("1"); TextCell("2"); TextCell("3") }
Column { TextCell("4"); TextCell("5"); TextCell("6") }
Column { TextCell("7"); TextCell("8") }
}
Row {
TextCell("9")
TextCell("10")
TextCell("11")
}
}
< 레이아웃 정렬 >
수직 방향 축 기본 정렬
- Row 컴포저블 verticalAlignment 파라미터
- Alignment.Top
- Alignment.CenterVertically
- Alignment.Bottom
수평축 방향 정렬
- Column 컴포저블 horizontalAlignment 파라미터
- Alignment.Start
- Alignment.CenterHorizontally
- Alignment.End
< 레이아웃 배열 위치 >
- Row : 수평방향 orizontalArrangement
- Arrangement.Start
- Arrangement.Center
- Arrangement.End
- Column : 수직방향 verticalArrangement
- Arrangement.Top
- Arrangement.Center
- Arrangement.Bottom
- 배열 간격 조정 :
- Arrangement.SpaceEvenly : 모든 간격 균등
- Arrangement.SpaceBetween : 양 끝 제외 균등 간격
- Arrangement.SpaceAround : 양 끈 간격은 내부 간격의 절반
< Row & Coumn 스코프 모디파이어 >
ColumnScope 모디파이어
- 자식 컴포넌트 위치 제어
- Modifier.align()
- Modifier.alignBy()
- Modifier.weight()
- RowScope 모디파이어
- 자식들이 이용 가능한 모디파이어 제공
- Modifier.align()
- Modifier.alignBy()
- Modifier.alignByBaseline()
- Modifier.paddingFrom()
- Modifier.weight()
< 스코프 모디파이어 가중치 >
- RowScope 가중치 모디파이어 이용 시, 각 자식의 폭을 그 형제들을 기준으로 상대적 지정 가능
Row {
TextCell("1", Modifier.weight(0.2f, fill = true))
TextCell("2", Modifier.weight(0.4f, fill = true))
TextCell("3", Modifier.weight(0.3f, fill = true))
}
CH.26 Box 레이아웃
< Box 정렬 >
- Box 컴포저블은 하나의 정렬 파라미터 제공
- 박스 콘텐츠 영역 안 자식 그룹 위치 커스터마이즈
- 파라미터 contentAlignment
- Alignment.TopStart & .Topcenter & .TopEnd
- Alignment.CenterStart & .Center & .CenterEnd
- Alignment.BottomCenter & .BottomEnd & .BottomStart
< BoxScope 모디파이어 >
- 자식 컴포저블에 적용
- align()
- matchParentSize()
Box(modifier = Modifier.size(height = 90.dp, width = 290.dp)) {
Text("TopStart", Modifier.align(Alignment.TopStart))
}
< clip() 모디파이어 >
- 특정 형태 렌더링
- 모디파이어 호출 & Shape 값 전달
- RectangleShape
- CircleShape
- RoundedCornerShape
- CutCornerShape
Box(Modifier.size(200.dp).clip(CircleShape).background(Color.Blue))
CH.27 커스텀 레이아웃 모디파이어
< 커스텀 레이아웃 모디파이어 >
- 표준 구문
- layout 후행 람다는 measurable, constraints 2개의 파라미터 각각 전달
- measurable: 자식 요소
- constraints: 자식 이용 가능한 최대/최소 폭과 높이
fun Modifier.<커스텀 레이아웃 이름> (
//선택적 파라미터
) = layout { measurable, constraints ->
//요소의 위치와 크기를 조정할 코드
}
fun Modifier.exampleLayout(x: Int, y: Int) = layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
placeable.placeRelative(x, y)
}
}
< 커스텀 모디파이어 >
Box(Modifier.size(120.dp, 80.dp)) {
ColorBox(
Modifier
.exampleLayout(90, 50)
.background(Color.Blue)
)
}
< 정렬 선 >
fun Modifier.exampleLayout(fraction: Float) = layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
val x = -(placeable.width * fraction).roundToInt()
layout(placeable.width, placeable.height) {
placeable.placeRelative(x = x, y = 0)
}
}
< 베이스라인 >
- Text 컴포저블은 텍스트 콘텐츠 베이스라인에 따라 정렬 가능
- Placeable 객체 통해 접근
- 자식 위치 설정 위해 참조점으로 커스터마이즈
val placeable = measurable.measure(constraints)
val firstBaseline = placeable[FirstBaseline]
val lastBaseline = placeable[LastBaseline]
- 전달된 자식이 정렬 타입을 지원하는지 확인 필요
if (placeable[FirstBaseline] != AlignmentLine.Unspecified) { ... }
CH.28 커스텀 레이아웃 구현
< 커스텀 레이아웃 >
- 표준 구조
- 자식 레이아웃 프로퍼티 변경 X
- 사용자 커스텀 레이아웃 구성 템플릿 역할
@Composable
fun DoNothingLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content
) { measurables, constraints ->
val placeables = measurables.map { measurable ->
measurable.measure(constraints)
}
layout(constraints.maxWidth, constraints.maxHeight) {
placeables.forEach { placeable ->
placeable.placeRelative(0, 0)
}
}
}
}
< CascadeLayout 컴포저블 >
@Composable
fun CascadeLayout(
spacing: Int = 0, //spacing 파라미터 추가
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content
) { measurables, constraints ->
var indent = 0 //가장 최근 indent 추적 위한 변수
layout(constraints.maxWidth, constraints.maxHeight) {
var yCoord //한 자식을 바로 이전 자식의 아래 표시위해 y좌푯값 유지
val placeables = measurables.map { it.measure(constraints) }
placeables.forEach { placeable ->
placeable.placeRelative(x=indent, y=yCoord)
indent += placeable.width + spacing
yCoord += placeable.height + spacing
//각 자식의 위치 계산
}
}
}
}
CH.29 & CH.30 ConstraintLayout
< ConstraintLayout >
- 반응형 사용자 인터페이스 레이아웃을 쉽게 만들기 위해 디자인
- 간단하고 인상적이며 유연한 레이아웃 시스템 제공
- 자식 컴포넌트들의 위치 및 크기 동작 관리
//기본
ConstraintLayout {
// 자식 컴포저블
}
//Modifier를 통해 크기, 배경 등도 설정 가능
ConstraintLayout(Modifier.size(200.dp, 300.dp).background(Color.Green)) { }
< 참조 >
- createRef() : 하나의 참조 생성
- createRefs() : 여러 참조 동시에 생성
- 컴포저블에 Modifier.constrainAs()로 참조 연결
val text1 = createRef()
val (button, text1, text2) = createRefs()
- 참조 컴포저블에 할당
CaonstraintLayout {
val text1 = createRdf()
Text("Hello", modifier = Modifier.constrainAs(text1) {
//제약
})
}
< 제약 >
- 한 컴포저블의 정렬과 위치를 조정함에 있어 다른 컴포저블들, 특별한 요소들을 상대적 지정
- 기기 방향 변경, 다른 화면 크기의 기기에 표시될 때 반응 방법 지정
- 수직, 수평 축에 충분한 제약을 설정
Text("Hello", Modifier.constrainAs(text1) {
top.linkTo(parent.top, margin = 16.dp)
bottom.linkTo(parent.bottom, margin = 16.dp)
})
centerVerticallyTo(parent)
centerHorizontallyTo(parent)
< 마진 >
- 각 제약 커넥션은 마진값과 연결
< 반대 제약 & 편향 >
반대제약
- 동일한 축을 따라 한 컴포저블이 가진 2개의 제약
- 좌우 가장자리에 모두 제약을 갖고 있으면 수평 반대 제약
MyButton(text = "Button1", Modifier.constrainAs(button1) {
top.linkTo(parent.top, margin = 60.dp)
linkTo(parent.start, parent.end)
}
제약 편향
- 반대 제약 상태에서 컴포넌트 위치 조정 허용 위해 구현 필요
- 하나의 제약 조건에 대해 지정된 백분율만큼 치우치도록 적용 가능
linkTo(parent.start, parent.end, bias = 0.75f)
< 체인 >
- 하나의 그룹으로 정의된 2개 이상의 컴포저블을 포함하는 레이아웃 동작 방법 제공
- 수직축, 수평축 기준 선언 가능
- 체인 안 컴포저블 간격과 크기 정의
ConstraintLayout(
modifier = Modifier.size(width = 400.dp, height = 109.dp)
) {
val (button1, button2, button3) = createRefs()
createHorizontalChain(button1, button2, button3)
MyButton(text = "Button1", modifier = Modifier.constrainAs(button1) {
centerVerticallyTo(parent)
})
MyButton(text = "Button2", modifier = Modifier.constrainAs(button2) {
centerVerticallyTo(parent)
})
MyButton(text = "Button3", modifier = Modifier.constrainAs(button3) {
centerVerticallyTo(parent)
})
}
< 체인 스타일 >
- 체인 레이아웃 동작
- Spread Chain : 전체 공간에 균등 분포
- SpreadInside Chain : 시작과 끝 제외, 나머지 컴포넌트만 균등 분포
- Weighted Chain : 가중치에 따라 공간 분배
- Packed Chain : 컴포넌트를 뭉쳐서 배치
createHorizontalChain(button1, button2, button3, chainStyle = ChainStyle.SpreadInside)
< 크기 설정 >
- Dimension.preferredWrapContent: 콘텐츠 크기 기준
- Dimension.wrapContent: 콘텐츠 크기 기준 (제약 무시)
- Dimension.fillToConstraints: 가능한 공간 최대 채우기
- Dimension.preferredValue: 특정 크기 선호
- Dimension.value: 고정 크기
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
< 가이드라인 >
- 추가적 연결 제약 제공
- 여러 가이드라인 추가 가능
- 수평 또는 수직 방향 설정
- 여러 컴포저블을 일정한 축에 맞춰 정렬할 때 유용
val guide = createGuidelineFromStart(fraction = 0.6f)
< 베리어 헬퍼 >
- 가상의 뷰
- 가이드라인과 유사
- 레퍼런스 컴포넌트 요소에 의해 정의
- 겹침 방지 및 동적 레이아웃 조정에 매우 유용
val barrier = createEndBarrier(button1, button2, margin = 30.dp)
< ConstraintSet >
- 제약 집합
- 모디파이어 선언 중복 없이 재사용할 수 있는 제약 집합 생성 가능
- 여러 기준 따라 다양한 제약 집합 유연하게 제공 가능
private fun myConstraintSet(margin: Dp): ConstraintSet {
return ConstraintSet {
val button1 = createRefFor("button1")
constrain(button1) {
linkTo(parent.top, parent.bottom, topMargin = margin, bottomMargin = margin)
linkTo(parent.start, parent.end, startMargin = margin, endMargin = margin)
width = Dimension.fillToConstraints
height = Dimension.fillToConstraints
}
}
}
CH.31 IntrinsicSize
< IntrinsicSize >
- 내재적 측정값
- IntrinsicSize를 사용하면 규칙을 깨지 않고도 자식의 크기 정보를 얻을 수 있다.
Row(modifier = modifier.height(IntrinsicSize.Min)) {..}
- 부모 컴포저블이 값에 접근하여 자식의 크기 정보를 얻을 수 있다
- 모디파이어가 없다면 Row, Column 같은 레이아웃 컴포저블은 부모가 설정한, 이용 가능한 모든 공간을 차지하는 크기로 설정
< IntrinsicSize.Max>
- IntrinsicSize.Max : 자식의 최대 크기 정보, 컴포저블이 표시하는 텍스트 길이
Column(Modifier.width(IntrinsicSize.Max))
< IntrinsicSize.Min >
- IntrinsicSize.Min : 자식의 최 크기 정보, 문자열 가장 긴 단어의 길이
Column(Modifier.width(IntrinsicSize.Min))