본문 바로가기

프로그래밍/안드로이드

[안드로이드] 레이아웃 전개(Layout Inflation)

※ 저는 안드로이드 프로그래밍 정복(김상형 著, 한빛미디어) 책을 이용해 공부하고 있으며

예제와 코드는 이 책을 통해 공부중임을 밝힙니다.

개인적인 공부를 하면서 정리한 형식이기 때문에 심각한 오류가 있을 수 있습니다. 피드백 주시면 정말 감사하겠습니다.


※ 안드로이드 프로그래밍 정복(196p~207p) 참조

지금 공부한지 약 5일정도 되가는데 자바를 하다가 너무 재미없어서 "안드로이드를 통해서 만들어보면서 하자!"라는 생각에 뛰어들었다.  자바 진도는 객체지향 하다가 때려쳤는데 여기까지 배운 내용만으로도 진도 나가는데 아직까지 큰 무리는 없다. 오히려 재밌다.



<들어가기에 앞서>


1. 전개(Inflation) : XML 문서에 정의된 레이아웃과 차일드 뷰의 속성을 읽어 실제 뷰 객체를 생성하는 것.

2. 전개 메서드 : setContentView ( XML 문서의 리소스 ID를 전달받아 레이아웃을 전개해 액티비티를 채운다 )

3. 리소스 ID : R.java에 정의되어 있는 "상수" 값을 의미

이런 식으로 "상수"로 정의되어 있다.

4. 리소스를 전개하는 부분은 우리가 직접 수행할 수 있는 일련의 방법들이 있다.



<기존의 방식 : XML파일을 만들어 레이아웃을 전개해보자!>


 <이렇게 XML 문서를 작성해 레이아웃을 꾸몄다.>

이후 java 코드에서 setContentView (R.layout.문서이름) 을 통해서 액티비티를 내용물로 채웠다. 즉, 리소스에서 R.layout.activity_exercise를 읽어 액티비리의 내용물로 채운다.


전개(Inflation) : XML 문서에 정의된 레이아웃과 차일드 뷰의 속성을 읽어 실제 뷰 객체를 생성하는 동작을 말한다. 레이아웃의 정보대로 객체를 생성하고, 속성 변경 메서드를 순서대로 호출한다.


여기서 setContentView 메서드 "전개"를 하는 핵심 메서드인데, XML 문서의 리소스 ID를 전달받아 이 레이아웃을 전개하여 액티비티를 채운다. 즉, 레이아웃의 정보대로 객체를 생성하고 속성 변경 메서드를 순서대로 호출한다.


순서를 따라가보면 컴파일된 XML 문서의 정보를 해석하여 수직(vertical) 리니어 레이아웃을 생성하고 배경을 회색(#cccccc)으로 설정한다. 그리고 크기 20sp의 빨간색(#ff0000) 텍스트뷰를 생성하고 그 안에 들어가는 내용물을 채운다.


그러면 레이아웃 XML파일을 만드는 것 없이

"코드"로 레이아웃을 작성할 수 있을까?

정답은 YES이다.


<두번째 방식 : 코드로 레이아웃을 만들어 전개해보자!> 

 .java에서 코드만으로 레이아웃을 만들어보자.

먼저 작성했던 XML 코드를 최소한의 설정만 남겨두고 나머지는 모두 지웠다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"
android:layout_height="match_parent">

</LinearLayout>
그리고 아래와 같이 .java에서 코드를 작성해보았다.

이 예제에서는 new 연산자를 통해 LinearLayour 객체와 TextView 객체를 생성한 뒤 속성을 지정했다.

하지만 이 둘은 아직 부모 자식 관계를 형성하지는 못했다. 

따라서 자식 관계를 형성시켜주기 위해 "addView"를 호출한다.


ViewGroup의 addView메서드는 인수만 달리해서 여러가지 버전으로 오버로딩되어 있다.


void addView(View child)  // 뷰를 차일드 목록의 제일 마지막에 추가하고, 디폴트 파라미터를 사용

void addView(View child, int index) // 뷸를 형제 차일드 중간에 삽입

void addView(VIew child, VIewGroup.LaoyutParams params) //  레이아웃 관련 파라미터를 인수로 전달



내가 아직 오버로딩에 대한 개념이 미숙해서, 오버로딩에 대한 개념을 알아보고 가려한다.
단순하게 말하면 오버로딩은 비슷한 기능을 여러 개 탑재하는 것이다.

오버로딩(over-ㅇloading) : 기존에 없는 새로운 메서드를 정의하는 것(new)
오버라이딩(over-riding) : 상속받은 메서드의 내용을 벽녕하는 것(change, modify)



linear.addView(text)를 통해 "리니어 안에 텍스트뷰가 배치"된다.
그리고 이 뷰그룹(ViewGroup)을 액티비티에 "등록"해야 하는데, setContentView가 이 역할을 수행한다.

[ setContentiView의 오버로딩 ]

 1) XML레이아웃을 메모리상에 객체화(인플레이션)

void setContentView(int layoutResID) 

  2) 화면에 나타나는 뷰를 지정

void setContentView ( View view, [ViewGroup.LayoutParams params] )


<기존의 방식>에서 사용한 방법은 리소스 ID를 전달하여 "내부"에서 전개하는 것이고 
<두번째 방식>에서 사용한 방법은 코드에서 생성한 "뷰 객체"를 전개하는 것이다.

즉, <기존의 방식>에서 뷰를 생성하는 주체는 "XML"
     <두번째 방식>에서 뷰를 생성하는 주체는 "코드"


XML로 전개하는 법도 배웠고, 코드로 전개하는 법도 배웠다.
여기서, XML로 전개하는 부분을 우리가 직접 수행할 수도 있다
LayoutInflater 클래스가 바로 XML리소스를 전개해 "뷰 객체"를 만드는 역할을 한다.


<세번째 방식 : 레이아웃 Inflator 클래스 만들기> 


다시 .xml 레이아웃을 채워보자

View inflate (int resource , ViewGroup root)

반환값 : 루트 뷰 ( 레이아웃의 모든 차일드를 포함하며 XML 문서의 내용대로 속성 설정까지 왼료됨)
첫번째 인수 (int resource) : XML 리소스 ( 이름에 맞게 적절하게 수정 )
두번째 인수 (ViewGroup root) : 생성된 뷰의 루트로 사용할 뷰 (리소스 내에 루트가 있다면 null)

"inflate 메서드"는 전달된 resource의 정보를 해석하고 뷰를 생성하여 속성 지정 후 루트 뷰를 리턴한다. "리턴값으로 나오는 뷰"는 레이아웃의 모든 차일드를 포함하여 XML 문서의 내용대로 속성 설정까지 완료한 상태이다. 이 리턴된 뷰를 setContentView로 전달하는 것이다.

첫번째 인수 XML 리소스는 XML 문서가 아니라, aapt 툴에 의해 컴파일된 이진법으로 이루어진 정보이다.

두번쨰 인수 root는 생성된 뷰의 루트로 사용할 뷰이되, 리소스 내에 루트가 따로 있다면 null로 사용한다.


getSystemService 메서드는 전개자를 구하는 메서드이다.

다음과 같이 할 수도 있다.


① static LayoutInflater LayoutInflater.from ( Context context )

LayoutInflater inflater = LayoutInflater.from(this);

LinearLayout linear = (LinearLayout)inflater.inflate(R.layout.XML리소스, null);

setContentView(linear);


② static View inflate ( Context, int resource, ViewGroup root )

  LinearLayout linear = (LinearLayout)View.inflate(this, R.layout.XML리소스, null);

  setContentView(linear);


setContentView ( View.inflate ( this, R.layout.XML리소스, null ))



< 정 리 > 


위 세가지 방식을 정리 요약하면 다음과 같다.


1. XML 기반 : XML UI 선언 (XML 레이아웃을 직접 만들거나 )

2. 자바 기반 : 코드로 UI 생성 (코드에서 레이아웃을 만들거나 )

3. 하이브리드 : XML로 사용자 인터페이스 선언, 코드로 UI 속성 수정 ( XML 레이아웃을 전개 )



특히, 3번의 경우 여러 가지의 전개자를 만들어주는 방법이 있다고 했다.
그렇다면 안드로이드가 이렇게 전개자를 별도로 지원하는 이유는 무엇일까?


1. 안드로이드가 전개자를 별도로 제공하는 이유는 특정 뷰 하나 또는 특정 뷰 그룹 하나만 따로 전개하는 것을 지원하기 위해서다.

2. 차일드 뷰를 직접 생성하는 것보다 XML파일로 잘 정의해두고 필요할 때마다 끌어쓰면 훨씬 효율적이다. (코드 중복화)

예를 들어 사용자의 입/출력에 따라 메시지 개수가 가변적이라면 디자인 타임에서 미리 레이아웃을 결정할 수 없다.


설명이 다소 이해하기 어려울 수 있다. 내가 이해한 방식으로는 이렇다.

XML 문서 내에 15개의 버튼을 만들었는 데,  코드를 작성하다 보면 단 한 개의 버튼만 불러올 필요가 있는 경우에, 전개자를 이용해서 버튼 하나를 호출하는 것이다. 전개를 통해서 이 과정을 수행하며 좀 더 유연한 코드 작성이 가능할 것이다.



기존코드를 통해 알아보도록 하자. 여기서는 수직리니어 레이아웃 1개, 텍스트뷰 1개 객체를 생성했다.

텍스트뷰에 설정된 속성 4가지 ( setText, setGravity, setTextColor, setTextSize) 가 있다.
이를 하나의 코드로 압축해보자.

TextVeiw text = (TextView)View.inflate(this, R.layout.mytext, null):


텍스트뷰를 new 연산자로 생성하지 않고 전개자로 리소스ID를 전달하여 XML 문서의 정보대로 생성한다.
규모가 큰 프로젝트가 있다고 하면, 텍스트 속성에 맞도록  XML 리소스를 잘 정의해놓고, 만들어 놓는 리소스를 입맛에 따라 골라 쓰는 것이다.