View
Android의 UI는 ViewGroup과 View의 계층으로 이루어짐
출처 : Good Joon :: 안드로이드 개발 #4 - 첫 UI 개발
View란?
Activity에서 화면을 구성하는 최소 단위(Widget들의 기본 Class) 로 화면에 보이는 모든 것
View를 상속하는 Class 예 : TextView ImageView, EditText, Button 등
ViewGruop
n개의 View를 담을 수 있는 Container로 ViewGroup 또한 View를 상속받아 만든 Class입니다.
ViewGroup를 상속하는 Class 예 : TextView ImageView, EditText, Button 등
출처: Android UI Layouts Tutorial
View LifeCycle
출처 : Android View Lifecycle
Category |
Method |
Description |
Creation |
Constructors |
First form of the Constructor 1. code에 의해 생성될 때 2. layout file이 inflate 될 때
Second/Third form of the Constructor 1. layout file에서 attribute를 적용할 때
|
onFinishInflate() |
View와 View의 children들이 XML에서 inflate 될 때 호출 |
|
Layout |
onMeasure(int, int) |
해당 View와 View의 children에 대해 size 요구 사항을 결정하기 위해 호출 |
onLayout(boolean, int, int, int, int) |
해당 View와 View의 children의 크기와 위치를 할당할 때 호출 |
|
onSizeChanged(int, int, int, int) |
View의 Size가 변경 되었을 때 호출 |
|
Drawing |
onDraw(android.graphics.Canvas) |
View의 contents를 Cavnas를 통해 render할 때 호출 (많은 시간이 소요됨) |
Event
Processing
|
onKeyDown(int, KeyEvent) |
새로운 하드웨어 키 이벤트가 발생했을 때 호출 |
onKeyUp(int, KeyEvent) |
하드웨어 키 이벤트가 up 됐을 때 호출 |
|
onTrackballEvent(MotionEvent) |
트랙볼 motion 이벤트가 발생했을 때 호출 |
|
onTouchEvent(MotionEvent) |
touch screen에 motion 이벤트가 발생했을 때 호출 |
|
Focus |
onFocusChanged(boolean, int, android.graphics.Rect) |
View의 focus가 얻거나 잃었을 때 호출 |
onWindowFocusChanged(boolean) |
Window가 담고 있는 View의 focus가 얻거나 잃었을 때 호출 |
|
Attaching |
onAttachedToWindow() |
View가 Window에 붙었을 때 호출 이때부터 View의 id를 통해 접근할 수 있음. |
onDetachedFromWindow() |
View가 Window에 분리되었을 때 호출 |
|
onWindowVisibilityChanged() |
Window가 담고있는 View의 visibility가 변경되었을 때 호출 |
onMeasure() vs onLayout()
onMeasure()
View의 크기를 결정할 때 불리는 함수이다. View는 Layout 안에서 각 View 마다 자신의 width(Member Varialbe : mMeasureWidth)나 height(Member Variable : mMeasureHeight)의 값을 가질 수 있다.
onMeasure() 호출 과정
View.measure() -> View.onMeasure() -> View.setMeasuredDimension()
onMeasure() 함수 override 시 내부에서는 setMeasureDimension() 함수를 호출해서 자신의 크기를 설정해야 함. 따라서 override 시에는 super.onMeasure()를 필수로 호출해야 한다.
Child View는 Parent View보다 크게 그려지지 않음. Parent View보다 크게 그려지는 부분은 보이지 않음.
내부에서 많이 쓰이는 함수
1234
// 자식 뷰의 크기를 계산시킨다.
this.measureChild()
// measureChildren()에서 사용하는 하나의 메소드이다. 이 메소드를 통해 특정 자식 view의 MeasureSpce을 만들수 있다.
this.getChildMeasureSpec()
onLayout()
Child View의 위치를 잡아줌. onMeasure() 호출 후에 onLayout()이 호출되므로 자신의 크기를 이미 인지하는 상태에서 위치를 잡아 줄 수 있다. gravity에 따라 View의 위치 조정을 할 수 있으므로 gravity 설정과 관련된 계산도 onLayout()에서 해준다.
onLayout() 호출 과정
ViewGroup.layout() -> View.layout() -> View.onLayout()
내부에서 많이 쓰이는 함수
12345678910111213141516171819202122
// 자신의 왼쪽 패딩값
this.getPaddingLeft()
// 자신의 위쪽 패딩값
this.getPaddingTop()
// 자신의 오른쪽 패딩값
this.getPaddingRight()
// 자신의 아래쪽 패딩값
this.getPaddingBottom()
// 자신의 크기 계산후 나온 넓이
this.getMeasuredWidth()
// 자신의 크기 계산후 나온 높이
this.getMeasuredHeight()
// 자식 뷰의 개수를 반환한다.
getChildCount()
// 자식 뷰의 크기를 반환한다. 이 때 MeasureSpec.AT_MOST 로 거의 호출한다.
child.measure()
// 자식 뷰의 넓이를 가져온다. child.measure() 호출 후 호출한다.
child.getMeasuredWidth()
// 자식 뷰의 길이를 가져온다. child.measure() 호출 후 호출한다.
child.getMeasuredHeight()
// 자식 뷰의 위치를 지정한다.
child.layout()
View Update
invalidate()
View를 다시 그리기 위한 Method. 결과적으로 onDraw()가 호출됨. 뷰가 다시 그려지지만 크기는 변경 안됨.
예 : 사용자 정의보기가 텍스트 또는 배경색 속성이 변경
requestLayout()
View의 크기에 영향이 있다면 reqeustLayout()을 호출해야함. onMeasure()와 onLayout()까지 View에 대해서뿐만 아니라 부모 뷰에 대한 모든 라인까지 트리거함. requestLayout()는 onDraw()는 호출 안함. 따라서 일반적으로 invalidate()와 결합됨
(예 : 맞춤 라벨의 텍스트 속성이 변경된 경우, 라벨의 크기가 바뀌므로 다시 측정하고 다시 그려야함)
invalidate();
requestLayout();
forceLayout()
parent view group에서 requestLayout()을 호출 했을 때, child view들은 다시 그릴 필요가 없음. 그러나 child view가 다시 그려야할 것이 포함되어 있다면, child view에서 forceLayout()을 호출할 수 있음. forceLayout()은 parent view group의 requestLayout()과 함께 발생하는 경우에만 child view에서 동작함. 상위 view tree에서 requestLayout()이 trigger 되지 않으면 view가 forceLayout() 호출해도 효과가 없음.
View가 그려지는 과정
Activity가 Focus를 얻으면 Layout을 그리도록 요청. Android Framework는 그리는 절차를 거치고, 이 때 Activity는 Layout 계층의 root node를 제공해야 함. Layout 계층의 root node로 그리기 시작. Layout tree를 측정하고 그리기를 요청. 차례대로, ViewGroup은 Children View들의 darw() 요청을 담당. View는 자기 자신이 그리는 것을 담당
Parent는 Children보다 먼저 그려지며, Parent -> Children 순으로 tree에 형성됨
Android Framework는 Invaild한 영역에 그릴 수 없음,
View를 Background에서 그릴 수 있음.
invalidate()를 호출하면 View를 강제로 그릴 수 있음.
measure(int widthMeasureSpec, int heightMeasureSpec)
Parent Node에서 Child Node를 경유하여 실행되며, View의 크기를 알아내기 위해 호출된다. View의 실제 크기 측정은 onMeasure(int, int)를 통해 이뤄진다. onMeasure(int, int)는 measure(int, int)의 내부에서 호출한다.
View의 measure() 함수가 반환 할 때, View의 getMeasureWidth()와 getMeasureHeight()값이 설정된다. 만약 Child View 측정값의 합이 너무 크거나 작을 경우 다시 measure() 함수를 호출하여 크기를 재측정한다.
측정 과정에서는 Parent View와 Child View간의 크기정보를 전달하기 위해 2가지의 Class를 사용한다.
ViewGroup.LayoutParam
Parent에게 측정(View의 너비, 높이) 및 위치에 대한 것을 알리는 class측정 기준. 즉, Child View가 Parent View에게 자신이 어떻게 측정되고 위치를 정할지 요청하는데 사용된다. ViewGroup의 sub class인 RelativeLayout 경우 자신만의 ViewGroup.LayoutParams의 sub class는 Child View를 수평적으로 또는 수직적으로 가운데 정렬 할 수 있는 기능이 있다.
- 정확한 숫자 값
- MATCH_PARENT : Padding을 뺀 Parent 크기만큼 커야함
- WRAP_CONTENT : View가 안의 내용을 감쌀 수 있을 만큼 커야함
ViewGruop.MeasureSpec
Parent View가 Child View에게 요구사항을 전달하는데 사용된다.
- UNSPECIFIED : Parent가 Children이 얼마만큼 크기를 원하는지 알 때 사용. Children이 원하는 크기를 가질 수 있음.
- EXACTLY : Parent의 크기를 정확히 부여할 때 사용, Children View는 Parent View 크기 안에 있어야 함.
- AT MOST : Parent가 Children에게 최대 크기를 알려줄 때 사용.
layout(int left, int top, int right, int bottom)
Parent Node에서 Child Node를 경유하여 실행되며, View와 Child View들의 크기와 위치를 할당할 때 사용된다. measure(int, int)에 의해 각 View에 저장된 크기를 사용하여 위치를 지정한다. 내부적으로 onLayout()를 호출하고 onLayout()에서 실제 View 위치를 할당하는 구조로 되어있다.
Reference
ViewGroup과 View의 관계 | 찰스의 안드로이드
쿠…sal: [컴][안드로이드] Android 의 View 에서 onMeasure 와 onLayout 의 의미
신불사 - 신현호라 불리는 사나이 :: [Android] Custom View, Custom Layout ( 커스텀 레이아웃, 커스텀 뷰, 직접 뷰 레이아웃 만들기 )
안드로이드 면접 | 현우의 개발노트
Index
'Programming > Android' 카테고리의 다른 글
[Android] How To APK Install Programmatically (0) | 2018.09.05 |
---|---|
[Android] Input Event (0) | 2018.06.16 |
[Android] Dependency Structure (0) | 2018.04.19 |
[Android] View Collapse & Expand Animation (4) | 2017.11.24 |
[Android] ViewPager & Fragment (2) | 2017.11.06 |