Layout Tricks: Creating Reusable UI Components
 안드로이드 플랫폼은 다양한 종류의 단순한 UI 위젯을 제공하며, 개발자는 이 UI 위젯들을 조합하고 이어 붙여 복잡하고 유용한 UI를 만들수 있다. 그러나 어플리케이션을 개발하다 보면, 작은 UI 블럭이 아닌 그 보다 좀 더 큰  보다 상위의 UI 구성요소가 필요 할 때가 있다. 이러한 요구사항을 효율적으로 수행하기 위해, 개발자는 여러개의 표준 UI 위젯들을 하나의 재사용가능한 구성요소로 결합해 사용할 수 있다.

 예를들어, 개발자는 다음과 같은 상위 UI 블럭들을 만들어 낼 수 있다. - 하나의 프로그레스바와 취소 버튼으로 이루어진 블럭, OK 와 Cancel 두 개의 버튼을 포함한 패널, 아이콘과 제목, 짧은 설명을 곁들인 패널등등. 이러한 상위 UI 요소들을 Custom View 이용해 코드를 작성해 만들 수도 있지만, 보다 손쉽게 오직 XML 만을 이용해서 만들 수도 있다.

 안드로이드 XML 레이아웃 파일 내에서, 각각의 태그는 실재의 클래스 Instance 와 매핑될 수 있지만, (이 때 클래스들은 항상 View 의 서브클래스이다.)  UI 툴킷은  View 클래스 Instance 와 매핑되지 않는 세 가지 특별한 태그를 제공해 준다. 그 태그는 <requestFocus />, <merge /> 그리고 <include /> 이다. 그 중에서 이 글은, 순수하게 XML 으로 구성된 UI 구성요소를 만들기 위해 <include /> 태그를 어떻게 사용하는지 설명하고 있다. <include/> 와 결합해서 사용할 경우 매우 강력한,<merge /> 태그를 사용하는 방법에 관해서는 Merging Layouts 문서를 참고하라.

 <include /> 태그는 이름이 뜻하는 그대로의 역할을 수행한다. - 또다른 XML 레이아웃을 포함할 수 있게 해준다. 이 태그를 사용하는 것은 Home 어플리케이션에서 발췌한 다음의 예제에서 볼 수 있듯이 매우 단순한다. 

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="123"><com.android.launcher.Workspace
   
android:id="@+id/workspace"
   
android:layout_width="fill_parent"
   
android:layout_height="fill_parent"
   
launcher:defaultScreen="1">

 
<include android:id="@+id/cell1" layout="@layout/workspace_screen" />
 
<include android:id="@+id/cell2" layout="@layout/workspace_screen" />
 
<include android:id="@+id/cell3" layout="@layout/workspace_screen" />

</com.android.launcher.Workspace></PRE>

 <include /> 에서 반드시 요구되는 속성 값은 layout  속성 뿐이다. 이 속성은 android namespace prefix 없이 사용되며, 추가하고자 하는 layout 파일을 참고하는데 사용된다. 위 예제에서는 <include/> 태그를 이용하여, 동일한 layout (@layout/workspace_screen)을 세 번 연속하여 포함 시켰다. 이 Tag는 또한 개발자가 속성값을 Override 할 수 있게 해준다. 위 예제에서 처럼 android:id 속성을 이용하여, 레이아웃을 포함하고있는 Root View 의 id 값을 설정 할 수도 있고, 마찬가지로 포함되어진 레이아웃들의 id 값을 설정할 수도 있다. 이와 유사하게, 개발자는 포함시킨 레이아웃의 또 다른 Layout 파라메터들도 (android:layout_ 으로 시작되는 모든 속성 값) Override 할 수 있다. 여기 몇 가지 예가 있다.

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="137"><include android:layout_width="fill_parent" layout="@layout/image_holder" />
<include android:layout_width="256dip" layout="@layout/image_holder" /></PRE>

 이 태그는 개발자가 장치의 설정 변경에 따라(예를 들어 화면 가로 세로...) UI 의 일부분이 변경되도록 UI를 커스터마이즈 하고자 할 때 유용하게 사용된다. 그런 경우, 엑티비티의 메인 레이아웃은 layout/ 디렉토리에 위치하고, layout-land/ 과layout-port/ 폴더에 두 가지 서로 다른 형태의 레이아웃(하지만 이름은 동일한...)을 메인 레이아웃에서 포함하도록 구현 할 수 있다. 이러한 방식은 개발자가 포트레이트 모드와 랜드스케이프 모드 화면을 구성하는데 있어서, 대부분의 UI 요소를 공유해서 사용할 수 있도록 해준다.

Layout Tricks: Creating Efficient Layouts


  안드로이드 UI 툴킷은 몇 가지 Layout 매니저(LinearLayout / RelativeLayout 등의 ViewGroup)를 제공한다. Layout 매니저들은 쉽게 사용할 수 있으며, 많은 경우 기본적인 기능만을 이용해서 원하는 사용자 인터페이스를 구현할 수 있다. 

 하지만 불행하게도, 기본적인 구성요소만을 사용하는 것이 사용자 인터페이스를 작성하는데 가장 효율적인 방법은 아니다. 예를 들어, 개발자가 LinearLayout 을 남용해서 사용할 경우, 전체 UI를 구성하는 View의 계층구조가 복잡해지고, 사용되는 View 의 숫자가 크게 증가될 수 있다. 화면을 구성하는데 사용된 모든 View 와 Layout 매니저들을 처리하는데는 비용이 든다. 특히 LinearLayout 의 weight 파라매터를 사용하는 경우, Layout 화면을 구성하기 위해,  두 번의 Measure Pass 를 거쳐야 하기 때문에 더욱 많은 비용이 소모된다.

 여기 매우 단순하고 일반적인 예가 하나 있다. 아래의 그림과 같이 하나의 리스트 아이템을 표현하기 위한 Layout 을 생각해 보자. 왼쪽에는 아이콘 이미지가 있고, 상단에는 타이틀 텍스트가 그 아래는 부가적인 설명이 포함된 Layout 이다. 


 HierarchyViewer 로 캡처한 Layout 와이어 프레임을 살펴보면, 하나의 ImageView 와 두 개의 TextView 가 어떻게 구성되어 있는지 보다 명확하게 확인 할 수 있다. 


 LinearLayout 을 이용하 이러한 화면을 구성하는 것은 어렵지 않다. 전체 Layout 은 Horizontal LinearLayout (orientation 속성값이 Horizontal ) 이며, 하나의 ImageView 와 Vertical LinearLayout 을 포함하고 있다. 또한 Vertical LinearLayout 은 두 개의 TextView 를 포함한다. 소스 코드는 다음과 같다.

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="227"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:layout_width="fill_parent"
   
android:layout_height="?android:attr/listPreferredItemHeight"
   
   
android:padding="6dip">
   
   
<ImageView
       
android:id="@+id/icon"
       
       
android:layout_width="wrap_content"
       
android:layout_height="fill_parent"
       
android:layout_marginRight="6dip"
       
       
android:src="@drawable/icon" />

   
<LinearLayout
       
android:orientation="vertical"
   
       
android:layout_width="0dip"
       
android:layout_weight="1"
       
android:layout_height="fill_parent">

       
<TextView
           
android:layout_width="fill_parent"
           
android:layout_height="0dip"
           
android:layout_weight="1"
                   
           
android:gravity="center_vertical"
           
android:text="My Application" />
           
       
<TextView  
           
android:layout_width="fill_parent"
           
android:layout_height="0dip"
           
android:layout_weight="1"
           
           
android:singleLine="true"
           
android:ellipsize="marquee"
           
android:text="Simple application" />
           
   
</LinearLayout>

</LinearLayout></PRE>

 이 Layout 은 정상적으로 작동하지만, 낭비 요소를 담고 있다. 개발자는 하나의 RelativeLayout 이용해서 동일한 형태의 UI를 구성할 수도 있다. 그럼으로 화면을 구성하는데 사용되는 View 의 수를 하나 줄일 수 있고, 추가 적으로 View 의 계층 구조도 좀 더 단순화 될 수 있다. 또한 RelativeLayout 을 이용해서 구현하는 것은 어렵지도 않다.

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="231"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:layout_width="fill_parent"
   
android:layout_height="?android:attr/listPreferredItemHeight"
   
   
android:padding="6dip">
   
   
<ImageView
       
android:id="@+id/icon"
       
       
android:layout_width="wrap_content"
       
android:layout_height="fill_parent"
       
       
android:layout_alignParentTop="true"
       
android:layout_alignParentBottom="true"
       
android:layout_marginRight="6dip"
       
       
android:src="@drawable/icon" />

   
<TextView  
       
android:id="@+id/secondLine"

       
android:layout_width="fill_parent"
       
android:layout_height="26dip"
       
       
android:layout_toRightOf="@id/icon"
       
android:layout_alignParentBottom="true"
       
android:layout_alignParentRight="true"
       
       
android:singleLine="true"
       
android:ellipsize="marquee"
       
android:text="Simple application" />

   
<TextView
       
android:layout_width="fill_parent"
       
android:layout_height="wrap_content"
       
       
android:layout_toRightOf="@id/icon"
       
android:layout_alignParentRight="true"
       
android:layout_alignParentTop="true"
       
android:layout_above="@id/secondLine"
       
android:layout_alignWithParentIfMissing="true"
               
       
android:gravity="center_vertical"
       
android:text="My Application" />

</RelativeLayout></PRE>

  새로운 구현 방식은 이전에 LinearLayout을 이용한 구현한 것과 거의 동일하게 작동하지만, 한가지 주의해야할 점이 있다. 위의 Layout 중에, 만일 특정 리스트 아이템에 대해서, 부가적인 설명을 나타내는 TextView 가 사용될 필요가 없는 경우 이전 LinearLayout 을 이용해서 구성한 경우, 해당 TextView 의 Visibility 속성을 Gone 으로 설정하면 손쉽게 부가 설명 항목을 제외 시킬 수 있다. 하지만 그러한 방식은, RelativeLayout 을 사용하는 경우 정상적으로 작동하지 않을 수도 있다.

 RelativeLayout 에서 View 들은 특정 부모 View (RelativeLayout 자체나 아니면 해당 Layout이 참조하고 있는 View) 를 기반으로 정렬되어 진다. 예를 들어, 위 예제의 경우, 부가적인 설명을 표시하는 TextView 는 RelativeLayout 의 바닦면에 정렬되며, 타이틀을 표시하는 TextView 는 그 View 위에 위치하도록 선언되어 있다. 이 때, 부가적인 설명을 표시하는 TextView 가 Gone 이 될 경우, RelativeLayout 은 타이틀을 표시하는 View 를 어디에 위치시켜야 할지 알 수 없게 된다. 이러한 문제를 해결하기 위해, 개발자는 layout_alignWithParentIfMissing 파라매터를 사용할 수 있다. 

 이 Boolean 인자는 만일 특정한 View 가 기준으로 삼아야되는 부모 View 가 없을 경우, 해당 View 를 포함하고 있는 RelativeLayout 자체를 기준으로 삼으라고 알려준다.  예제의 경우, aliginWithParentIfMissing 를 설정함으로서, 만일 타이틀을 표시하는 TextView 가 기준으로 삼고 있는 TextView 가 없을 경우 (Gone 에 의해...), 해당 View 대신 RelativeLayout 을 기준으로 삼게 되고, 그 결과 TextView 는 아래와 같이 RelativeLayout 의 바닦면 바로 위에 위치하게 된다.

 이로서 RelativeLayout 을 이용해 구성한 두 번째 UI도 완벽하게 작동한다. 무엇보다도, 계층구조는 더 단순해 졌고, LinearLayout 의 weight 파라메터를 사용하지 않았기 때문에 더 효율적이다. 두 가지 구현 방식의 차이점은 HierarchyViewer 를 이용해 살펴 보면 확실하게 들어난다.

 다시한번 말하지만, 이러한 차이는 ListView 를 구성하는 개발 아이템 아이템 마다, 동일한 형식의 Layout 을 사용하게 되면 훨씬 더 중요해 질 수 있다.  개발자들이 이번 글에서 예로든 간단한 예제를 통해, 여러가지 레이아웃에 대해 잘 아는 것이, UI 를 최적화하는데 최선의 길임을 알 수 있기를 희망한다.

Layout Tricks: Using ViewStubs

 안드로이드에서 Activity 간에 UI 구성 요소를 공유하고 재사용하는 것은 매우 쉽다. <include/> 태그에게 감사하자. 때로는 복잡한 UI를 만들어내는 것이 너무 쉬운 나머지, 매우 드물게 사용되는 View 들도 전부 포함하는,  굉장히 많은 View 로 구성된 UI를 만들어 낼 수도 있다. 이런 경우를 대비해, 매우 고맙게도, 안드로이드에서는 ViewStub 위젯을 제공해 준다. ViewStub 을 사용하면,  개발자는 <include/>  태그가 제공하는 이점을 취하면서도, 잘 사용되지 않는 View 는 생성하지 않고 Layout 을 구성할 수 있다.

 ViewStub 은 매우 가벼운 더미 View 이다. 이 View 는 어떤 영역을 차지하지도 않으며, 무엇을 그리지도 않고, UI 를 구성하는 Layout 에 참여하지도 않는다.  즉, ViewStub 을 생성하고, View 계층구조내에서 유지하는데 매우 적은 비용만이 필요하다.  ViewStub 은 Lazy Include 하다고 표현할 수 있는데,  ViewStub 에 의해 참조되고 있는 View 는 오직 개발자가 명시적으로 지시하는 경우에 한해서, 생성(inflate)되고 View 계층 구조에 추가되기 때문에 그렇다. (즉, Layout Inflate 시점에 바로 생성되지 않고 그 이 후에 생성된다...)

 다음 그림은 Shelves 어플리케이션의 스크린 샷이다. 이 Activity 는 책 선 반위에 놓인, 사용자들이 열람가능한 책 목록을 보여준다. 


 동일한 Activtriy 가 사용자가 새로운 책을 추가할 때도 사용된다. 그런데, 책을 추가하는 작업이 이루어지는 동안에, Shelves 는 추가적인 사용자 인터페이스를 보여준다. 아래의 스크린샷에서 볼 수 있듯이, 책을 추가하는 동안 화면 하단에,  프로그래스바와 취소 버튼이 나타난다. 


 책을 추가하는 작업은, 적어도 책 목록을 살펴보는 작업에 비해 일반적인 작업이 아니기 때문에,  해당 정보를 표시하는 패널은 ViewStub 을 이용하여 구현되어 있다.


 사용자가 책을 추가하는 작업을 시작하는 시점에 ViewStub 이 실재로 형상화 되며, 해당 ViewStub 이 참조하고 있는 Layout 파일이 ViewStub 을 대체하게된다.


  ViewStub 을 사용하기 위해서, 개발자는 ViewStub 을 실재로 형상화 하기 위해 필요한 android:id 속성 값과 해당 ViewStub 이 어떤 Layout 파일에 정의된 View 와 교체될지를 나타내는 android:layout 속성 값을 지정해 주면된다. 추가로, ViewStub 에서는 또 한 가지 속성값이 사용된다. android:inflatedId 는 참조되는 Layout 파일의 루트 View 의 android:id 속성 값을 Override 할 수 있게 해 준다. 또한, ViewStub 에 정의한 Layout 파래매터들은, ViewStub 이 참조한 Layout 의 루트 View 에 적용된다. 실재 사용예는 아래와 같다.

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="208"><ViewStub
 
android:id="@+id/stub_import"
 
android:inflatedId="@+id/panel_import"

 
android:layout="@layout/progress_overlay"

 
android:layout_width="fill_parent"
 
android:layout_height="wrap_content"
 
android:layout_gravity="bottom" /></PRE>

  개발자가 ViewStub 을 형상화 하고자 할 때면, 단순히 해당 ViewStub 의 inflate()메서드를 호출 하거나, Visibility 속성을 VISIBLE 이나 INVISIBILE 로 변경하면 된다. 단, inflate() 메서드를 를 호출할 경우, 형상화된 Layout 파일의 루트 View 를 반환값으로 전달 받을 수 있는 장점이 있다. 

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="212">((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
// or
View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();</PRE>

  ViewStub 이 형상화된 이 후에는, View 계층 구조에서 완전히 제거된다는 것을 기억하는 것은 매우 중요하다. 즉, ViewStub 에 대한 참조를 Class 멤버 변수로 두는 식으로 오래 동안 유지할 필요가 없다. 

  ViewStub 은 쉬운 프로그래밍과 효율적인 프로그래밍 사이에서 환상적인 조화를 이루고 있다. 어플리케이션 실행 중에, 특정한 View 를 코드상에서 생성한 후, View 계층 구조에 추가하도록 구현하기 보다 ViewStub 을 사용하는 것이 훨씬 쉽고 효율적일 수 있다. 현재 ViewStub 이 갖고 있는, 딱 한가지 단점은 <merge/> 태그를 지원하지 않는다는 것이다. 

 

Layout Tricks: Merging Layouts

  한 번 작성한 Layout 코드를 공유하고 재사용하기 위해 <include/> 태그를 사용하는 방법에 대하여 이야기 했었다. 이번 글에서는 <include/> 태그를 사용할 때 생기는 문제점을 보완해 줄 수 있는 <merge/> 태그에 관해 이야기해 본다.

 안드로이드에서 UI Layout 을 구성 할 때, View 계층 구조의 단계를 줄여 최적화 하기 위해 <merge /> 태그가 만들어졌다.  예제를 통해 살펴보면, 이 태그를 사용하는 목적에 대해 쉽게 이해할 수 있다. 다음의 예는, 어떤 이미지를 표시하고, 그 이미지 위해 해당 이미지의 제목을 표시해 주는 XML Layout 이다. 구조는 매우 단순하다.  FrameLayout 을 이용해서, ImageView 위에 TextView 를 표시하였다. 

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="156"><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:layout_width="fill_parent"
   
android:layout_height="fill_parent">

   
<ImageView  
       
android:layout_width="fill_parent"
       
android:layout_height="fill_parent"
   
       
android:scaleType="center"
       
android:src="@drawable/golden_gate" />
   
   
<TextView
       
android:layout_width="wrap_content"
       
android:layout_height="wrap_content"
       
android:layout_marginBottom="20dip"
       
android:layout_gravity="center_horizontal|bottom"

       
android:padding="12dip"
       
       
android:background="#AA000000"
       
android:textColor="#ffffffff"
       
       
android:text="Golden Gate" />

</FrameLayout></PRE>

 이 Layout 은 원하는 대로 잘 작동하며, 특별히 잘못된 점은 찾을 수 없다.

 하지만 만일 이 레이아웃을 HierarchyViewer 를 통해 살펴보면 흥미로운 점이 들어난다. 개발자가 View 계층 구조를 잘 살펴보면, 우리가 XML 파일에 정의한  FrameLayout 이 딱 하나의  FrameLayout 만을 자식 View 로 가지고 있는 것을 확인 할 수 있다.  (파란색으로 강조되어 있다.)

 이 때 FrameLayout 은 fill_parent 속성 값을 사용하고 있기 때문에, 그 부모와 동일한 영역을 차지한다. 또한, 특별한 배경을 지정하지도 않았으며, 추가적인 Padding 속성이나 Gravity 속성을 지정하지 않았기 때문에, 사실 화면을 구성 하는데 역할을 수행하지 않는다. 즉, 이 경우 추가적으로 사용된 FrameLayout 은 어떠한 이유도 없이 그저 UI 를 보다 복잡하게 만들 뿐이다. 하지만  우리가 어떻게 이 FrameLayout 을 제거할 수 있을까?  어찌되었든, Layout 을 지정하는 XML 다큐먼트는 Root 태그를 가져야 하면,  XML 상에 정의된 태그 는 실재 View 로 구현된다.

 바로 이 경우에 <merge/> 태그가 쓸모있다.  LayoutInflater 가 View 를 형상화 하는 과정 중에, <merge/> 태그를 만나게 되면, 해당 <merge/> 태그는 건너 뛰고,  그 자식 View 들을 <merge/> 태그의 부모 View 에 추가한다. 설명이 조금 헷갈릴 수도 있겠다. 이해를 돕기 위해, 이전 예제에서 사용된 FrameLayout  대신 <merge/> 를 사용한 후 살펴 보자. 

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="165"><merge xmlns:android="http://schemas.android.com/apk/res/android">

   
<ImageView  
       
android:layout_width="fill_parent"
       
android:layout_height="fill_parent"
   
       
android:scaleType="center"
       
android:src="@drawable/golden_gate" />
   
   
<TextView
       
android:layout_width="wrap_content"
       
android:layout_height="wrap_content"
       
android:layout_marginBottom="20dip"
       
android:layout_gravity="center_horizontal|bottom"

       
android:padding="12dip"
       
       
android:background="#AA000000"
       
android:textColor="#ffffffff"
       
       
android:text="Golden Gate" />

</merge></PRE>

  이 새로운 버전의 Layout 에서 TextView 와 ImageView 는  최상위 FrameLayout 에 바로 추가된다. 그 결과, 화면상으로 동일하게 보이지만, 실재 View 계층구조는 좀 더 단순해 진다.

  명백하게도  위의 예제에서 <merge /> 태그를 사용 할 수 있는 것은, Activity 에 사용된  Content View 가 FrameLayout  이기 때문이다. (FrameLayout 이 두 번 반복되기 때문에 하나를 줄일 수 있음)  만일 FrameLayout 대신 LinearLayout 이 Root  태그로 사용되었다면, <merge/> 태그를 사용할 수 없다. 

  하지만 <merge /> 태그는 다른 경우에도 유용하게 사용될 수 있다. 예를 들어, <merge /> 태그는 <include /> 를 통해 View 를 추가하고자 할 때, 완벽하게 작동한다. 또한, XML 상에서 몇개의 View 를 조합하여, 커스텀한 View 를 구성하고자 하는 경우에도 <merge />  태그는 유용하게 사용된다.  Button 내부의 내용을 원하는 대로 수정할 수 있는, 두 개의 Button 을 보여주는 OKCancelBar 라는 CustomView 를 만들 때,  <merge /> 를 어떻게 사용할 수 있는지 한 번 살펴보자. (원한 다면, 이 예제의 완벽한 소스를 다운로드 받을 수도 있다.)  아래의 예제는,  어떤 이미지 위에 우리가 새롭게 정의한 CustomView 를 표시한다. 

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="172"><merge
   
xmlns:android="http://schemas.android.com/apk/res/android"
   
xmlns:okCancelBar="http://schemas.android.com/apk/res/com.example.android.merge">

   
<ImageView  
       
android:layout_width="fill_parent"
       
android:layout_height="fill_parent"
   
       
android:scaleType="center"
       
android:src="@drawable/golden_gate" />
   
   
<com.example.android.merge.OkCancelBar
       
android:layout_width="fill_parent"
       
android:layout_height="wrap_content"
       
android:layout_gravity="bottom"

       
android:paddingTop="8dip"
       
android:gravity="center_horizontal"
       
       
android:background="#AA000000"
       
       
okCancelBar:okLabel="Save"
       
okCancelBar:cancelLabel="Don't save" />

</merge></PRE>

새로운 Layout 은 다음과 같은 결과를 만들어 낸다.

 새로운 CustomView  인 OKCancelBar 의 소스 코드는 매우 단순하다. 왜냐하면 두 개의 버튼을 생성하기 위한 코드는 외부 XML 파일에 별도로 지정되어 있기 때문이다. 다음 코드에서 확인 할 수 있듯이, R.layout.okcancelbar 에 지정된 Layout 이 LayoutInflate 를 통해 형상화 된 후, OKCancelBar 의 자식 View로 추가된다. 

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="178">public class OkCancelBar extends LinearLayout {
   
public OkCancelBar(Context context, AttributeSet attrs) {
       
super(context, attrs);
        setOrientation
(HORIZONTAL);
        setGravity
(Gravity.CENTER);
        setWeightSum
(1.0f);
       
       
LayoutInflater.from(context).inflate(R.layout.okcancelbar, this, true);
       
       
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OkCancelBar, 0, 0);
       
       
String text = array.getString(R.styleable.OkCancelBar_okLabel);
       
if (text == null) text = "Ok";
       
((Button) findViewById(R.id.okcancelbar_ok)).setText(text);
       
        text
= array.getString(R.styleable.OkCancelBar_cancelLabel);
       
if (text == null) text = "Cancel";
       
((Button) findViewById(R.id.okcancelbar_cancel)).setText(text);
       
        array
.recycle();
   
}
}</PRE>

 두 개의 버튼은 다음의 XML Layout에 정의되어 있다. 추가적인 Layout 없이, 부모 View 인 OKCancelBar 에 두 개의 버튼을 직접 추가하기 위해서 <merge /> 태그를 사용했다. 또한, 개별 버튼은 유지하고 관리하기 쉽게 하기 위해,  외부 XML 파일에 별도로 구현된 후, <include/> 태그를 이용해 두 번 포함되었고, 단순히 id 값만을 Override 하였다. 

<PRE style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 10px; OVERFLOW-X: auto; OVERFLOW-Y: auto; BACKGROUND-COLOR: rgb(250,250,250); MARGIN: 0px 0px 1em 1em; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; FONT-FAMILY: monospace; COLOR: rgb(0,112,0); BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 10px" class=prettyprint jQuery1271053429359="182"><merge xmlns:android="http://schemas.android.com/apk/res/android">
   
<include
       
layout="@layout/okcancelbar_button"
       
android:id="@+id/okcancelbar_ok" />
       
   
<include
       
layout="@layout/okcancelbar_button"
       
android:id="@+id/okcancelbar_cancel" />
</merge></PRE>

  결과적으로 우리는, 효율적인 View 계층 구조를 갖으면서도, 유연하고 유지보수 하기 쉬운 Custom View 를 작성 하였다. 

 살펴본 봐와 같이, <merge/> 태그는 코드를 작성할 때, 굉장히 유용하며 깜짝 놀랄만한 일들을 해 준다. 하지만, 몇 가지 한계점도 갖고 있다. 

  • <merge/> 는 Root 태그로만 사용될 수 있다.
  • <merge/> 로 시작되는 Layout 을 형상화 할 때, 개발자는 반드시, ViewGroup 을 지정해 주어야 하고, attachToRoot 값을 true 로 설정 해 주어야 한다. (보다 자세한 설명은 inflate() 메서드에 관한 다음 내용을 살펴 보라.)
신고

'Android > 번역' 카테고리의 다른 글

Layout Tricks 번역 [펌글]  (0) 2010.04.14
Data Storage, Database 번역 [펌글]  (0) 2010.04.14
Content Providers 번역 [펌글]  (0) 2010.04.14
Using the Contacts API  (0) 2010.03.17
Posted by 까칠코더.


티스토리 툴바