SlideShare ist ein Scribd-Unternehmen logo
1 von 60
Downloaden Sie, um offline zu lesen
발표자
안세원 (kingori@gmail.com)
● 카카오모빌리티 안드로이드 앱 개발자
● GDG Korea
○ #android, #pangyo 채널 죽돌이
● 최근 참여 앱
○ 카카오 T 택시 기사용
○ 카카오 T
○ 카카오톡
2
ConstraintLayout
● 복잡한 레이아웃을 단순한 계층구조를 이용해 표현할 수 있는 ViewGroup
● 형제 View 들과의 관계를 정의해서 레이아웃을 구성한다는 점은
RelativeLayout과 비슷하지만, 보다 유연하고 다양한 기능을 제공함
https://developer.android.com/training/constraint-layout/index.html 3
왜 써야 하죠?
● RelativeLayout 에선 불가능했던 자식 뷰 간의 상호 관계 정의 가능
ex) 두 View를 위 아래로 붙여서 컨테이너 중앙에 배치하기
● LinearLayout을 써야만 했던 뷰 비율 조절도 간단히 가능
● 뷰 계층을 간단하게 할 수 있어 유지보수도 좋고 성능도 좋고!
4
다루는 내용
 ConstraintLayout 1.1 의 거의 모든 기능
○ MATCH_CONSTRAINT
○ Dimension ratio
○ Percent
○ Bias
○ Guideline
○ Chain
○ Barrier
○ Placeholder
○ ConstraintSet
○ Group
5
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
 ConstraintLayout 1.1 의 거의 모든 기능
○ MATCH_CONSTRAINT
○ Dimension ratio
○ Percent
○ Bias
○ Guideline
○ Chain
○ Barrier
○ Placeholder
○ ConstraintSet
○ Group
다루는 내용
6
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
 ConstraintLayout 1.1 의 거의 모든 기능
○ MATCH_CONSTRAINT
○ Dimension ratio
○ Percent
○ Bias
○ Guideline
○ Chain
○ Barrier
○ Placeholder
○ ConstraintSet
○ Group
다루는 내용
7
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
 ConstraintLayout 1.1 의 거의 모든 기능
○ MATCH_CONSTRAINT
○ Dimension ratio
○ Percent
○ Bias
○ Guideline
○ Chain
○ Barrier
○ Placeholder
○ ConstraintSet
○ Group
다루는 내용
8
안다루는 내용
● Circular Positioning
● Optimizer
● Android Studio의 Layout editor 활용
9
릴리즈 이력
● 2017.2 : 1.0
● 2017.3 : 1.0.2
● 2018.4 : 1.1.0
● 20XX.X : 2.0 ?
10
Gradle 설정
repositories {
maven {
url 'https://maven.google.com'
}
}
dependencies {
compile 'com.android.support.constraint:constraint-layout:1.1.0'
}
11
1장
천리길도 View 하나부터
Constraint
● ConstraintLayout에서 자식 뷰의 위치를 잡는 기준
● 다른 뷰나 부모 레이아웃과의 정렬 조건 / 연결 관계를 나타냄
● layout_constraint[기준1]_to[기준2]of="[viewId|parent]"
● start, end 속성은 left, right 속성보다 우선됨
<Button android:id="@+id/buttonB" ...
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toLeftOf="@id/buttonA"
app:layout_constraintBottom_toBottomOf="parent"/>
13
Constraint
● RelativeLayout의 alignLeft / toLeftOf 와는 다름
○ constraint는 위치/ 크기의 기준을 잡는 역할일 뿐
○ 이와 달리 RelativeLayout의 align 등은 위치 자체를 정의함
● Top/Bottom/Left/Right를 모두 선언해야 하는 것은 아님
ex) android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
→ 부모 뷰 좌상단 정렬
○ 하지만 체인 등 다른 기능 사용시 예상치 못한 동작이 발생할 수 있음!
 가급적 4방향 모두 선언하는 습관을 들이자
14
뷰 크기
● android:layout_width / layout_height 속성 사용
○ 고정 크기 ex) android:layout_width="100dp"
○ wrap_content
○ match_constraint
● match_parent가 아닌 match_constraint!
○ 크기는 0dp : constraint에 꽉 차게 크기를 맞춤
○ match_constraint라는 속성 값은 없다! → 0dp 이라 쓰고 match_constraint라고 읽음
○ 제대로 동작하려면 뷰의 양쪽 constraint를 다 선언해줘야 함 (ex. left 와 right)
● match_parent는 사용하지 말 것!
○ 1.0.0 beta-5 에선 아예 예외를 던졌으나, 다음 버전에서 수정되었음
○ 그래도 권장하지 않으니 사용하지 말 것
15
뷰 크기
<Button
android:layout_width="150dp"
android:layout_height="0dp"
android:text="HAHA"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
16
뷰 크기 - Percent size
● 부모 뷰의 크기에 비례하여 뷰의 크기를 결정함
○ 단, padding은 제외한 크기!
● layout_width 는 match_constraint로 선언
● layout_constraint[Width|Height]_default: 대상 축의 constraint의 계산 방식
○ spread: constraint 영역에 맞춤 (기본 동작)
○ wrap: 뷰의 크기에 맞춤
○ percent: constraint[Width|Height]_percent 속성에 선언한 비율에 맞춤
android:layout_width="0dp"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.4"
17
뷰 크기 – 가로/세로 비율 지정
● dimensionRatio : 뷰의 가로/세로 비율 결정
○ app:layout_constraintDimensionRatio="1" → 가로/세로 = 1/1
○ app:layout_constraintDimensionRatio="1:1" → 가로:세로 = 1:1
● 적어도 한 방향은 match_constraint 여야 함!
● 두 방향 모두 match_constraint 일 경우, 비율에 맞춰 constraint 내에서 가장
큰 크기로 설정
18
뷰 크기 – 가로/세로 비율 지정
<Button
android:layout_width="150dp"
android:layout_height="0dp"
android:text="HAHA"
app:layout_cDimensionRatio="1:1"/>
<Button
android:layout_width="0dp"
android:layout_height="0dp"
android:text="HAHA"
app:layout_cDimensionRatio="2"/>
<Button
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="HAHA"
app:layout_cDimensionRatio="1:2"/>
19
뷰 크기 – 가로/세로 비율 지정
● 명시적으로 비율을 적용할 축을 지정할 수 있음 : (W|H,)[비율]
○ W: 높이에 맞춰 폭을 조정
○ H: 폭에 맞춰 높이를 조정
● 폭 match_constraint, 높이 match_constraint인 경우
○ H,1:2: 폭을 constraint에 맞춰 설정한 후, 비율에 따라 높이를 결정
○ W,1:2: 높이를 constraint에 맞춰 설정한 후, 비율에 따라 폭을 결정
● 폭 wrap_content, 높이 match_constraint인 경우
○ H,1:2: 폭을 wrap_content에 맞춰 설정한 후, 비율에 따라 높이를 결정
○ W,1:2: 폭이 wrap_content로 결정되어 버렸기 때문인지 역으로 2:1 비율이 설정되어 버림(??)
20
뷰 크기 – 가로/세로 비율 지정
<Button
android:layout_width="0dp"
android:layout_height="0dp"
android:text="HAHA"
app:layout_cDimensionRatio
="W,1:2"/>
<Button
android:layout_width="0dp"
android:layout_height="0dp"
android:text="HAHA"
app:layout_cDimensionRatio
="H,1:2"/>
 넘쳐버림
<Button
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="HAHA"
app:layout_cDimensionRatio
="W,1:2"/>
 2:1 이 되어버림
<Button
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="HAHA"
app:layout_cDimensionRatio
="H,1:2"/>
21
뷰 크기 - 최소/최대
● 최소/최대 크기 지정 : app:layout_constraintWidth_[min|max]="size"
● android:minWidth와 다른점
○ wrap_content 일 땐 android:[min|max]width 적용
○ mactch_constraint 일 땐 app:layout_constraintWidth_[min|max] 적용
<Button
android:layout_width="wrap_content"
android:minWidth="100dp"
app:layout_constraintWidth_min="200dp"
… />
<Button
android:layout_width="0dp"
android:maxWidth="100dp"
app:layout_constraintWidth_max="200dp"
… />
22
위치 지정 - bias
● bias: (constraint 영역 크기 - 뷰 크기) 를 분배하는 비율
○ app:layout_constraintHorizontal_bias="0~1"
■ 0: 왼쪽에 붙이기 / 1: 오른쪽에 붙이기 / 0.5: 중간에 위치 (기본값)
<Button
android:layout_width="wrap_content"
app:layout_constraintHorizontal_bias="0"
… />
<Button
android:layout_width="wrap_content"
app:layout_constraintHorizontal_bias="1"
… />
23
위치 지정 - guideline
● android.support.constraint.Guideline
○ 가로 또는 세로 축 방향을 가진 가상의 뷰
● 부모 뷰의 특정 위치를 기준점으로 삼을 때 사용
● 축, 위치 값을 속성으로 가짐
○ 축: android:orientation="[vertical|horizontal]"
○ 위치
■ app:layout_constraintGuide_begin : 시작 지점으로 부터의 거리
■ app:layout_constraintGuide_end : 끝 지점으로 부터의 거리
■ app:layout_constraintGuide_percent : 시작 지점으로 부터의 % 위치
24
위치 지정 - guideline
<android.support.constraint.Guideline
android:id="@+id/gd_left"
app:layout_constraintGuide_begin="100dp"
android:orientation="vertical"/>
<android.support.constraint.Guideline
android:id="@+id/gd_right"
app:layout_constraintGuide_end="50dp"
android:orientation="vertical"/>
<android.support.constraint.Guideline
android:id="@+id/gd_bottom"
app:layout_constraintGuide_percent="0.8"
android:orientation="horizontal"/>
<Button
app:layout_constraintLeft_toRightOf="@id/gd_left"
app:layout_constraintRight_toLeftOf="@id/gd_right"
app:layout_constraintBottom_toTopOf="@id/gd_bottom"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="0dp" android:layout_height="0dp"
android:text="Button"/>
25
2장
여러개의 뷰
체인
● 서로 연결되어 그룹으로 동작하는 뷰의 묶음
● 체인으로 연결된 뷰 끼리도 체인이 연결된 방향으로만 그룹으로 동작함
● 생성 조건: 마주보는 뷰끼리 마주보는 방향으로 서로 constraint를 설정함
A B
A B 체인?
right to left of B left to right of A O
right to left of B right to right of A X
right to left of B top to top of A X
27
체인
● 체인 스타일의 종류
https://developer.android.com/reference/android/support/constraint/ConstraintLayout.html#Chains
28
체인
● 체인 스타일 별 설정 방법
○ spread: 헤드에 app:layout_constraintHorizontal_chainStyle="spread" 선언
○ spread_inside: 헤드에 app:layout_constraintHorizontal_chainStyle="spread_inside" 선언
○ packed: 헤드에 app:layout_constraintHorizontal_chainStyle="packed" 선언
○ packed with bias: packed 헤드에 app:layout_constraintHorizontal_bias="0~1" 선언
○ weighted: match_constraint 인 뷰가 포함된 체인의 뷰에
app:layout_constraintHorizontal_weight 속성 선언
● 체인 헤드: 위치 상 체인의 가장 앞쪽(왼쪽 혹은 윗쪽)에 위치한 뷰
29
체인 - spread
● (체인의 constraint 영역 - 뷰 크기의 합) 을 균등분할하여 배치
● 체인 바깥 마진은 constraint 영역에서 제외
● 체인 안쪽 마진은 마진을 가진 뷰의 크기에 합산
<Button android:id="@+id/btn_a"
android:layout_width="100dp"
android:layout_marginLeft="50dp"
app:layout_cLeft_toLeftOf="parent"
app:layout_cRight_toLeftOf="@id/btn_b"
app:layout_cHorizontal_chainStyle="spread"/>
<Button android:id="@+id/btn_b"
android:layout_width="100dp"
android:layout_marginLeft="20dp"
app:layout_cLeft_toRightOf="@id/btn_a"
app:layout_cRight_toRightOf="parent"/>
constraint 영역 = 300 - 50 = 250
view 크기 = 100 + 100 + 20 = 220
간격 = (250 - 220 )/ 3 = 10
A의 왼쪽 = 50 + 10 = 60
B의 왼쪽 = 60 + 100 + 10 + 20 = 190
30
체인 - spread_inside
● 체인의 양 끝 뷰를 constraint영역의 양 끝에 배치하고, 나머지 뷰를 남는 공
간을 균등분할하여 배치
<Button android:id="@+id/btn_a"
android:layout_width="100dp"
android:layout_marginLeft="20dp"
app:layout_cLeft_toLeftOf="parent"
app:layout_cRight_toLeftOf="@id/btn_b"
app:layout_cHorizontal_chainStyle="spread_inside"/>
<Button android:id="@+id/btn_b"
android:layout_width="100dp"
android:layout_marginRight="20dp"
app:layout_cLeft_toRightOf="@id/btn_a"
app:layout_cRight_toRightOf="parent"/>
31
체인 - packed / packed biased
● 체인을 구성하는 각 뷰를 밀착하여 배열
● (constraint 영역) - (뷰 폭의 합) 을 bias에 따라 분배
<Button android:id="@+id/btn_a"
android:layout_width="100dp"
app:layout_cLeft_toLeftOf="parent"
app:layout_cRight_toLeftOf="@id/btn_b"
app:layout_cHorizontal_chainStyle="packed"/>
<Button android:id="@+id/btn_b"
android:layout_width="100dp"
android:layout_marginRight="20dp"
android:layout_marginLeft="20dp"
app:layout_cLeft_toRightOf="@id/btn_a"
app:layout_cRight_toRightOf="parent"/>
constraint 영역 = 300 - 20 = 280
view 크기 = 100 + 100 + 20 = 220
남는 공간 = (280 - 220 )/ 2 = 30
A의 왼쪽 = 30
B의 왼쪽 = 30 + 100 + 20 = 150
32
체인 - weighted
● 크기가 match_constraint인 뷰가 하나 이상 포함된 체인
● match_constraint 뷰에 app:layout_constraintHorizontal_weight 속성으로 비율을 지정
● 주의: 모든 match_constraint 뷰에 빠짐없이 weight 속성을 지정해야 함!
<Button android:id="@+id/btn_a"
android:layout_width="0dp"
app:layout_cLeft_toLeftOf="parent"
app:layout_cRight_toLeftOf="@id/btn_b"
app:layout_constraintHorizontal_weight="2"/>
<Button android:id="@+id/btn_b"
android:layout_width="0dp"
android:layout_marginRight="10dp"
android:layout_marginLeft="20dp"
app:layout_cLeft_toRightOf="@id/btn_a"
app:layout_cRight_toRightOf="parent"
app:layout_constraintHorizontal_weight="1"/>
뷰가 차지할 수 있는 크기
= 300 - 20 - 10 = 270
A의 크기 = 270 /3 * 2 = 180
B의 크기 = 270 /3 * 1 = 90
33
Barrier
● 여러 뷰의 가장자리 위치에 만드는 가상의 뷰
● 복잡한 양식 등을 만드는데 활용할 수 있음
34
Barrier
<TextView
android:id="@+id/tv_1" android:text="name:"
android:layout_width="wrap_content
android:layout_height="wrap_content"
app:layout_cTop_toTopOf="parent
app:layout_cLeft_toLeftOf="parent"
app:layout_cBottom_toBottomOf="parent"
app:layout_cRight_toRightOf="parent"
app:layout_cVertical_bias="0" app:layout_cHorizontal_bias="0"
android:layout_margin="8dp" />
<TextView
android:id="@+id/tv_2" android:text="passwd:" android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_cTop_toBottomOf="@id/tv_1" app:layout_cLeft_toLeftOf="parent"
app:layout_cBottom_toBottomOf="parent" app:layout_cRight_toRightOf="parent"
app:layout_cVertical_bias="0" app:layout_cHorizontal_bias="0"
android:layout_margin="8dp"/>
<android.support.constraint.Barrier
android:id="@+id/br_label"
android:layout_width="0dp"
android:layout_height="0dp"
app:barrierDirection="right"
app:constraint_referenced_ids="tv_1,tv_2" />
35
Group
● 여러 뷰의 visibility를 한꺼번에 조정
○ 그룹의 visibility를 바꾸면 그룹에 속한 모든 뷰의 visiblity가 바뀜
⇒ flat해진 구조 덕분에 여러개의 뷰의 visibility를 바꿔야 할 때 유용함
● 하나의 뷰가 여러 그룹에 속할 경우, xml에 마지막으로 선언된 그룹의
visibility를 따름
<android.support.constraint.Group
android:id="@+id/grp_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="tv_1,tv_3"
android:visibility="gone"/>
36
ConstraintSet
● 프로그램적으로 constraint를 만드는 기능
○ 일일이 바닥부터 만들어내거나
○ 다른 xml로 부터 constraint만 뽑아오거나
○ ConstraintLayout 인스턴스에서 뽑아오거나
● 만들어진 constraint를 ConstraintLayout에 적용할 수 있음
● 동적으로 ConstraintLayout의 모든 뷰 혹은 일부 뷰의 레이아웃을 갱신할 수
있음
● ConstraintSet은 constraint만 갱신하므로 constraint와 관련 없는 속성
(padding, text size, …)은 영향받지 않음
● TransitionManager.beginDelayedTransition() 을 이용하여 손쉽게 애니메이션
생성 가능
37
ConstraintSet
constraintSet2 = ConstraintSet()
constraintSet2.clone(content)
constraintSet2.connect(R.id.icon_2, ConstraintSet.TOP,
R.id.btn_layout1, ConstraintSet.TOP )
constraintSet2.constrainWidth(R.id.icon_2,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120f,
resources.displayMetrics).toInt())
...
38
ConstraintSet
● ConstraintSet A를 ConstraintSet B로 갱신한 경우
○ A엔 있으나 B에는 없는 뷰 : 이전 constraint 유지
○ A엔 없으나 B에는 있는 뷰 : 무시됨
○ A에선 V1, V2가 체인이었으나 B에선 V1만 언급하며, 체인 관계가 깨진 경우
: 체인 관계는 깨지며, V2 의 위치는 다시 설정됨
39
Placeholder
● 기존 뷰의 위치를 재조정하는 가상의 뷰
● Placeholder가 대체한 원래의 뷰는 사라짐
<Button android:id="@+id/btn" android:text="btn"
android:layout_width="wrap_content" android:layout_height="wrap_content"
app:layout_cTop_toTopOf="parent" app:layout_cLeft_toLeftOf="parent" />
<android.support.constraint.Placeholder android:id="@+id/ph_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_cRight_toRightOf="parent"
app:layout_cBottom_toBottomOf="parent"/>
ph_1.setContentId(R.id.btn) //ph_1 자리로 btn을 옮김
40
Placeholder - 용도
● 동일한 속성을 가진 뷰를 배치만 다른 레이아웃에서 재사용
→ 가로/세로 레이아웃에서 view를 중복해서 선언하지 않아도 됨
→ 하지만 view 크기 컨트롤에 문제가 있음
<ConstLayout>
<View1 id="@+id/v1"
attr1="v1"
attr2="v2" .../>
...
<include layout="holder"/>
</ConstLayout>
<merge>
<Placeholder
id="p1"
const1=".."
app:content="@id/v1">
... />
</merge>
<merge>
<Placeholder
id="p1"
const1=".."
app:content="@id/v1"/>
... />
</merge>
layout/holder.xmllayout/layout.xml layout-land/holder.xml
41
Placeholder - 용도
● Runtime에 하나 혹은 여러 뷰의 위치를 바꿈
● 그 용도로 ConstraintSet을 만들지 않았나?
○ xml 상에서 간단하게 선언할 수 있어 편함
○ 하나의 placeholder에 여러 뷰를 번갈아가며 위치시킬 수 있음
http://androidkt.com/constraintlayout/42
3장
심화학습
wrap_content
● 텍스트가 긴 TextView에선 wrap_content가 의도치 않게 동작할 수 있음
○ margin 무시됨
○ 측정해 보면 constraint 크기가 아닌 parent 크기까지 폭이 늘어남
<TextView
android:layout_width="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text="asdfasdfasdlfjasdf;asdlf;jasd;flkjasdf;l..."
android:layout_margin="50dp"
android:maxLines="1" android:ellipsize="end" />
44
wrap_content
● wrap_content를 constraint 영역 내에 제대로 표현하려면 추가 설정 필요
○ 크기는 android:layout_width="wrap_content"
○ app:layout_constrainedWidth="true" 설정을 추가
○ 1.0.2 에선 match_constraint & layout_constraintWidth_default="wrap"
 이 조합은 1.1.0 에서 deprecate됨
<TextView
android:text="asdfasdfasdlfjasdf;asdlf;ja..."
android:layout_width="wrap_content"
app:layout_constraintedWidth="wrap"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_margin="50dp"
android:maxLines="1" android:ellipsize="end"/>
짧은 텍스트 긴 텍스트 45
패딩, 마진과 레이아웃의 관계
● 부모 뷰의 패딩은 constraint 영역에서 빼야 함
ex) 300dp 폭, 좌우 패딩 50dp 이라면 constraint 영역은 300-(50)*2 = 200dp
● guideline은 부모 뷰의 패딩을 적용한 위치에 만들어짐
46
패딩, 마진과 레이아웃의 관계
<android.support.constraint.ConstraintLayout
android:layout_width="300dp" android:layout_height="300dp"
android:paddingLeft="100dp" android:paddingRight=“50dp">
<android.support.constraint.Guideline
android:orientation="vertical"
app:layout_constraintGuide_begin="50dp" /> → padding+begin = 100 + 50 = 150dp 위치에 생성
<android.support.constraint.Guideline
android:orientation="vertical"
app:layout_constraintGuide_end="50dp"/> → width - padding - end = 300- 50 - 50
= 200dp 위치에 생성
<android.support.constraint.Guideline
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5"/> → paddingLeft + ( constraintWidth ) / 2
= 100 + ( 300- 100 - 50 ) / 2
= 100 + 75 = 175dp 위치에 생성
47
패딩, 마진과 레이아웃의 관계
● 체인이 아니어도 다른 뷰와 constraint 관계를 맺을 수 있으나, 상대 뷰의
margin은 고려되지 않음
→ margin까지 고려하려면 chain 관계를 가져야 함
<Button android:id="@+id/btn_a"
android:layout_width="wrap_content"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"/>
<Button
android:id="@+id/btn_b"
android:layout_width="wrap_content"
app:layout_cLeft_toRightOf="@id/btn_a"/>
 B는 A의 marginRight를 고려하지 않음
48
패딩, 마진과 레이아웃의 관계
● 다음 조건에서 자식 뷰의 왼쪽 좌표를 계산하시오
○ 부모 뷰: 폭 300dp , 패딩 30dp
○ 자식 뷰: 폭 50dp, 좌우 마진 20dp
■ left to parent, right to parent
■ horizontal bias 0.2
?
49
패딩, 마진과 레이아웃의 관계
● view의 왼쪽 좌표 계산식
viewLeft = constraintLeft + (constraintWidth - (viewWidth+marginSide) ) * bias + marginLeft
○ constraintLeft
■ parent일 경우: parent.Left + parent.PaddingLeft
■ 특정 view A 의 오른쪽을 기준으로 할 경우: (constraintLeft_toRightOf="A" )
a.right + a.marginRight*
* 둘이 packed chain style 관계일 때에만!
○ constraintWidth = constraintRight - constraintLeft
○ marginSide = marginLeft + marginRight
● viewWidth 가 constraintWidth 보다 크면 어떻게 될까?
→ viewLeft 가 음수가 되어 constraintLeft 보다 왼쪽에 위치할 수 있음
50
패딩, 마진과 레이아웃의 관계
● 다음 조건에서 자식 뷰의 왼쪽 좌표를 계산하시오
○ 부모 뷰: 폭 300dp , 패딩 30dp
○ 자식 뷰: 폭 50dp, 좌우 마진 20dp
■ left to parent, right to parent
■ horizontal bias 0.2
viewLeft = constraintLeft + (constraintWidth - (viewWidth+marginSide) ) * bias + marginLeft
= 30 + ( 300 - 30 -30 - (50 + 20 + 20 ) ) * 0.2 + 20
= 30 + ( 240 - 90 ) * 0.2 + 20
= 30 + 30 + 20 = 80
→ 50dp 위치에 barrier를 만들어서 확인해보자!
51
패딩, 마진과 레이아웃의 관계
<android.support.constraint.ConstraintLayout
android:layout_width="300dp" android:padding="50dp">
<TextView
android:layout_width="100dp"/>
<TextView
android:layout_width="300dp"
app:layout_cHorizontal_bias="0.5"/>
<TextView
android:layout_width="100dp"
android:layout_marginLeft="50dp"/>
→ constraint width = 300 - 50 - 50 = 200
viewLeft = 50 + (200 - 100) * 0.5 = 50 + 50 = 100dp
→ viewLeft = 50 + (200 - 300) * 0.5 = 50 - 50 = 0dp
주의: 실제로 그려질 땐 50dp 위치부터 그려지고
텍스트 앞쪽은 짤림 → padding 50dp 의 영향
→ viewLeft = 50 + (200 - (100 + 50)) * 0.5 + 50
= 50 + 25 + 50 = 125dp
52
바라보는 뷰가 gone인 경우 - 체인
● 사라진 뷰는 없었던 뷰 취급
○ 뷰 크기는 0dp, 뷰의 마진도 0dp 처리
● 체인 헤드는 사라져도 여전히 헤드 역할을 함
○ A-B-C 체인에서 A가 gone일 경우, B가 새로운 헤더가 될까?
 아니오. 여전히 A가 체인 헤더의 역할을 함
53
바라보는 뷰가 gone인 경우 – 비 체인
● 사라진 뷰는 크기와 마진은 0dp인 채로 위치함
● 사라진 뷰를 참조하던 뷰는 여전히 해당 뷰를 참조함
○ 하지만 bias값이 있을 경우 이상하게 동작함  버그인가??
Left to parent
Right to parent
No bias
Left to parent
Right to parent
Bias=0
54
바라보는 뷰가 gone인 경우 - layout_goneMargin
● layout_goneMargin* : 바라보는 뷰의 visibility가 gone일 때 적용되는 마진
ex) A-B-C 체인에서
○ A가 gone 일 경우: B의 layout_goneMarginLeft가 동작
○ C가 gone 일 경우: B의 layout_goneMarginRight가 동작
● marginLeft와 goneMarginLeft 둘 다 적용될 상황에선 어떻게 동작할까?
→ goneMargin이 우선순위가 높음
● A-B-C 체인에서 B, C 모두 layout_goneMarginLeft를 선언했고,
A, B 모두 gone 된다면 B의 layout_goneMarginLeft는 동작할까?
→ 동작하지 않음. gone 된 뷰의 속성은 의미없음
55
참고자료 - ReleaseNote
● http://tools.android.com/recent/constraintlayoutbeta5isnowavailable : match_parent 크래시 위협
● https://androidstudio.googleblog.com/2017/02/constraintlayout-10-is-now-available.html
● https://androidstudio.googleblog.com/2017/03/constraintlayout-101-is-now-available.html
● https://androidstudio.googleblog.com/2017/03/constraintlayout-102-is-now-available.html
● https://androidstudio.googleblog.com/2017/05/constraintlayout-110-beta-1-release.html
● https://androidstudio.googleblog.com/2017/10/constraintlayout-110-beta-2.html
. packed chain의 0dp, 이 버전부턴 match_constraint로 동작함
. app:layout_constrainedWidth="true|false" 추가
● https://androidstudio.googleblog.com/2017/10/constraintlayout-110-beta-3-is-now.html
. Circular constraints 추가
● https://androidstudio.googleblog.com/2017/12/constraintlayout-110-beta-4.html
● https://androidstudio.googleblog.com/2018/02/constraintlayout-110-beta-5.html
● https://androidstudio.googleblog.com/2018/03/constraintlayout-110-beta-6.html
● https://androidstudio.googleblog.com/2018/04/constraintlayout-110.html
56
참고자료 - 구글 자료
● https://android-developers.googleblog.com/2017/08/understanding-
performance-benefits-of.html
● https://www.youtube.com/watch?list=PLWz5rJ2EKKc9e0d55YHgJFHXNZbGH
EXJX&time_continue=1&v=OHcfs6rStRo
● https://developer.android.com/reference/android/support/constraint/Constr
aintLayout.html
● https://developer.android.com/training/constraint-layout/index.html
● https://codelabs.developers.google.com/codelabs/constraint-layout/#0
57
참고자료 - 기타
● https://constraintlayout.com/
● https://academy.realm.io/posts/360-andev-2017-nicolas-roard-advanced-
constraintlayout/
● https://academy.realm.io/kr/posts/constraintlayout-it-can-do-what-now/
● https://proandroiddev.com/creating-awesome-animations-using-constraintlayout-
and-constraintset-part-i-390cc72c5f75
● https://medium.com/@rafael_toledo/whats-new-in-constraint-layout-1-1-x-
f0bdd4dbdfb3
● http://androidkt.com/constraintlayout/
● https://gist.github.com/kingori/fa4a71bf4e844b02d772d8c6d667fd32#file-
constraintlayout-xml
● https://github.com/kingori/Droidknight_ConstraintLayout
58
Q & A
끝

Weitere ähnliche Inhalte

Was ist angesagt?

SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)Takuto Wada
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう増田 亨
 
[부스트캠프 웹・모바일 7기 Tech Talk]이지훈_뉴비의 시점에서 바라본 Kotlin_suspend
[부스트캠프 웹・모바일 7기 Tech Talk]이지훈_뉴비의 시점에서 바라본 Kotlin_suspend[부스트캠프 웹・모바일 7기 Tech Talk]이지훈_뉴비의 시점에서 바라본 Kotlin_suspend
[부스트캠프 웹・모바일 7기 Tech Talk]이지훈_뉴비의 시점에서 바라본 Kotlin_suspendCONNECT FOUNDATION
 
ドメイン駆動設計をゲーム開発に活かす
ドメイン駆動設計をゲーム開発に活かすドメイン駆動設計をゲーム開発に活かす
ドメイン駆動設計をゲーム開発に活かす増田 亨
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるpospome
 
Unityでオニオンアーキテクチャ
UnityでオニオンアーキテクチャUnityでオニオンアーキテクチャ
Unityでオニオンアーキテクチャtorisoup
 
Kotlin의 코루틴은 어떻게 동작하는가
Kotlin의 코루틴은 어떻게 동작하는가Kotlin의 코루틴은 어떻게 동작하는가
Kotlin의 코루틴은 어떻게 동작하는가Chang W. Doh
 
Vue.jsでFormをAtomic Designしてみた時のコンポーネント間のデータのやりとり
Vue.jsでFormをAtomic Designしてみた時のコンポーネント間のデータのやりとりVue.jsでFormをAtomic Designしてみた時のコンポーネント間のデータのやりとり
Vue.jsでFormをAtomic Designしてみた時のコンポーネント間のデータのやりとりYuta Ohashi
 
Python におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころPython におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころJunya Hayashi
 
ソフトウェアにおける 複雑さとは何なのか?
ソフトウェアにおける 複雑さとは何なのか?ソフトウェアにおける 複雑さとは何なのか?
ソフトウェアにおける 複雑さとは何なのか?Yoshitaka Kawashima
 
実践に向けたドメイン駆動設計のエッセンス
実践に向けたドメイン駆動設計のエッセンス実践に向けたドメイン駆動設計のエッセンス
実践に向けたドメイン駆動設計のエッセンス増田 亨
 
JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc
JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_cccJPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc
JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_cccMasatoshi Tada
 
go_router が隠してくれるもの
go_router が隠してくれるものgo_router が隠してくれるもの
go_router が隠してくれるものcch-robo
 
Spring Social でソーシャルログインを実装する
Spring Social でソーシャルログインを実装するSpring Social でソーシャルログインを実装する
Spring Social でソーシャルログインを実装するRakuten Group, Inc.
 
View Customize Pluginで出来ること
View Customize Pluginで出来ることView Customize Pluginで出来ること
View Customize Pluginで出来ることonozaty
 
Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話Tomohiro Nakajima
 
reduxのstate設計の話
reduxのstate設計の話reduxのstate設計の話
reduxのstate設計の話ayatas0623
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているKoichi Tanaka
 

Was ist angesagt? (20)

SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみようドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計 ( DDD ) をやってみよう
 
[부스트캠프 웹・모바일 7기 Tech Talk]이지훈_뉴비의 시점에서 바라본 Kotlin_suspend
[부스트캠프 웹・모바일 7기 Tech Talk]이지훈_뉴비의 시점에서 바라본 Kotlin_suspend[부스트캠프 웹・모바일 7기 Tech Talk]이지훈_뉴비의 시점에서 바라본 Kotlin_suspend
[부스트캠프 웹・모바일 7기 Tech Talk]이지훈_뉴비의 시점에서 바라본 Kotlin_suspend
 
ドメイン駆動設計をゲーム開発に活かす
ドメイン駆動設計をゲーム開発に活かすドメイン駆動設計をゲーム開発に活かす
ドメイン駆動設計をゲーム開発に活かす
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
 
Unityでオニオンアーキテクチャ
UnityでオニオンアーキテクチャUnityでオニオンアーキテクチャ
Unityでオニオンアーキテクチャ
 
Kotlin의 코루틴은 어떻게 동작하는가
Kotlin의 코루틴은 어떻게 동작하는가Kotlin의 코루틴은 어떻게 동작하는가
Kotlin의 코루틴은 어떻게 동작하는가
 
Vue.jsでFormをAtomic Designしてみた時のコンポーネント間のデータのやりとり
Vue.jsでFormをAtomic Designしてみた時のコンポーネント間のデータのやりとりVue.jsでFormをAtomic Designしてみた時のコンポーネント間のデータのやりとり
Vue.jsでFormをAtomic Designしてみた時のコンポーネント間のデータのやりとり
 
Di入門
Di入門Di入門
Di入門
 
Python におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころPython におけるドメイン駆動設計(戦術面)の勘どころ
Python におけるドメイン駆動設計(戦術面)の勘どころ
 
ソフトウェアにおける 複雑さとは何なのか?
ソフトウェアにおける 複雑さとは何なのか?ソフトウェアにおける 複雑さとは何なのか?
ソフトウェアにおける 複雑さとは何なのか?
 
実践に向けたドメイン駆動設計のエッセンス
実践に向けたドメイン駆動設計のエッセンス実践に向けたドメイン駆動設計のエッセンス
実践に向けたドメイン駆動設計のエッセンス
 
JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc
JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_cccJPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc
JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc
 
go_router が隠してくれるもの
go_router が隠してくれるものgo_router が隠してくれるもの
go_router が隠してくれるもの
 
Spring Social でソーシャルログインを実装する
Spring Social でソーシャルログインを実装するSpring Social でソーシャルログインを実装する
Spring Social でソーシャルログインを実装する
 
実践 NestJS
実践 NestJS実践 NestJS
実践 NestJS
 
View Customize Pluginで出来ること
View Customize Pluginで出来ることView Customize Pluginで出来ること
View Customize Pluginで出来ること
 
Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話
 
reduxのstate設計の話
reduxのstate設計の話reduxのstate設計の話
reduxのstate設計の話
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っている
 

Mehr von Sewon Ann

Flipper 불완전 정복
Flipper 불완전 정복Flipper 불완전 정복
Flipper 불완전 정복Sewon Ann
 
Android studio 디버거 조금 더 잘 쓰기
Android studio 디버거 조금 더 잘 쓰기Android studio 디버거 조금 더 잘 쓰기
Android studio 디버거 조금 더 잘 쓰기Sewon Ann
 
안드로이드 개발에 유용한 도구들
안드로이드 개발에 유용한 도구들안드로이드 개발에 유용한 도구들
안드로이드 개발에 유용한 도구들Sewon Ann
 
백엔드 서버 구축없이 모바일 앱앱 만들어보기
백엔드 서버 구축없이 모바일 앱앱 만들어보기백엔드 서버 구축없이 모바일 앱앱 만들어보기
백엔드 서버 구축없이 모바일 앱앱 만들어보기Sewon Ann
 
Framer js a/s talk
Framer js a/s talkFramer js a/s talk
Framer js a/s talkSewon Ann
 
develop android app using intellij
develop android app using intellijdevelop android app using intellij
develop android app using intellijSewon Ann
 
ant로 안드로이드 앱을 자동으로 빌드하자
ant로 안드로이드 앱을 자동으로 빌드하자ant로 안드로이드 앱을 자동으로 빌드하자
ant로 안드로이드 앱을 자동으로 빌드하자Sewon Ann
 
Google Hackathon Korea - hangout mafia
Google Hackathon Korea - hangout mafiaGoogle Hackathon Korea - hangout mafia
Google Hackathon Korea - hangout mafiaSewon Ann
 
Spring 3의 jsr 303 지원
Spring 3의 jsr 303 지원Spring 3의 jsr 303 지원
Spring 3의 jsr 303 지원Sewon Ann
 

Mehr von Sewon Ann (9)

Flipper 불완전 정복
Flipper 불완전 정복Flipper 불완전 정복
Flipper 불완전 정복
 
Android studio 디버거 조금 더 잘 쓰기
Android studio 디버거 조금 더 잘 쓰기Android studio 디버거 조금 더 잘 쓰기
Android studio 디버거 조금 더 잘 쓰기
 
안드로이드 개발에 유용한 도구들
안드로이드 개발에 유용한 도구들안드로이드 개발에 유용한 도구들
안드로이드 개발에 유용한 도구들
 
백엔드 서버 구축없이 모바일 앱앱 만들어보기
백엔드 서버 구축없이 모바일 앱앱 만들어보기백엔드 서버 구축없이 모바일 앱앱 만들어보기
백엔드 서버 구축없이 모바일 앱앱 만들어보기
 
Framer js a/s talk
Framer js a/s talkFramer js a/s talk
Framer js a/s talk
 
develop android app using intellij
develop android app using intellijdevelop android app using intellij
develop android app using intellij
 
ant로 안드로이드 앱을 자동으로 빌드하자
ant로 안드로이드 앱을 자동으로 빌드하자ant로 안드로이드 앱을 자동으로 빌드하자
ant로 안드로이드 앱을 자동으로 빌드하자
 
Google Hackathon Korea - hangout mafia
Google Hackathon Korea - hangout mafiaGoogle Hackathon Korea - hangout mafia
Google Hackathon Korea - hangout mafia
 
Spring 3의 jsr 303 지원
Spring 3의 jsr 303 지원Spring 3의 jsr 303 지원
Spring 3의 jsr 303 지원
 

지금은 Constraint layout 시대

  • 1.
  • 2. 발표자 안세원 (kingori@gmail.com) ● 카카오모빌리티 안드로이드 앱 개발자 ● GDG Korea ○ #android, #pangyo 채널 죽돌이 ● 최근 참여 앱 ○ 카카오 T 택시 기사용 ○ 카카오 T ○ 카카오톡 2
  • 3. ConstraintLayout ● 복잡한 레이아웃을 단순한 계층구조를 이용해 표현할 수 있는 ViewGroup ● 형제 View 들과의 관계를 정의해서 레이아웃을 구성한다는 점은 RelativeLayout과 비슷하지만, 보다 유연하고 다양한 기능을 제공함 https://developer.android.com/training/constraint-layout/index.html 3
  • 4. 왜 써야 하죠? ● RelativeLayout 에선 불가능했던 자식 뷰 간의 상호 관계 정의 가능 ex) 두 View를 위 아래로 붙여서 컨테이너 중앙에 배치하기 ● LinearLayout을 써야만 했던 뷰 비율 조절도 간단히 가능 ● 뷰 계층을 간단하게 할 수 있어 유지보수도 좋고 성능도 좋고! 4
  • 5. 다루는 내용  ConstraintLayout 1.1 의 거의 모든 기능 ○ MATCH_CONSTRAINT ○ Dimension ratio ○ Percent ○ Bias ○ Guideline ○ Chain ○ Barrier ○ Placeholder ○ ConstraintSet ○ Group 5
  • 9. 안다루는 내용 ● Circular Positioning ● Optimizer ● Android Studio의 Layout editor 활용 9
  • 10. 릴리즈 이력 ● 2017.2 : 1.0 ● 2017.3 : 1.0.2 ● 2018.4 : 1.1.0 ● 20XX.X : 2.0 ? 10
  • 11. Gradle 설정 repositories { maven { url 'https://maven.google.com' } } dependencies { compile 'com.android.support.constraint:constraint-layout:1.1.0' } 11
  • 13. Constraint ● ConstraintLayout에서 자식 뷰의 위치를 잡는 기준 ● 다른 뷰나 부모 레이아웃과의 정렬 조건 / 연결 관계를 나타냄 ● layout_constraint[기준1]_to[기준2]of="[viewId|parent]" ● start, end 속성은 left, right 속성보다 우선됨 <Button android:id="@+id/buttonB" ... app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintRight_toLeftOf="@id/buttonA" app:layout_constraintBottom_toBottomOf="parent"/> 13
  • 14. Constraint ● RelativeLayout의 alignLeft / toLeftOf 와는 다름 ○ constraint는 위치/ 크기의 기준을 잡는 역할일 뿐 ○ 이와 달리 RelativeLayout의 align 등은 위치 자체를 정의함 ● Top/Bottom/Left/Right를 모두 선언해야 하는 것은 아님 ex) android:layout_width=“wrap_content” android:layout_height=“wrap_content” app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" → 부모 뷰 좌상단 정렬 ○ 하지만 체인 등 다른 기능 사용시 예상치 못한 동작이 발생할 수 있음!  가급적 4방향 모두 선언하는 습관을 들이자 14
  • 15. 뷰 크기 ● android:layout_width / layout_height 속성 사용 ○ 고정 크기 ex) android:layout_width="100dp" ○ wrap_content ○ match_constraint ● match_parent가 아닌 match_constraint! ○ 크기는 0dp : constraint에 꽉 차게 크기를 맞춤 ○ match_constraint라는 속성 값은 없다! → 0dp 이라 쓰고 match_constraint라고 읽음 ○ 제대로 동작하려면 뷰의 양쪽 constraint를 다 선언해줘야 함 (ex. left 와 right) ● match_parent는 사용하지 말 것! ○ 1.0.0 beta-5 에선 아예 예외를 던졌으나, 다음 버전에서 수정되었음 ○ 그래도 권장하지 않으니 사용하지 말 것 15
  • 17. 뷰 크기 - Percent size ● 부모 뷰의 크기에 비례하여 뷰의 크기를 결정함 ○ 단, padding은 제외한 크기! ● layout_width 는 match_constraint로 선언 ● layout_constraint[Width|Height]_default: 대상 축의 constraint의 계산 방식 ○ spread: constraint 영역에 맞춤 (기본 동작) ○ wrap: 뷰의 크기에 맞춤 ○ percent: constraint[Width|Height]_percent 속성에 선언한 비율에 맞춤 android:layout_width="0dp" app:layout_constraintWidth_default="percent" app:layout_constraintWidth_percent="0.4" 17
  • 18. 뷰 크기 – 가로/세로 비율 지정 ● dimensionRatio : 뷰의 가로/세로 비율 결정 ○ app:layout_constraintDimensionRatio="1" → 가로/세로 = 1/1 ○ app:layout_constraintDimensionRatio="1:1" → 가로:세로 = 1:1 ● 적어도 한 방향은 match_constraint 여야 함! ● 두 방향 모두 match_constraint 일 경우, 비율에 맞춰 constraint 내에서 가장 큰 크기로 설정 18
  • 19. 뷰 크기 – 가로/세로 비율 지정 <Button android:layout_width="150dp" android:layout_height="0dp" android:text="HAHA" app:layout_cDimensionRatio="1:1"/> <Button android:layout_width="0dp" android:layout_height="0dp" android:text="HAHA" app:layout_cDimensionRatio="2"/> <Button android:layout_width="wrap_content" android:layout_height="0dp" android:text="HAHA" app:layout_cDimensionRatio="1:2"/> 19
  • 20. 뷰 크기 – 가로/세로 비율 지정 ● 명시적으로 비율을 적용할 축을 지정할 수 있음 : (W|H,)[비율] ○ W: 높이에 맞춰 폭을 조정 ○ H: 폭에 맞춰 높이를 조정 ● 폭 match_constraint, 높이 match_constraint인 경우 ○ H,1:2: 폭을 constraint에 맞춰 설정한 후, 비율에 따라 높이를 결정 ○ W,1:2: 높이를 constraint에 맞춰 설정한 후, 비율에 따라 폭을 결정 ● 폭 wrap_content, 높이 match_constraint인 경우 ○ H,1:2: 폭을 wrap_content에 맞춰 설정한 후, 비율에 따라 높이를 결정 ○ W,1:2: 폭이 wrap_content로 결정되어 버렸기 때문인지 역으로 2:1 비율이 설정되어 버림(??) 20
  • 21. 뷰 크기 – 가로/세로 비율 지정 <Button android:layout_width="0dp" android:layout_height="0dp" android:text="HAHA" app:layout_cDimensionRatio ="W,1:2"/> <Button android:layout_width="0dp" android:layout_height="0dp" android:text="HAHA" app:layout_cDimensionRatio ="H,1:2"/>  넘쳐버림 <Button android:layout_width="wrap_content" android:layout_height="0dp" android:text="HAHA" app:layout_cDimensionRatio ="W,1:2"/>  2:1 이 되어버림 <Button android:layout_width="wrap_content" android:layout_height="0dp" android:text="HAHA" app:layout_cDimensionRatio ="H,1:2"/> 21
  • 22. 뷰 크기 - 최소/최대 ● 최소/최대 크기 지정 : app:layout_constraintWidth_[min|max]="size" ● android:minWidth와 다른점 ○ wrap_content 일 땐 android:[min|max]width 적용 ○ mactch_constraint 일 땐 app:layout_constraintWidth_[min|max] 적용 <Button android:layout_width="wrap_content" android:minWidth="100dp" app:layout_constraintWidth_min="200dp" … /> <Button android:layout_width="0dp" android:maxWidth="100dp" app:layout_constraintWidth_max="200dp" … /> 22
  • 23. 위치 지정 - bias ● bias: (constraint 영역 크기 - 뷰 크기) 를 분배하는 비율 ○ app:layout_constraintHorizontal_bias="0~1" ■ 0: 왼쪽에 붙이기 / 1: 오른쪽에 붙이기 / 0.5: 중간에 위치 (기본값) <Button android:layout_width="wrap_content" app:layout_constraintHorizontal_bias="0" … /> <Button android:layout_width="wrap_content" app:layout_constraintHorizontal_bias="1" … /> 23
  • 24. 위치 지정 - guideline ● android.support.constraint.Guideline ○ 가로 또는 세로 축 방향을 가진 가상의 뷰 ● 부모 뷰의 특정 위치를 기준점으로 삼을 때 사용 ● 축, 위치 값을 속성으로 가짐 ○ 축: android:orientation="[vertical|horizontal]" ○ 위치 ■ app:layout_constraintGuide_begin : 시작 지점으로 부터의 거리 ■ app:layout_constraintGuide_end : 끝 지점으로 부터의 거리 ■ app:layout_constraintGuide_percent : 시작 지점으로 부터의 % 위치 24
  • 25. 위치 지정 - guideline <android.support.constraint.Guideline android:id="@+id/gd_left" app:layout_constraintGuide_begin="100dp" android:orientation="vertical"/> <android.support.constraint.Guideline android:id="@+id/gd_right" app:layout_constraintGuide_end="50dp" android:orientation="vertical"/> <android.support.constraint.Guideline android:id="@+id/gd_bottom" app:layout_constraintGuide_percent="0.8" android:orientation="horizontal"/> <Button app:layout_constraintLeft_toRightOf="@id/gd_left" app:layout_constraintRight_toLeftOf="@id/gd_right" app:layout_constraintBottom_toTopOf="@id/gd_bottom" app:layout_constraintTop_toTopOf="parent" android:layout_width="0dp" android:layout_height="0dp" android:text="Button"/> 25
  • 27. 체인 ● 서로 연결되어 그룹으로 동작하는 뷰의 묶음 ● 체인으로 연결된 뷰 끼리도 체인이 연결된 방향으로만 그룹으로 동작함 ● 생성 조건: 마주보는 뷰끼리 마주보는 방향으로 서로 constraint를 설정함 A B A B 체인? right to left of B left to right of A O right to left of B right to right of A X right to left of B top to top of A X 27
  • 28. 체인 ● 체인 스타일의 종류 https://developer.android.com/reference/android/support/constraint/ConstraintLayout.html#Chains 28
  • 29. 체인 ● 체인 스타일 별 설정 방법 ○ spread: 헤드에 app:layout_constraintHorizontal_chainStyle="spread" 선언 ○ spread_inside: 헤드에 app:layout_constraintHorizontal_chainStyle="spread_inside" 선언 ○ packed: 헤드에 app:layout_constraintHorizontal_chainStyle="packed" 선언 ○ packed with bias: packed 헤드에 app:layout_constraintHorizontal_bias="0~1" 선언 ○ weighted: match_constraint 인 뷰가 포함된 체인의 뷰에 app:layout_constraintHorizontal_weight 속성 선언 ● 체인 헤드: 위치 상 체인의 가장 앞쪽(왼쪽 혹은 윗쪽)에 위치한 뷰 29
  • 30. 체인 - spread ● (체인의 constraint 영역 - 뷰 크기의 합) 을 균등분할하여 배치 ● 체인 바깥 마진은 constraint 영역에서 제외 ● 체인 안쪽 마진은 마진을 가진 뷰의 크기에 합산 <Button android:id="@+id/btn_a" android:layout_width="100dp" android:layout_marginLeft="50dp" app:layout_cLeft_toLeftOf="parent" app:layout_cRight_toLeftOf="@id/btn_b" app:layout_cHorizontal_chainStyle="spread"/> <Button android:id="@+id/btn_b" android:layout_width="100dp" android:layout_marginLeft="20dp" app:layout_cLeft_toRightOf="@id/btn_a" app:layout_cRight_toRightOf="parent"/> constraint 영역 = 300 - 50 = 250 view 크기 = 100 + 100 + 20 = 220 간격 = (250 - 220 )/ 3 = 10 A의 왼쪽 = 50 + 10 = 60 B의 왼쪽 = 60 + 100 + 10 + 20 = 190 30
  • 31. 체인 - spread_inside ● 체인의 양 끝 뷰를 constraint영역의 양 끝에 배치하고, 나머지 뷰를 남는 공 간을 균등분할하여 배치 <Button android:id="@+id/btn_a" android:layout_width="100dp" android:layout_marginLeft="20dp" app:layout_cLeft_toLeftOf="parent" app:layout_cRight_toLeftOf="@id/btn_b" app:layout_cHorizontal_chainStyle="spread_inside"/> <Button android:id="@+id/btn_b" android:layout_width="100dp" android:layout_marginRight="20dp" app:layout_cLeft_toRightOf="@id/btn_a" app:layout_cRight_toRightOf="parent"/> 31
  • 32. 체인 - packed / packed biased ● 체인을 구성하는 각 뷰를 밀착하여 배열 ● (constraint 영역) - (뷰 폭의 합) 을 bias에 따라 분배 <Button android:id="@+id/btn_a" android:layout_width="100dp" app:layout_cLeft_toLeftOf="parent" app:layout_cRight_toLeftOf="@id/btn_b" app:layout_cHorizontal_chainStyle="packed"/> <Button android:id="@+id/btn_b" android:layout_width="100dp" android:layout_marginRight="20dp" android:layout_marginLeft="20dp" app:layout_cLeft_toRightOf="@id/btn_a" app:layout_cRight_toRightOf="parent"/> constraint 영역 = 300 - 20 = 280 view 크기 = 100 + 100 + 20 = 220 남는 공간 = (280 - 220 )/ 2 = 30 A의 왼쪽 = 30 B의 왼쪽 = 30 + 100 + 20 = 150 32
  • 33. 체인 - weighted ● 크기가 match_constraint인 뷰가 하나 이상 포함된 체인 ● match_constraint 뷰에 app:layout_constraintHorizontal_weight 속성으로 비율을 지정 ● 주의: 모든 match_constraint 뷰에 빠짐없이 weight 속성을 지정해야 함! <Button android:id="@+id/btn_a" android:layout_width="0dp" app:layout_cLeft_toLeftOf="parent" app:layout_cRight_toLeftOf="@id/btn_b" app:layout_constraintHorizontal_weight="2"/> <Button android:id="@+id/btn_b" android:layout_width="0dp" android:layout_marginRight="10dp" android:layout_marginLeft="20dp" app:layout_cLeft_toRightOf="@id/btn_a" app:layout_cRight_toRightOf="parent" app:layout_constraintHorizontal_weight="1"/> 뷰가 차지할 수 있는 크기 = 300 - 20 - 10 = 270 A의 크기 = 270 /3 * 2 = 180 B의 크기 = 270 /3 * 1 = 90 33
  • 34. Barrier ● 여러 뷰의 가장자리 위치에 만드는 가상의 뷰 ● 복잡한 양식 등을 만드는데 활용할 수 있음 34
  • 35. Barrier <TextView android:id="@+id/tv_1" android:text="name:" android:layout_width="wrap_content android:layout_height="wrap_content" app:layout_cTop_toTopOf="parent app:layout_cLeft_toLeftOf="parent" app:layout_cBottom_toBottomOf="parent" app:layout_cRight_toRightOf="parent" app:layout_cVertical_bias="0" app:layout_cHorizontal_bias="0" android:layout_margin="8dp" /> <TextView android:id="@+id/tv_2" android:text="passwd:" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_cTop_toBottomOf="@id/tv_1" app:layout_cLeft_toLeftOf="parent" app:layout_cBottom_toBottomOf="parent" app:layout_cRight_toRightOf="parent" app:layout_cVertical_bias="0" app:layout_cHorizontal_bias="0" android:layout_margin="8dp"/> <android.support.constraint.Barrier android:id="@+id/br_label" android:layout_width="0dp" android:layout_height="0dp" app:barrierDirection="right" app:constraint_referenced_ids="tv_1,tv_2" /> 35
  • 36. Group ● 여러 뷰의 visibility를 한꺼번에 조정 ○ 그룹의 visibility를 바꾸면 그룹에 속한 모든 뷰의 visiblity가 바뀜 ⇒ flat해진 구조 덕분에 여러개의 뷰의 visibility를 바꿔야 할 때 유용함 ● 하나의 뷰가 여러 그룹에 속할 경우, xml에 마지막으로 선언된 그룹의 visibility를 따름 <android.support.constraint.Group android:id="@+id/grp_1" android:layout_width="wrap_content" android:layout_height="wrap_content" app:constraint_referenced_ids="tv_1,tv_3" android:visibility="gone"/> 36
  • 37. ConstraintSet ● 프로그램적으로 constraint를 만드는 기능 ○ 일일이 바닥부터 만들어내거나 ○ 다른 xml로 부터 constraint만 뽑아오거나 ○ ConstraintLayout 인스턴스에서 뽑아오거나 ● 만들어진 constraint를 ConstraintLayout에 적용할 수 있음 ● 동적으로 ConstraintLayout의 모든 뷰 혹은 일부 뷰의 레이아웃을 갱신할 수 있음 ● ConstraintSet은 constraint만 갱신하므로 constraint와 관련 없는 속성 (padding, text size, …)은 영향받지 않음 ● TransitionManager.beginDelayedTransition() 을 이용하여 손쉽게 애니메이션 생성 가능 37
  • 38. ConstraintSet constraintSet2 = ConstraintSet() constraintSet2.clone(content) constraintSet2.connect(R.id.icon_2, ConstraintSet.TOP, R.id.btn_layout1, ConstraintSet.TOP ) constraintSet2.constrainWidth(R.id.icon_2, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120f, resources.displayMetrics).toInt()) ... 38
  • 39. ConstraintSet ● ConstraintSet A를 ConstraintSet B로 갱신한 경우 ○ A엔 있으나 B에는 없는 뷰 : 이전 constraint 유지 ○ A엔 없으나 B에는 있는 뷰 : 무시됨 ○ A에선 V1, V2가 체인이었으나 B에선 V1만 언급하며, 체인 관계가 깨진 경우 : 체인 관계는 깨지며, V2 의 위치는 다시 설정됨 39
  • 40. Placeholder ● 기존 뷰의 위치를 재조정하는 가상의 뷰 ● Placeholder가 대체한 원래의 뷰는 사라짐 <Button android:id="@+id/btn" android:text="btn" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_cTop_toTopOf="parent" app:layout_cLeft_toLeftOf="parent" /> <android.support.constraint.Placeholder android:id="@+id/ph_1" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_cRight_toRightOf="parent" app:layout_cBottom_toBottomOf="parent"/> ph_1.setContentId(R.id.btn) //ph_1 자리로 btn을 옮김 40
  • 41. Placeholder - 용도 ● 동일한 속성을 가진 뷰를 배치만 다른 레이아웃에서 재사용 → 가로/세로 레이아웃에서 view를 중복해서 선언하지 않아도 됨 → 하지만 view 크기 컨트롤에 문제가 있음 <ConstLayout> <View1 id="@+id/v1" attr1="v1" attr2="v2" .../> ... <include layout="holder"/> </ConstLayout> <merge> <Placeholder id="p1" const1=".." app:content="@id/v1"> ... /> </merge> <merge> <Placeholder id="p1" const1=".." app:content="@id/v1"/> ... /> </merge> layout/holder.xmllayout/layout.xml layout-land/holder.xml 41
  • 42. Placeholder - 용도 ● Runtime에 하나 혹은 여러 뷰의 위치를 바꿈 ● 그 용도로 ConstraintSet을 만들지 않았나? ○ xml 상에서 간단하게 선언할 수 있어 편함 ○ 하나의 placeholder에 여러 뷰를 번갈아가며 위치시킬 수 있음 http://androidkt.com/constraintlayout/42
  • 44. wrap_content ● 텍스트가 긴 TextView에선 wrap_content가 의도치 않게 동작할 수 있음 ○ margin 무시됨 ○ 측정해 보면 constraint 크기가 아닌 parent 크기까지 폭이 늘어남 <TextView android:layout_width="wrap_content" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" android:text="asdfasdfasdlfjasdf;asdlf;jasd;flkjasdf;l..." android:layout_margin="50dp" android:maxLines="1" android:ellipsize="end" /> 44
  • 45. wrap_content ● wrap_content를 constraint 영역 내에 제대로 표현하려면 추가 설정 필요 ○ 크기는 android:layout_width="wrap_content" ○ app:layout_constrainedWidth="true" 설정을 추가 ○ 1.0.2 에선 match_constraint & layout_constraintWidth_default="wrap"  이 조합은 1.1.0 에서 deprecate됨 <TextView android:text="asdfasdfasdlfjasdf;asdlf;ja..." android:layout_width="wrap_content" app:layout_constraintedWidth="wrap" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" android:layout_margin="50dp" android:maxLines="1" android:ellipsize="end"/> 짧은 텍스트 긴 텍스트 45
  • 46. 패딩, 마진과 레이아웃의 관계 ● 부모 뷰의 패딩은 constraint 영역에서 빼야 함 ex) 300dp 폭, 좌우 패딩 50dp 이라면 constraint 영역은 300-(50)*2 = 200dp ● guideline은 부모 뷰의 패딩을 적용한 위치에 만들어짐 46
  • 47. 패딩, 마진과 레이아웃의 관계 <android.support.constraint.ConstraintLayout android:layout_width="300dp" android:layout_height="300dp" android:paddingLeft="100dp" android:paddingRight=“50dp"> <android.support.constraint.Guideline android:orientation="vertical" app:layout_constraintGuide_begin="50dp" /> → padding+begin = 100 + 50 = 150dp 위치에 생성 <android.support.constraint.Guideline android:orientation="vertical" app:layout_constraintGuide_end="50dp"/> → width - padding - end = 300- 50 - 50 = 200dp 위치에 생성 <android.support.constraint.Guideline android:orientation="vertical" app:layout_constraintGuide_percent="0.5"/> → paddingLeft + ( constraintWidth ) / 2 = 100 + ( 300- 100 - 50 ) / 2 = 100 + 75 = 175dp 위치에 생성 47
  • 48. 패딩, 마진과 레이아웃의 관계 ● 체인이 아니어도 다른 뷰와 constraint 관계를 맺을 수 있으나, 상대 뷰의 margin은 고려되지 않음 → margin까지 고려하려면 chain 관계를 가져야 함 <Button android:id="@+id/btn_a" android:layout_width="wrap_content" android:layout_marginLeft="50dp" android:layout_marginRight="50dp"/> <Button android:id="@+id/btn_b" android:layout_width="wrap_content" app:layout_cLeft_toRightOf="@id/btn_a"/>  B는 A의 marginRight를 고려하지 않음 48
  • 49. 패딩, 마진과 레이아웃의 관계 ● 다음 조건에서 자식 뷰의 왼쪽 좌표를 계산하시오 ○ 부모 뷰: 폭 300dp , 패딩 30dp ○ 자식 뷰: 폭 50dp, 좌우 마진 20dp ■ left to parent, right to parent ■ horizontal bias 0.2 ? 49
  • 50. 패딩, 마진과 레이아웃의 관계 ● view의 왼쪽 좌표 계산식 viewLeft = constraintLeft + (constraintWidth - (viewWidth+marginSide) ) * bias + marginLeft ○ constraintLeft ■ parent일 경우: parent.Left + parent.PaddingLeft ■ 특정 view A 의 오른쪽을 기준으로 할 경우: (constraintLeft_toRightOf="A" ) a.right + a.marginRight* * 둘이 packed chain style 관계일 때에만! ○ constraintWidth = constraintRight - constraintLeft ○ marginSide = marginLeft + marginRight ● viewWidth 가 constraintWidth 보다 크면 어떻게 될까? → viewLeft 가 음수가 되어 constraintLeft 보다 왼쪽에 위치할 수 있음 50
  • 51. 패딩, 마진과 레이아웃의 관계 ● 다음 조건에서 자식 뷰의 왼쪽 좌표를 계산하시오 ○ 부모 뷰: 폭 300dp , 패딩 30dp ○ 자식 뷰: 폭 50dp, 좌우 마진 20dp ■ left to parent, right to parent ■ horizontal bias 0.2 viewLeft = constraintLeft + (constraintWidth - (viewWidth+marginSide) ) * bias + marginLeft = 30 + ( 300 - 30 -30 - (50 + 20 + 20 ) ) * 0.2 + 20 = 30 + ( 240 - 90 ) * 0.2 + 20 = 30 + 30 + 20 = 80 → 50dp 위치에 barrier를 만들어서 확인해보자! 51
  • 52. 패딩, 마진과 레이아웃의 관계 <android.support.constraint.ConstraintLayout android:layout_width="300dp" android:padding="50dp"> <TextView android:layout_width="100dp"/> <TextView android:layout_width="300dp" app:layout_cHorizontal_bias="0.5"/> <TextView android:layout_width="100dp" android:layout_marginLeft="50dp"/> → constraint width = 300 - 50 - 50 = 200 viewLeft = 50 + (200 - 100) * 0.5 = 50 + 50 = 100dp → viewLeft = 50 + (200 - 300) * 0.5 = 50 - 50 = 0dp 주의: 실제로 그려질 땐 50dp 위치부터 그려지고 텍스트 앞쪽은 짤림 → padding 50dp 의 영향 → viewLeft = 50 + (200 - (100 + 50)) * 0.5 + 50 = 50 + 25 + 50 = 125dp 52
  • 53. 바라보는 뷰가 gone인 경우 - 체인 ● 사라진 뷰는 없었던 뷰 취급 ○ 뷰 크기는 0dp, 뷰의 마진도 0dp 처리 ● 체인 헤드는 사라져도 여전히 헤드 역할을 함 ○ A-B-C 체인에서 A가 gone일 경우, B가 새로운 헤더가 될까?  아니오. 여전히 A가 체인 헤더의 역할을 함 53
  • 54. 바라보는 뷰가 gone인 경우 – 비 체인 ● 사라진 뷰는 크기와 마진은 0dp인 채로 위치함 ● 사라진 뷰를 참조하던 뷰는 여전히 해당 뷰를 참조함 ○ 하지만 bias값이 있을 경우 이상하게 동작함  버그인가?? Left to parent Right to parent No bias Left to parent Right to parent Bias=0 54
  • 55. 바라보는 뷰가 gone인 경우 - layout_goneMargin ● layout_goneMargin* : 바라보는 뷰의 visibility가 gone일 때 적용되는 마진 ex) A-B-C 체인에서 ○ A가 gone 일 경우: B의 layout_goneMarginLeft가 동작 ○ C가 gone 일 경우: B의 layout_goneMarginRight가 동작 ● marginLeft와 goneMarginLeft 둘 다 적용될 상황에선 어떻게 동작할까? → goneMargin이 우선순위가 높음 ● A-B-C 체인에서 B, C 모두 layout_goneMarginLeft를 선언했고, A, B 모두 gone 된다면 B의 layout_goneMarginLeft는 동작할까? → 동작하지 않음. gone 된 뷰의 속성은 의미없음 55
  • 56. 참고자료 - ReleaseNote ● http://tools.android.com/recent/constraintlayoutbeta5isnowavailable : match_parent 크래시 위협 ● https://androidstudio.googleblog.com/2017/02/constraintlayout-10-is-now-available.html ● https://androidstudio.googleblog.com/2017/03/constraintlayout-101-is-now-available.html ● https://androidstudio.googleblog.com/2017/03/constraintlayout-102-is-now-available.html ● https://androidstudio.googleblog.com/2017/05/constraintlayout-110-beta-1-release.html ● https://androidstudio.googleblog.com/2017/10/constraintlayout-110-beta-2.html . packed chain의 0dp, 이 버전부턴 match_constraint로 동작함 . app:layout_constrainedWidth="true|false" 추가 ● https://androidstudio.googleblog.com/2017/10/constraintlayout-110-beta-3-is-now.html . Circular constraints 추가 ● https://androidstudio.googleblog.com/2017/12/constraintlayout-110-beta-4.html ● https://androidstudio.googleblog.com/2018/02/constraintlayout-110-beta-5.html ● https://androidstudio.googleblog.com/2018/03/constraintlayout-110-beta-6.html ● https://androidstudio.googleblog.com/2018/04/constraintlayout-110.html 56
  • 57. 참고자료 - 구글 자료 ● https://android-developers.googleblog.com/2017/08/understanding- performance-benefits-of.html ● https://www.youtube.com/watch?list=PLWz5rJ2EKKc9e0d55YHgJFHXNZbGH EXJX&time_continue=1&v=OHcfs6rStRo ● https://developer.android.com/reference/android/support/constraint/Constr aintLayout.html ● https://developer.android.com/training/constraint-layout/index.html ● https://codelabs.developers.google.com/codelabs/constraint-layout/#0 57
  • 58. 참고자료 - 기타 ● https://constraintlayout.com/ ● https://academy.realm.io/posts/360-andev-2017-nicolas-roard-advanced- constraintlayout/ ● https://academy.realm.io/kr/posts/constraintlayout-it-can-do-what-now/ ● https://proandroiddev.com/creating-awesome-animations-using-constraintlayout- and-constraintset-part-i-390cc72c5f75 ● https://medium.com/@rafael_toledo/whats-new-in-constraint-layout-1-1-x- f0bdd4dbdfb3 ● http://androidkt.com/constraintlayout/ ● https://gist.github.com/kingori/fa4a71bf4e844b02d772d8c6d667fd32#file- constraintlayout-xml ● https://github.com/kingori/Droidknight_ConstraintLayout 58
  • 59. Q & A
  • 60.