[번역] 안드로이드 Layout Tricks: Merging Layouts 안드로이드
2010/04/01 23:54 |
Layout Tricks: Merging Layouts
LayoutTricks 의 마지막은 Merge 태그에 관한 이야기입니다. 좀 생소하긴 해도, CustomView 를 생성하고자 할 때, 굉장히 효과적으로 View 계층 구조를 줄여나갈 수 있어서, 알아두면 쏠쏠하게 효과를 볼 수 있을 듯 하네요.
한 번 작성한 Layout 코드를 공유하고 재사용하기 위해 <include/> 태그를 사용하는 방법에 대하여 이야기 했었다. 이번 글에서는 <include/> 태그를 사용할 때 생기는 문제점을 보완해 줄 수 있는 <merge/> 태그에 관해 이야기해 본다.
안드로이드에서 UI Layout 을 구성 할 때, View 계층 구조의 단계를 줄여 최적화 하기 위해 <merge /> 태그가 만들어졌다. 예제를 통해 살펴보면, 이 태그를 사용하는 목적에 대해 쉽게 이해할 수 있다. 다음의 예는, 어떤 이미지를 표시하고, 그 이미지 위해 해당 이미지의 제목을 표시해 주는 XML Layout 이다. 구조는 매우 단순하다. FrameLayout 을 이용해서, ImageView 위에 TextView 를 표시하였다.
<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>
이 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/> 를 사용한 후 살펴 보자.
<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>
이 새로운 버전의 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 를 표시한다.
<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>
새로운 Layout 은 다음과 같은 결과를 만들어 낸다.
새로운 CustomView 인 OKCancelBar 의 소스 코드는 매우 단순하다. 왜냐하면 두 개의 버튼을 생성하기 위한 코드는 외부 XML 파일에 별도로 지정되어 있기 때문이다. 다음 코드에서 확인 할 수 있듯이, R.layout.okcancelbar 에 지정된 Layout 이 LayoutInflate 를 통해 형상화 된 후, OKCancelBar 의 자식 View로 추가된다.
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();
}
}
두 개의 버튼은 다음의 XML Layout에 정의되어 있다. 추가적인 Layout 없이, 부모 View 인 OKCancelBar 에 두 개의 버튼을 직접 추가하기 위해서 <merge /> 태그를 사용했다. 또한, 개별 버튼은 유지하고 관리하기 쉽게 하기 위해, 외부 XML 파일에 별도로 구현된 후, <include/> 태그를 이용해 두 번 포함되었고, 단순히 id 값만을 Override 하였다.
<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>
결과적으로 우리는, 효율적인 View 계층 구조를 갖으면서도, 유연하고 유지보수 하기 쉬운 Custom View 를 작성 하였다.
살펴본 봐와 같이, <merge/> 태그는 코드를 작성할 때, 굉장히 유용하며 깜짝 놀랄만한 일들을 해 준다. 하지만, 몇 가지 한계점도 갖고 있다.
- <merge/> 는 Root 태그로만 사용될 수 있다.
- <merge/> 로 시작되는 Layout 을 형상화 할 때, 개발자는 반드시, ViewGroup 을 지정해 주어야 하고, attachToRoot 값을 true 로 설정 해 주어야 한다. (보다 자세한 설명은 inflate() 메서드에 관한 다음 내용을 살펴 보라.)