본문 바로가기

Android/Palette

[모든 팔레트 정복] 11. Layout(레이아웃) - ConstraintLayout part2

안녕하세요? 닉네임간편입니다. 이번 시간에는 저번 시간에 이어서 ConstraintLayout(제약 레이아웃)에 대해서 계속해서 알아보겠습니다.

1. 크기를 비율로 설정

ConstraintLayout은 크기를 비율로 설정해서 다양한 화면 크기에 유동적으로 대응할 수 있습니다.

안드로이드 기기는 다양한 화면 사이즈를 갖고 있기 때문에 이를 고려해서 화면을 만들어야 합니다.

다양한 크기에 대응하는 방법으로는 크게 3가지가 있습니다.

a. ConstraintLayout을 사용해 보다 유연한 레이아웃 만들기

b. 화면 구성에 맞게 디자인된 다양한 레이아웃을 제공하도록 대체 레이아웃 만들기

c. 크기가 변경되는 나인 패치 비트맵 만들기

ConstraintLayout을 사용하면 레이아웃에 있는 다른 뷰와의 관계에 따라 각 뷰의 위치와 크기를 지정할 수 있습니다. 이를 통해 기기별 화면 크기가 달라지더라도 모든 뷰가 함께 크기가 변화됩니다.

그리고 다음과 같은 사항이 있습니다.

1) match_constraints 사용

레이아웃이 다양한 화면 크기에 맞게 조정되는지 확인하기 위해선 직접 수치를 입력하여 크기와 위치를 정하는 것이 아니라 match_content와 match constraints(0dp를 일컫는 용어)를 사용해야 합니다. 이때 ConstraintLayout에서는 match_parent를 사용하면 안 되며, 대신 match constraints를 사용해야 합니다. 이는 제약조건과 일치하게 뷰의 크기를 정하는 것으로, 여백을 모두 처리한 후 양쪽의 제약조건에 맞게 최대한 크기가 확장됩니다.

2) 일정 비율로 크기를 지정

예를 들어 constraintHeight_default를 percent로 설정하면 다른 위젯의 일정 비율만큼 크기가 정해지며, 기본적으로 부모 레이아웃의 일정 %만큼 크기가 정해집니다. constraintHeight_percent에서 직접 % 수치를 입력하면 됩니다. 물론 이는 constraintWidth에 대해서도 동일하게 적용됩니다.

2. 크기 조정

요소의 높이와 너비를 계산하는 방법은 크게 3가지가 있습니다.

1) 고정

요소의 높이와 너비를 계산하는 방법은 크게 3가지가 있습니다.

2) 콘텐츠 래핑(wrapping)

콘텐츠에 맞게 뷰가 필요한 만큼 확장됩니다. wrap_content로 설정하면 됩니다.

3) 제약 조건과 일치(0dp)

양쪽 모두에 제약 조건이 있을 때, 여백을 처리한 후 뷰의 크기가 최대한 확장됩니다.

이때 특정 속성과 값을 사용해서 높이와 너비를 수정할 수 있습니다.

예를 들어 layout_constraintWidth_default에 값을 설정한 것에 따라서 조정이 가능합니다.

a. spread(넓히기)

양쪽의 제약조건에 맞게 뷰의 크기를 최대한 넓힙니다. 기본적으로 이것이 설정되어있습니다.

b. wrap(래핑)

콘텐츠에 맞게 필요한 만큼 뷰의 크기를 확장하지만, 경우에 따라 뷰의 크기가 콘텐츠의 크기보다 작을 수 있습니다. 따라서 앞서 설명했던 콘텐츠 래핑은 단순히 뷰의 크기가 콘텐츠의 크기와 동일하게 된다면, 이 경우 뷰의 크기는 콘텐츠의 크기보다 작아질 수 있습니다.

c. percent(비율)

이를 비율로 설정하면 부모 레이아웃에서 차지하는 비율만큼 크기를 확장시키거나 축소시킬 수 있습니다.

3. 체인

체인은 양방향 위치 제약 조건을 통해 서로 연결된 뷰 그룹입니다. 쉽게 말해 두 뷰가 서로를 바라보고 있는 것입니다.

체인의 뷰는 세로 또는 가로로 분포될 수 있습니다. 체인 스타일은 크게 4가지가 있으며, 체인의 헤드에 스타일을 설정할 수 있습니다.

체인의 헤드는 연결된 뷰들 중 가장 왼쪽(Start), 위쪽에 있는 뷰입니다.

[activity_main]

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp">
        <TextView
            android:id="@+id/view1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/view2"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/moredarkgray"
            app:layout_constraintHorizontal_chainStyle="spread"
            android:text="view1"
            android:textSize="40sp" />

        <TextView
            android:id="@+id/view2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@+id/view1"
            app:layout_constraintEnd_toStartOf="@+id/view3"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/moredarkgray"
            android:text="view2"
            android:textSize="40sp"/>

        <TextView
            android:id="@+id/view3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/moredarkgray"
            android:text="view3"
            android:textSize="40sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/view2"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp">
        <TextView
            android:id="@+id/view4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/view5"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/moredarkgray"
            app:layout_constraintHorizontal_chainStyle="spread_inside"
            android:text="view4"
            android:textSize="40sp" />

        <TextView
            android:id="@+id/view5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@+id/view4"
            app:layout_constraintEnd_toStartOf="@+id/view6"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/moredarkgray"
            android:text="view5"
            android:textSize="40sp"/>

        <TextView
            android:id="@+id/view6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/moredarkgray"
            android:text="view6"
            android:textSize="40sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/view5"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp">
        <TextView
            android:id="@+id/view7"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/view8"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/littlegray"
            app:layout_constraintHorizontal_chainStyle="spread_inside"
            app:layout_constraintHorizontal_weight="1"
            android:text="view7"
            android:textSize="40sp" />

        <TextView
            android:id="@+id/view8"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@+id/view7"
            app:layout_constraintEnd_toStartOf="@+id/view9"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintHorizontal_weight="2"
            android:background="@color/moredarkgray"
            android:text="view8"
            android:textSize="40sp"/>

        <TextView
            android:id="@+id/view9"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="@color/doubledarkgray"
            android:text="view9"
            android:textSize="40sp"
            app:layout_constraintHorizontal_weight="3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/view8"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp">
        <TextView
            android:id="@+id/view10"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/view11"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/moredarkgray"
            app:layout_constraintHorizontal_chainStyle="packed"
            android:text="view10"
            android:textSize="40sp" />

        <TextView
            android:id="@+id/view11"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@+id/view10"
            app:layout_constraintEnd_toStartOf="@+id/view12"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/moredarkgray"
            android:text="view11"
            android:textSize="40sp"/>

        <TextView
            android:id="@+id/view12"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/moredarkgray"
            android:text="view12"
            android:textSize="40sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/view11"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp">
        <TextView
            android:id="@+id/view13"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/view14"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/moredarkgray"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintHorizontal_bias="1"
            android:text="view13"
            android:textSize="40sp" />

        <TextView
            android:id="@+id/view14"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@+id/view13"
            app:layout_constraintEnd_toStartOf="@+id/view15"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/moredarkgray"
            android:text="view14"
            android:textSize="40sp"/>

        <TextView
            android:id="@+id/view15"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/moredarkgray"
            android:text="view15"
            android:textSize="40sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/view14"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>

1) Spread(넓히기)

여백을 처리한 후, 뷰를 균등하게 분포시킵니다. 기본적으로는 이 스타일로 지정됩니다.

1번째 예시를 보면 view1부터 view3까지 균등하게 배치된 걸 확인할 수 있습니다.

2) Spread Inside(내부에서 넓히기)

첫 번째 보기와 마지막 보기는 체인의 각 끝에 있는 제약 조건에 고정되고 나머지 보기는 균등하게 분포됩니다.

2번째 예시를 보면 view4와 view6는 양 끝쪽에 있고 view5가 가운데에 있습니다.

3) Weighted(가중치)

체인이 '넓히기' 또는 '내부에서 넓히기'로 설정한 다음, 하나 이상의 뷰의 너비 혹은 높이를 '제약 조건과 일치(0dp)'로 설정하면 가중치 개념을 적용할 수 있습니다. 이를 통해 화면의 전체 너비 혹은 높이에서 뷰가 차지하는 크기를 정할 수 있습니다.

만일 weight를 설정하지 않는다면 너비의 경우 전체 너비를 동일하게 할당받습니다. 따라서 모두 같은 크기가 됩니다.

그러나 layout_constraintHorizontal_weight 및 layout_constraintVertical_weight 속성을 사용하여 각 보기에 중요도 가중치를 할당할 수 있습니다. 이는 앞선 앞선 LinearLayout의 layout_weight개념과 동일하게 적용합니다.

가중치가 가장 높은 뷰에 가장 많은 공간이 할당되고, 가중치가 동일한 뷰에는 동일한 크기의 공간이 할당됩니다.

3번째 예시의 경우 view7부터 view9까지 순서대로 가중치를 1,2,3 갖고 있습니다. 따라서 각각의 너비가 가중치에 따라 확장되거나 축소된 걸 확인할 수 있습니다.

이때 view7의 height가 wrap_content로 설정되었기 때문에 가로로 문자열을 표현할 수 없어 높이가 늘어났습니다.

4) Packed(채우기)

여백을 제외하고 뷰가 간격 없이 배치됩니다. 체인의 헤드가 되는 뷰의 편향을 변경하여 전체 체인의 편향을 좌우 또는 상하로 조정할 수 있습니다.

4번째 예시의 경우 모든 뷰 객체가 나란히 붙여서 배치된 걸 확인할 수 있습니다.

5번째 예시의 경우 헤드가 되는 view10의 편향을 조정해서 우측에 배치한 것을 확인할 수 있습니다.

이때 체인을 사용해도 나란히 정렬되는 것을 보장하진 않습니다. 따라서 의도한 대로 정렬하기 위해선 제약 조건을 추가해야 합니다.

4. 마무리

이번 시간에는 저번 시간에 다 다루지 못했던 ConstraintLayout에 대해서 계속해서 알아봤습니다.

이것으로 레이아웃에 대해 알아보는 시간은 마치겠습니다.

레이아웃은 앱을 만드는 데에 있어서 사용자가 보는 화면이기에 매우 중요합니다. 따라서 적지적소에 레이아웃을 배치할 수 있도록, 의도한 화면을 정확히 구현할 수 있도록 이번 기회에 레이아웃에 대해서 정리하셨으면 좋겠습니다.

 

728x90
반응형