Android

04

jimmyyy 2025. 4. 30. 12:12

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))

'Android' 카테고리의 다른 글

06  (0) 2025.05.14
05  (0) 2025.05.06
03  (0) 2025.04.16
02  (0) 2025.04.09
01  (0) 2025.04.02