«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

올해는 머신러닝이다.

[팁]android AbsoluteLayout 대체할수 있는 방법 본문

Android/Tip&Tech

[팁]android AbsoluteLayout 대체할수 있는 방법

행복한 수지아빠 2011. 6. 1. 17:12

Android... 멈추지 않는 변화

 

 

[Intro]

SDK 1.5 R1 버전이 발표된지 꽤 오래전 일입니다만,

무슨 바람이 들었는지 몰라도 재빨리 바뀐 부분을 캐치했어야 하는데 그러지 못했습니다.

이제서야 부랴부랴 Android Development Tool도 뭔가 바뀌어 버린것을 알았고,

SDK들도 뭔가 바뀌어 버린 것을 알았습니다.

 

이전에 만들어 두었던 프로젝트들도 1.5버전에 맞추어서 실행 하려던 찰나...

아차! 이게 뭡니까...

 

AbsoluteLayout is deprecated

 

아... 이렇게 허망하게 없애 버리다니...

없애는게 이해가 안가는것은 아닙니다.

디바이스마다 화면이 다르니 절대 좌표로 박으면 호환성 유지가 안되는건...

뭐 충분히 생각 할 수 있는 부분입니다. 그냥 이번에 1.5버전에선 과감하게 빼버린 것이지요

 

 

[Why?]

일단 AbsoluteLayout은 안좋은 것이니까 왠만하면 쓰지맙시다!

라고 구글에서 강력하게 걸고 넘어지는 것인데...

그렇다면 정말 절대 좌표를 사용해야 하는 경우에는 어떻게 해야 하는 것인가요...

일단 열심히 찾아 봤습니다.

 

맨 먼저 Reference를 뒤져봤습니다.

This class is deprecated. Use FrameLayout, RelativeLayout or a custom layout instead.

라는군요...

FrameLayoutRelativeLayoutCustomLayout을 사용 하라는 말 밖에 없네요.

(CustomLayout이라는 Class가 있는게 아닙니다.)

뭐 살짝 무책임 한게 없지 않아 있습니다. 데체 왜 없애 버렸는지,

그리고 어떻게 대체 하라는지 급 궁금해져서 또 다시 뒤져봤습니다.

 

검색 키워드를 잘 선택 했는지 어렵지 않게 아래 글을 발견 할 수 있었습니다.

Everything you can do with AbsoluteLayout can be done with FrameLayout 
and RelativeLayout. AbsoluteLayout was deprecated because its use 
encourage applications that will NOT work with devices with different 
screen dimensions/resolutions. As such we decided to deprecate 
AbsoluteLayout to encourage developers to do the right thing and use 
layouts that will work much better with other screen 
dimensions/resolutions.

 

-- 
Romain Guy 
Android framework engineer 
romain...@android.com

각 디바이스들은 각각 다른 크기와 해상도를 가지기 때문에 없애버렸습니다만,

FrameLayout과 RelativeLayout으로 모두 표현 가능 하다고

Romain Guy님께서 설명해 주신 글을 발견 했습니다. (원문보기)

 

누군지는 몰라도 Android framework engineer의 말이니 믿어야죠.

믿을 수 밖에 없습니다. 그럼 어떻게 해야 하는 것일까요?

 

 

[How?]

정렬이 잘 되어 있고 깔끔하게 보이는 왠만한 레이아웃은 AbsoluteLayout을 사용할 필요가 없습니다.

하지만 정말로 원하는 위치에 View들을 배치하고 싶을때는 어떻게 해야 좋을까요?

 

 

위와 같이 주어진 공간 안에서 View들을

원하는 임의의 위치에 표시 하고 싶을때는 어떻게 하면 좋을지 생각해 봅시다.

일단 FrameLayout은 모든 Child들을 좌측 상단(Left-Top)에 붙여버리는 Layout입니다.

Child가 여러개 붙어 있다면 Child View들은 좌측 상단에 붙어 차곡차곡 쌓이게 되는 것이죠.

FrameLayout안에 있는 View들은 margin같은 속성이 전혀 먹혀 들어가지 않습니다.

 

결국 FrameLayout은 사용 불가...

(하지만 FrameLayout이 전혀 사용 할 수 없는건 아닙니다. 조금만 생각해 보시면 알 수 있습니다.)

 

그렇다면 RelativeLayout을 선택 해야 하는데...

RelativeLayout은 B라는 View는 A라는 View의 아래, 혹은 좌측, 혹은 우측

이런식으로 상대적인 위치를 지정하여 View들을 배치 하는 방법입니다.

 

그렇다면 원하는 위치만큼 위에서, 옆에서 떨어 뜨리고 싶으면 이렇게 하면 되지 않을까...

하고 생각해 봤습니다.

 

 

원하는 위치만큼 밀어낼 투명한 View들을 만들어서 공간을 차지하게 만드는 겁니다....

그런데 그림으로 봐도 불가능 할 것 같지 않나요...?

그냥 손으로 때려 박는 것이라면야 가능 하겠지만, 만약 코딩으로 원하는 위치에 표시하고 싶다거나

View의 위치를 옮긴다고 한다면 이런 방법은 전혀 말이 되지 않습니다.

 

그래서 좀더 생각을 해본 결과 RelativeLayout안에 있는 

Child View의 android:layout_margin 값을 이용 하면 된다는 결론이 나오게 되었습니다.

그림 한번 보시죠.

 

 

각 View마다 android:layout_margin 속성을 주는 겁니다.

이때 상대적인 위치는 지정할 필요가 없습니다.

따로 지정하지 않으면 기준위치는 왼쪽 상단이니까 딱 좋은게죠.

빨간색 View를 만약 (10, 10) 위치에 표시하고 싶다면,

android:layout_marginTop과 android:layout_marginLeft값을 각각 10으로 설정 하면 땡입니다.

물론 View를 여러개 추가해도 상관 없습니다.

 

아래는 위의 레이아웃을 구성한 실제 코드 입니다.

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF" >
    <View
        android:layout_width="100dip"
        android:layout_height="100dip"
        android:background="#FF0000"
        android:layout_marginTop="50dip"
        android:layout_marginLeft="50dip" />
    <View
        android:layout_width="100dip"
        android:layout_height="100dip"
        android:background="#00FF00"
        android:layout_marginTop="100dip"
        android:layout_marginLeft="190dip" />
    <View
        android:layout_width="100dip"
        android:layout_height="100dip"
        android:background="#0000FF"
        android:layout_marginTop="30dip"
        android:layout_marginLeft="320dip" />
 </RelativeLayout>

생각보다 간단합니다.

이제 원하는 위치에 View를 옮길 수 있게 되었습니다.

 

 

[More]

RelativeLayout의 margin속성을 이용한 트릭으로 잘 구성을 하긴 했습니다.

그런데 조금 욕심이 나는 부분이 바로 Custom Layout인거죠.

좀더 잘 해 보겠다! 라고 하시는 분들은 Layout따로 만드셔도 됩니다.

 

그런데 한가지 알아야할 사실은 1.5버전 소스를 보면

AbsoluteLayout.java 소스가 버젓이 살아있다는 것입니다.

@Deprecated로 이 Class가 더 이상 사용되지 않기를 바라지만,

사실 소스가 그대로 살아 있으니 얼마든지 쓸 수 있습니다.

그냥 소스 통채로 가져다 쓰면 되긴 하지만 그건 그냥 Deprecated 경고를 무시하고

AbsoluteLayout을 사용하는것과 다른게 없죠.

 

그래서 어떻게 Customize를 하는지 간단하게 살펴 보기로 하겠습니다.

방법만 알면 나중에 어떤 Layout을 구성하든지 다 만들어 낼 수 있는 법!

전체소스는 올릴 수 없으니 AbsoluteLayout.java 도 참고하며 공부해 보도록 합시다.

 

 

[More and More]

일단 Layout은 모두 ViewGroup을 상속 받습니다.

ViewGroup은 View의 특수한 형태... 라고 설명해 두었는데,

그냥 다른 View를 담을 수 있는 View인거죠.

그렇다면 아래와 같이 Class를 만들 수 있겠죠?

public class CustomLayout extends ViewGroup

네... 간단합니다.

Eclipse에서 필요한 생성자와 Method를 만들라고 할터이니 그대로 만들면 됩니다.

헌데, 생성자를 만들 때 한가지 중요한 것이 있습니다.

XML에서 사용하기 위해서는 반드시 아래 두개의 생성자가 필요 합니다.

public CustomLayout(Context context)
public CustomLayout(Context context, AttributeSet attrs)

위 두 생성자가 없으면 XML에서 사용 할 수 없습니다. 엄밀하게 말하면

public CustomLayout(Context context, AttributeSet attrs) 생성자가 XML에서 사용하는

생성자 입니다. 하지만 코딩을 할 때는 public CustomLayout(Context context)를 사용하는게

편하기 때문에 두개 다 쓰는 것이죠.

AttributeSet 만들려면 좀 귀찮습니다. 코딩할때는 필요도 없고요.

 

그리고 onLayout은 반드시 Override해야 합니다.

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)

onLayout이 실제로 Child View들을 배치 하는 부분입니다. 있다가 살펴 보기로 하죠.

 

Layout은 LayoutParams와 같이 사용해야 합니다.

굳이 LayoutParams를 사용하지 않겠다고 하시는 분들이 있을 수 도 있습니다.

그럴 경우에는 Child View에 레이아웃 정보를 담은 뒤에 

onLayout에서 불러 와서 배치 하면 됩니다....만 Custom View가 아닌 이상

정보를 어떻게 담을 수 있겠습니까? 결국 LayoutParams를 써야 합니다. 음허허!

 

LayoutParams는 보통 Inner Class로 만들어 둡니다.

(다른 LayoutParams들도 다 Inner Class로 만들어져 있습니다.)

CustomLayout Class안에 하나더 만들어 봅시다.

public static class LayoutParams extends ViewGroup.LayoutParams

ViewGroup.LayoutParams를 상속 받아 만듭니다.

한가지 주의할 것은 static으로 만들어야 한다는 것입니다.

ViewGroup.LayoutParams Class가 static이기 때문에,

생성자를 사용하기 위해서는 CustomLayout.LayoutParams도 static이어야 하죠.

x, y 좌표 값을 가질 수 있게 CustomLayout.LayoutParams Class을 조금 수정해 봅시다.

public int x
public int y

일단 x, y 두 필드를 추가 하고,

public LayoutParams(int width, int height)

요런 위와 같이 생긴 생성자를,

public LayoutParams(int width, int height, int x, int y)

요렇게 바꾸어 줍시다.

생성자에서 x, y 저장하는건 굳이 설명할 필요도 없을 정도로 간단합니다.

ViewGroup.LayoutParams가 기본적으로 public int width, public int height를 가지고 있기 때문에,

super(width, height)는 반드시 필요하고, 거기에 x, y값을 저장하는 코드만 더넣으면 땡입니다.

 

그럼 실제로 LayoutParams를 어떻게 쓰는지 코드를 살펴 봅시다.

아직 전부 설명하지 않았지만 먼저 LayoutParams부분을 보도록 하겠습니다.

CustomLayout layout = new CustomLayout(this);
View v = new View(this);
v.setLayoutParams(new CustomLayout.LayoutParams(100, 100, 10, 10));
layout.addView(v);

이게 전부 입니다. 전혀 어렵지 않습니다.

 

예전에도 포스팅을 했었는데, XML Inflating은 Depth-First 방식이기 때문에,

onLayout()이 호출 될 때는 Child View들이 이미 추가가 되어 있는 상태입니다.

 

그래서 onLayout()안에서는 getChildCount()로 Child의 수를 알아내고, 

getChildAt(i)으로 View를 얻어낸 뒤에, Child View의 getLayoutParams()을 사용해서

위치 정보를 얻어오면 됩니다.

마지막으로 Child View의 layout() 메소드를 호출하면 Child View가 자리를 잡게 되는 것이죠.

final void layout(int l, int t, int r, int b)

Left, Top, Right, Bottom값을 넣어 주어야 합니다.

저희가 알고 있는건 x, y, width, height 4가지 정보를 알고 있으니 간단하게 LTRB로 변환 가능 하겠죠?

 

 

[Further Works]

사실 저기까지 만들어 놓으면 코딩할때 밖에 쓸 수 없습니다.

XML에서 사용하기 위해서는

LayoutParams에 public LayoutParams(Context c, AttributeSet attrs)생성자를 다시 작성 해야 합니다.

obtainStyledAttributes()를 사용 해야 하는데,

요것에 대한 내용은 좀 길어서 지금은 빼놓도록 하겠습니다.