IT_Programming/Android_Java

[펌][안드로이드] ConstraintLayout 사용하기 / 안드로이드 컨스트레인트레이아웃. (Android ConstraintLayout)

JJun ™ 2016. 9. 29. 10:20



 * 출처

 : http://dktfrmaster.blogspot.kr/2016/09/constraintlayout.html

 : https://recipes4dev.tistory.com/158


 * 참고할만한 자료

 : http://blog.naver.com/pistolcaffe/221010983726 (Android ConstraintLayout 분석 - 1)

 : http://blog.naver.com/pistolcaffe/221290283458 (Android ConstraintLayout 분석 - 2)



ConstraintLayout

ConstraintLayout은 위젯(뷰)의 위치, 크기를 유연한 방법으로 배치하도록 하는 ViewGroup의 서브클래스이다.
현재로써는, 다음과 같은 제약조건을 이용할 수 있다.

  • Relative Positioning
  • Margins
  • Centering Positioning
  • Visibility Behavior
  • Dimension Constraint
  • Virtual Helpers Objects

참고로, 제약조건은 순환관계를 가질 수 없다.

Note : 이 레이아웃은 Support 라이브러리로 지원되며, API 9(진저브레드) 이상부터 이용할 수 있다.
       안드로이드 팀은 이 레이아웃에 더 많은 기능을 추가할 예정이며, 바뀐 내용은 이 문서에 반영될 것이다.



개발 가이드

Relative Positioning (기존의 RelativeLayout과 유사)

Relative Positioning은 ConstraintLayout에서의 기본적인 레이아웃 배치 중 하나이다.
이 제약조건은 어떤 위젯을 다른 위젯에 상대적으로 배치할 수 있도록 한다.
이 제약조건은 위젯의 수직,수평을 기준으로 추가한다.
  • 수직 : Left, Right, Start, End
  • 수평 : Top, Bottom
즉, 이 개념은 위젯의 한 사이드를 다른 위젯의 사이드에 상대적으로 배치하는 것이다.
다음 예시는 버튼 B를 버튼 A의 오른쪽에 배치하는 과정이다.

<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
         app:layout_constraintLeft_toRightOf="@+id/buttonA" />
이는 버튼 B의 왼쪽 사이드를 버튼 A의 오른쪽 사이드에 배치되도록 제약조건을 시스템에 알려주는 것이다.
위치 제약조건은 시스템이 각 위젯의 사이드를 같은 위치가 되도록 배치할 것이다.



다음과 같은 위치 제약조건이 가능하다.
  • layout_constraintLeft_toLeftOf
  • layout_constraintLeft_toRightOf
  • layout_constraintRight_toLeftOf
  • layout_constraintRight_toRightOf
  • layout_constraintTop_toTopOf
  • layout_constraintTop_toBottomOf
  • layout_constraintBottom_toTopOf
  • layout_constraintBottom_toBottomOf
  • layout_constraintBaseline_toBaselineOf
  • layout_constraintStart_toEndOf
  • layout_constraintStart_toStartOf
  • layout_constraintEnd_toStartOf
  • layout_constraintEnd_toEndOf

위의 제약조건들은 기준이 될 위젯의 id가 필요하다. 혹은 부모를 의미하는 parent를 입력한다.
<Button android:id="@+id/buttonB" ...
         app:layout_constraintLeft_toLeftOf="parent" />



Margins

마진이 설정되면, 해당 뷰와 타겟 뷰 사이에 강제적으로 여백이 생기고, 이에 해당하는 제약조건이 생길 것이다.
기존에 사용했던 마진을 주는 방법으로 제약조건을 추가할 수 있다.
  • android:layout_marginStart
  • android:layout_marginEnd
  • android:layout_marginLeft
  • android:layout_marginTop
  • android:layout_marginRight
  • android:layout_marginBottom


Gone 상태의 위젯에 연결되었을때의 Margin

위치 제약조건의 대상이 View.GONE 상태일 때, 기존의 마진과는 다른 마진값을 적용할 수 있다.
이 값을 적용하려면 다음의 속성들을 이용한다.

  • layout_goneMarginStart
  • layout_goneMarginEnd
  • layout_goneMarginLeft
  • layout_goneMarginTop
  • layout_goneMarginRight
  • layout_goneMarginBottom



Centering positioning and bias


ConstraintLayout은 불가능한 제약조건을 잘 처리할 수 있기 때문에 유용하다. 예를 들어, 다음과 같은 상황이 있을 수 있다.
<android.support.constraint.ConstraintLayout ...>
             <Button android:id="@+id/button" ...
                 app:layout_constraintLeft_toLeftOf="parent"
                 app:layout_constraintRight_toRightOf="parent/>
         </>
이 상황에서, ConstraintLayout이 버튼과 동일한 수평사이즈가 아니라면, 두 제약조건은 동시에 만족하지 않는다.
(버튼의 양 사이드는 우리가 의도한 곳에 놓이지 않는다) 이런 경우에, 제약조건은 다음 그림과 같이 버튼을 양쪽 끝에서 같은 힘으로 당기는 것처럼
동작한다. 결과적으로, 버튼은 부모 컨테이너의 중앙에 놓이게 된다. 이는 수직의 제약조건에서도 유사하게 적용된다.



Bias


위에서 살펴본 바와 같이, 기본적으로 상반되는 두 제약조건의 결과는 위젯을 중앙에 배치시킨다.
하지만, bias라는 속성을 이용해 어느 한쪽에 더 가깝도록 위젯을 배치할 수 있다. 그 속성은 다음과 같다.

  • layout_constraintHorizontal_bias
  • layout_constraintVertical_bias

다음의 예제는 A 위젯을 기존의 50% 중앙 위치 대신, 30%인 왼쪽으로 기울도록 배치할 것이다.
<android.support.constraint.ConstraintLayout ...>
             <Button android:id="@+id/button" ...
                 app:layout_constraintHorizontal_bias="0.3"
                 app:layout_constraintLeft_toLeftOf="parent"
                 app:layout_constraintRight_toRightOf="parent/>
         </>
이처럼, bias를 잘 사용하면 UI를 스크린 크기의 변화에 더 잘 배치되도록 UI를 변경할 수 있다.



Visibility Behavior

ConstraintLayout은 위젯이 View.GONE 상태일때를 특별하게 다룰 수 있다. 일반적으로, GONE 위젯은 화면에 보여지지 않고,
레이아웃의 일부가 아니다.(즉, 실제 Dimension이 변하지 않는다.)
그러나, 레이아웃의 제약조건 관점에서, 
GONE 위젯은 여전히 레이아웃의 일부이다.(이는 중요한 특징이 있다.)
  • GONE 위젯의 크기는 0으로 처리된다.
  • 다른 위젯이 GONE 위젯에 제약조건을 걸었을 때, 그 제약조건은 여전히 유효하지만, GONE된 위젯의 마진은 0으로 처리된다.



위의 그림에서 보듯이, 이런 특별한 동작은 B의 레이아웃을 깨지 않고도, A를 GONE시켜서 임시 레이아웃을 유지할 수 있다.
Note : 위의 그림처럼 B가 A에 제약조건을 걸고 마진값을 가지고 있는 상태에서, A가 GONE이 되면 A의 마진값도 0이 되고,
           그림처럼 B는 자신이 A에 걸었던 마진값만 남게 된다. 제약조건을 걸었던 위젯의 GONE 여부에 따라 대체하는 마진값을 주기 위해,
           위에서 소개했던 
Gone margin값을 이용한다.



Dimensions 제약조건


android:layout_width
android:layout_height 속성을 이용해서, 다음의 3가지 방법으로 위젯의 크기를 지정할 수 있다.

  • 특정 크기(123dp@dimen/height)
  • wrap_content 위젯 자신이 크기를 결정
  • 0dp - match_constraint와 동일



처음의 두 방법은 다른 레이아웃과 동일하게 동작한다. 마지막 방법은 위젯이 제약조건에 맞춰서 리사이징 될 것이다.
위의 그림에서, (a)는 
wrap_content, (b)는 0dp, (c)는 마진값이 있을 때의 0dp 이다.


Ratio


위젯 크기는 수평:수직의 비율로도 정할 수 있다. 비율로 크기를 정하려면, width/height를 0dp(or match_constraint)로 지정하고
layout_constraintDimensionRatio 속성을 이용해 수평:수직 비율값을 준다. 다음의 예시는 수평:수직 비율을 1:1로 준 것이다.
<Button android:layout_width="wrap_content"
               android:layout_height="0dp"
               app:layout_constraintDimensionRatio="1:1" />

비율은 다음과 같이 2가지 방법으로 표현될 수 있다.

  • width와 height의 비율을 나타내는 소수값
  • width : height비율

수평과 수직이 모두 
match_constraint(0dp) 일 때도 비율로 나타낼 수 있다.
단, 이 때는 
layout_constraintDimensionRatio 속성에 W or H를 명시하여 변경될 사이드를 지정해 주어야 한다.
W는 수평, H는 수직) 사이드 구분자는 , 쉼표로 비율과 구분한다.

<Button android:layout_width="0dp"
               android:layout_height="0dp"
               app:layout_constraintDimensionRatio="H,16:9"
               app:layout_constraintBottom_toBottomOf="parent"
               app:layout_constraintTop_toTopOf="parent"/>

다음의 예제는 버튼의 width가 제약조건에 맞게 늘어나고, height는 16:9 비율로 리사이즈 될 것이다.


Vertual Helper Object


지금까지 설명된 위의 내용 이외에도, ConstraintLayout에서 레이아웃을 쉽게 배치할 수 있도록 하는 헬퍼 클래스가 있다. 
Guideline 클래스는 ConstraintLayout 내에 상대적으로 배치됬을 때, 수평 or 수직적인 가이드 라인을 생성해준다.
개발자는 그 가이드라인의 제약조건대로 위젯을 배치하면 된다.


Guideline


ConstraintLayout의 제약조건을 쉽게 설정하도록 도와주는 헬퍼클래스로, 이 객체는 화면에 나타나지 않고,(이 객체들은 View.GONE 상태이다.)
오로지 레이아웃을 위해 사용된다. 이 객체들은 ConstraintLayout 안에서만 사용된다.

Guideline
 객체는 수평, 수직이 될 수 있다.
  • 수직 Guideline은 width가 0이고, height는 ConstraintLayout과 같다.
  • 수평 Guideline은 height가 0이고, width는 ConstraintLayout과 같다.

Guideline 객체의 위치는 다음의 3가지가 가능하다.
  • Left(수평일 경우) or Top(수직일 경우) 로부터 고정된 거리 - layout_constraintGuide_begin
  • Right(수평일 경우) or Bottom(수직일 경우) 로부터 고정된 거리 - layout_constraintGuide_end
  • ConstraintLayout의 width or height에 대한 퍼센트 위치 - layout_constraintGuide_percent

Guideline
 객체를 이용하여 다른 위젯을 쉽게 배치할 수 있고, 위젯이 Guideline 객체에 제약조건을 추가할 수 있다.
xml에서는 다음의 예시와 같이 사용할 수 있다.
<android.support.constraint.ConstraintLayout
        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.support.constraint.Guideline
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/guideline"
            app:layout_constraintGuide_begin="100dp"
            android:orientation="vertical"/>
    <Button
            android:text="Button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/button"
            app:layout_constraintLeft_toLeftOf="@+id/guideline"
            android:layout_marginTop="16dp"
            app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>


ConstraintSet


위에 소개된 방법들은, 모두 xml에서의 적용방법이다. ConstraintSet 클래스의 객체를 이용하면 제약조건을 소스코드에서 변경할 수 있다.
이 클래스는 몇가지의 생성 방법이 있다.

  • Manually
ConstraintSet c = new ConstraintSet();
c.connect(...);
  • R.layout.* 로부터
ConstraintSet c = c.clone(context, R.layout.some_layout);
  • ConstraintLayout 으로부터
c.clone(cLayout);






안드로이드 컨스트레인트레이아웃. (Android ConstraintLayout)



1. 안드로이드 레이아웃 작성.

안드로이드 앱을 개발할 때, UI 화면을 구성하는 작업은 보기만큼(?) 마냥 쉬운 일이 아닙니다. 특히 앱 개발 경험이 상대적으로 부족한 초보 개발자들에겐, 앱의 탐색 메뉴 구성과 컨테이너 선택, 그리고 각 화면에 들어갈 레이아웃과 뷰 위젯 등을 결정하는 일이, 비즈니스 로직(business logic)을 구현하는 작업보다 우선적으로 해결해야 할 과제로 여겨지기도 하죠. 게다가, 요구사항의 변경으로 인해 화면 요소의 배치를 빈번하게 수정해야 하는 일이 발생하면 UI 화면을 수정하는데에만 전체 개발 시간의 상당 부분을 소비하게 될지도 모릅니다.


그러므로 안드로이드 UI 컴포넌트의 종류와 특성, 사용법을 정확히 이해하고 사용하는 것은 매우 중요한 일입니다. 특히, 뷰 위젯을 화면에 배치하기 위해 사용하는 레이아웃(Layout)은, 화면 구성이 복잡할수록, 또, 사용되는 뷰 위젯의 갯수가 많을수록, 어떤 종류의 레이아웃(들)을 선택하는지가 화면 구성 시간, 동작 성능, 유지보수 등에 영향을 줄 수 있습니다.


과거, "UI 구성의 유연함"보다 "배치의 직관성"이 중요시되던 시기에는, 여러 종류의 레이아웃을 중첩(nested)하여 UI를 구성하는 경우가 많았습니다. 사용하기 쉽고, 설계에 따른 화면 구성이 용이했기 때문인데요. 하지만 레이아웃을 중첩해서 사용하는 방법은 성능과 유지보수 측면에서 그다지 효율적이지 못한 단점이 있습니다.


그래서, 레이아웃을 중첩(nested)하지 않고 유연한 방법으로 뷰 위젯들을 배치할 수 있는 레이아웃이 만들어지게 되었습니다. 렐러티브레이아웃(RelativeLayout)의 "상대적 위치 관계에 따른 배치" 특성에 리니어레이아웃(LinearLayout)의 "가중치(weight)가 가진 장점"을 적용하고, 체인(chain) 사용으로 다른 레이아웃 없이 "요소들을 그룹화"할 수 있는, 그리고 그 동안 유명무실(?)했던 레이아웃 에디터를 적극 활용하여 좀 더 쉽고 빠르게 화면을 구성할 수 있게 만들어주는 레이아웃. 바로 컨스트레인트레이아웃(ConstraintLayout)입니다.

2. 안드로이드 컨스트레인트레이아웃(ConstraintLayout) 클래스

컨스트레인트레이아웃(ConstraintLayout)은 레이아웃 구성 시, 뷰 위젯의 위치와 크기를 유연하게 조절할 수 있게 만들어주는 레이아웃입니다. 음.. 너무 추상적인 표현이죠? 조금 더 구체적인 내용부터 차근차근 알아보겠습니다.


컨스트레인트레이아웃이 어떠한 방식으로 레이아웃 배치의 유연함을 제공하는지는, "컨스트레인트(Constraint)"라는 단어의 의미를 통해 유추할 수 있습니다. 바로 Constraint, "제약(또는 조건)"이죠.


컨스트레인트레이아웃는 레이아웃에 배치되는 뷰들에 여러 제약(Constraint)을 적용하여 각 뷰의 위치와 크기를 결정합니다. 여기서 말하는 "제약(Constraint)"이란, 각 요소들의 최종 위치와 크기를 결정하게 될 조건, 예를 들어, 특정 뷰 왼쪽 사이드를 지정된 뷰의 오른쪽 사이드에 맞추거나, 뷰의 왼쪽,오른쪽 사이드를 각각 부모 레이아웃의 왼쪽,오른쪽 사이드에 맞추는 것 등을 말하며, 이러한 각각의 제약(Constraint)은 컨스트레인트레이아웃이 가지는 하나의 레이아웃 속성으로 매핑됩니다. (이런 이유로, 본문과 이후 컨스트레인트레이아웃 관련 글에서는 제약(Constraint)과 속성이라는 용어를 섞어서 사용한다는 점 참고하시기 바랍니다.)

3. 컨스트레인트레이아웃 속성 기본 사용법.

컨스트레인트레이아웃이 제공하는 "제약(Constraint)"들, 즉, 컨스트레인트레이아웃 속성의 이름은 기본적으로 "layout_constraint"로 시작하며, 바로 뒤에 구체적인 제약 조건이 명시됩니다.


예를 들어, 뷰의 "왼쪽 사이드를 대상 뷰의 오른쪽 사이드에 배치"하는 속성은 "layout_constraintLeft_toRightOf"입니다. "layout_constraint"에 "Left_toRightOf"라는 제약 조건을 결합하여, 뷰의 왼쪽 사이드(Left)를 속성 값으로 지정된 ID 뷰의(Of) 오른쪽 사이드(ToRight)에 맞추라는 제약을 지정할 수 있습니다.


앱 개발 시, 레이아웃을 구성할 때는, 아래와 같이 XML 코드를 작성합니다.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="48sp"
        android:background="#FF0000"
        android:id="@+id/text1"
        android:text="TEXT1" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="48sp"
        android:background="#00FF00"
        android:id="@+id/text2"
        android:text="TEXT2"
        app:layout_constraintLeft_toRightOf="@id/text1" />
</android.support.constraint.ConstraintLayout>



4. 컨스트레인트레이아웃이 제공하는 제약(Constraint).

컨스트레인트레이아웃이 제공하는 제약(Constraint)은 그 종류가 매우 많으며, 사용 목적과 방법에 따라 몇 가지 카테고리로 분류됩니다, 물론, 컨스트레인트레이아웃을 사용하는데 있어, 하나의 속성 또는 하나의 카테고리에 속한 속성들만 사용할 수 있는 것은 아니며, 필요에 따라, 여러 카테고리의 제약들을 다양하게 조합하여 사용합니다.


아래 그림은 컨스트레인트레이아웃에서 사용할 수 있는 제약(Constraint)들을 카테고리로 구분하여 정리한 내용인데요, 카테고리에 대한 분류와 기본적인 설명은 [안드로이드 개발 참조문서. ConstraintLayout]에서 참고할 수 있습니다.


아래 표의 항목을 클릭하면, 각 항목에 대한 좀 더 상세한 설명과 예제를 확인할 수 있습니다.


카테고리설명
Relative positioning요소 간 상대 위치 지정. (left, right, top, bottom, start, end, baseline)
Margins요소 간 여백(Margin) 설정을 위한 제약.
Centering positioning뷰를 부모 레이아웃 또는 제약 영역의 중앙에 배치.
Circular positioning대상 뷰를 기준으로 각도(angle)와 반지름(radius)으로 상대 위치 지정.
Visibility behavior뷰의 Visibility 상태에 따른 최종 위치 결정 및 여백.
Dimension constraints뷰에 적용된 제약에 따른 뷰의 크기(Dimension) 결정.
Chains수평 또는 수직 방향(Axis)으로 나열된 뷰에 대한 그룹화. 배치 스타일 지정.
Virtual Helpers objects레이아웃 내 효율적인 뷰 배치에 사용 가능한 몇 가지 Helper 객체들.
(Guideline, Barrier, Group)
Optimizer제약 카테고리에 대한 최적화.


아래 표는 컨스트레인트레이아웃에서 제공하는 속성을 나열한 것입니다. 각 속성 이름을 클릭하면, 해당 속성에 대한 자세한 설명을 확인할 수 있습니다.


ConstraintLayout 속성설명
layout_constraintLeft_toLeftOf뷰의 왼쪽 사이드(Side)를 대상 뷰의 왼쪽 사이드(Side)에 맞춤.
layout_constraintLeft_toRightOf뷰의 왼쪽 사이드(Side)를 대상 뷰의 오른쪽 사이드(Side)에 맞춤.
layout_constraintRight_toLeftOf뷰의 오른쪽 사이드(Side)를 대상 뷰의 왼쪽 사이드(Side)에 맞춤.
layout_constraintRight_toRightOf뷰의 오른쪽 사이드(Side)를 대상 뷰의 오른쪽 사이드(Side)에 맞춤.
layout_constraintTop_toTopOf뷰의 위쪽 사이드(Side)를 대상 뷰의 위쪽 사이드(Side)에 맞춤.
layout_constraintTop_toBottomOf뷰의 위쪽 사이드(Side)를 대상 뷰의 아래쪽 사이드(Side)에 맞춤.
layout_constraintBottom_toTopOf뷰의 아래쪽 사이드(Side)를 대상 뷰의 위쪽 사이드(Side)에 맞춤.
layout_constraintBottom_toBottomOf뷰의 아래쪽 사이드(Side)를 대상 뷰의 아래쪽 사이드(Side)에 맞춤.
layout_constraintBaseline_toBaselineOf뷰의 텍스트 Baseline을 대상 뷰의 텍스트 Baseline에 맞춤.
layout_constraintStart_toEndOf뷰의 시작 사이드(Side)를 대상 뷰의 끝 사이드(Side)에 맞춤.
layout_constraintStart_toStartOf뷰의 시작 사이드(Side)를 대상 뷰의 시작 사이드(Side)에 맞춤.
layout_constraintEnd_toStartOf뷰의 끝 사이드(Side)를 대상 뷰의 시작 사이드(Side)에 맞춤.
layout_constraintEnd_toEndOf뷰의 끝 사이드(Side)를 대상 뷰의 끝 사이드(Side)에 맞춤.
layout_goneMarginLeft뷰 위젯의 왼쪽(Left) 사이드 대상 뷰가 View.GONE 상태일 때 여백 설정.
layout_goneMarginTop뷰 위젯의 위(Top) 사이드 대상 뷰가 View.GONE 상태일 때 여백 설정.
layout_goneMarginRight뷰 위젯의 오른쪽(Right) 사이드 대상 뷰가 View.GONE 상태일 때 여백 설정.
layout_goneMarginBottom뷰 위젯의 아래(Bottom) 사이드 대상 뷰가 View.GONE 상태일 때 여백 설정.
layout_goneMarginStart뷰 위젯의 시작(Start) 사이드 대상 뷰가 View.GONE 상태일 때 여백 설정.
layout_goneMarginEnd뷰 위젯의 끝(Start) 사이드 대상 뷰가 View.GONE 상태일 때 여백 설정.
layout_constraintHorizontal_bias수평 방향(Left/Right 또는 Start/End) 사이드 제약 시, 양 사이드 간 위치 비율.
layout_constraintVertical_bias수직 방향(Top/Bottom) 사이드 제약 시, 양 사이드 간 위치 비율.
layout_constraintCircle원형 위치 지정에 사용될 대상 뷰 ID 지정.
layout_constraintCircleRadius원형 위치 지정 시, 뷰 위젯과 대상 뷰 위젯 중심 사이의 거리.
layout_constraintCircleAngle원형 위치 지정 시, 원 둘레에서 뷰 위젯이 배치될 각도.


5. 참고.

.END.