2010년 10월 28일 목요일

자원요소-레이아웃

뷰에 배치된 위젯이 화면에 나타나는 방식을 지정한다.

엄밀히 말하면 레이이웃은 사용자 UI를 Activity 클래스에 나타내는 아키텍처이다. 배치 구조를 정의하고 사용자에게 나타내는 모든 엘리먼트를 담고 있다. 레이아웃은 두 가지 방법으로 정의할 수 있다:
• XML 파일에서 UI 요소를 정의한다. 위젯이나 레이아웃 등과 같은 View 클래스 또는 그 서브클래스를 이용하여 정의하는 방법이다.
• 실행시에 레이아웃 엘리먼트를 인스턴스화한다. 애플리케이션에서는 프로그램으로 View 나 ViewGroup 객체를 생성(및 그 속성을 정의)할 수 있다.

안드로이드 프레임웍은 UI를 구성하는데 이 중 한 가지나 두 가지를 합한 방식 모두를 지원한다. 예를 들어, 애플리케이션의 스크린 엘리먼트 등의 디폴트 레이아웃은 XML에 정의하고, 그 객체의 상태를 변경시키거나 객체를 추가하는 일은 코드 부분에 넣는 것어서 실행시에 나타나게 하는 것이 가능하다.

주요 뷰 그룹(뷰그룹과 레이아웃 클래스는 유사 개념)

주요 뷰 그룹 요약-다음에 열거하는 뷰 그룹 객체는 모두 자식UI엘리먼트를 갖고 있다. 어떤 것은 화면에 나타나는 UI에 대한 형태를 지정하고, 어떤 것은 자식 뷰들의 배치를 관리하는 구조만을 결정한다.

FrameLayout: 한 개 객체만을 보여주는 뷰 프레임을 설정.
Gallery: 한정된 리스트 내의 이미지를 좌우로 스크롤해서 보여주는 프레임.
GridView: m개 칼럼, n개 로우를 스크롤 가능한 그리드로 보여줌.
LinearLayout: 자식 뷰들을 하나의 수평 또는 수직 화면으로 구성하는 레이아웃. 이 레이아웃은 화면 크기보다 윈도우의 크기가 크면 스크롤바를 생성한다.
ListView: 상하 스크롤 가능한 목록을 만들어준다.
RelativeLayout: 자식 객체의 상호 관계(B객체의 좌측에 A객체 배치)  또는 자식과 부보의 관계(부모의 위에 배치)형태로 뷰의 위치를 지정하도록 한다.
ScrollView: 목록이 아닌 객체에 대해서 상하 스크롤바가 생기도록 한다.
Spinner: 지정된 목록의 한 개 항목을 하나의 텍스트박스 형태로 표시한다. 수평 혹은 수직으로 스크롤할 수 있는 리스트박스와 유사하다.
SurfaceView: 그리기 화면에 대한 직접 액세스를 제공한다. 화면의 위에 자식 뷰를 얹을 수는 있지만, 위젯보다는 그림 픽셀을 그릴 필요가 있을 때 사용하도록 하는 목적으로 제공되는 기능이다.
TabHost: 탭 선택 형태를 제공하는 뷰그룹으로 탭을 클릭하면 애플리케이션이 다른 화면으로 변경되도록 하는 기능을 제공한다.
TableLayout: 지정된 행과 열을 구성하도록 하는 테이블 형태 레이아웃으로, 각 셀은 각각의 위젯을 가질 수 있다. 행의 크기를 가장 큰 셀의 크기에 맞춰진다. 셀 경계선은 나타나지 않는다.
ViewFlipper: 한 개 텍스트박스 안에 한 번에 하나의 항목을 보여주는 목록을 가진 객체. 슬라이드 쇼처럼 지정된 시간 간격마다 스와핑이 되도록 설정할 수 있다.
ViewSwitcher: ViewFlipper와 동일.

FrameLayout
가장 단순한 형태의 레이아웃 객체이다. 기본적으로 스크린상의 빈 공간이며 나중에 하나의 객체로 화면을 채울 수 있다. 이 프레임 레아이웃의 모든 차일드 엘리먼트는 화면의 좌상단에 고정된다; 다른 위치를 지정할 수는 없다. 차일드 뷰들은 이전 뷰 위에 차례로 그려진다.

LinearLayout

LinearLayout은 모든 자식뷰들을 한 방향으로 배치한다. 하지만 기본 방향이나 여백을 다음 다섯 가지 프로퍼티를 통해 지정할 수 있다.
- orientation: android:orientation 프로퍼티를 horizontal이나 vertical 로 지정할 수 있다. 또는 코드에서 setOrientation() 메쏘드에서 HORIZONTAL/VERTICAL로 지정할 수 있다.
- fill model: pixel 수, wrap_content, fill_parent 등으로 설정.
- weight: widget의 중요도를 1,2 등으로 줄 수 있다.
- gravity:android:layout_gravity 프로퍼티를 left, center_horizontal, right로 줄 수 있다.
- padding:android:padding으로 전체 패딩을 지정한다. 하지만 각 영역별로 지정하려면 android:paddingLeft, android:paddingRight, android:paddingTop, android:paddingBottom을 사용한다.

ex) 텍스트뷰, 버튼을 배치한 LinearLayout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

TableLayout
TableLayout은 그 자식들을 행과 열로 배치한다. 하지만 행,열, 또는 셀에 대한 경계선은 보여주지 않는다. 테이블은 빈 셀을 가질 수 있지만, HTML에서처럼 칼럼 영역을 넘어설 수는 없다.

ex) 2x2 테이블 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:stretchColumns="1">
    <TableRow>
        <TextView
            android:text="@string/table_layout_4_open"
            android:padding="3dip" />
        <TextView
            android:text="@string/table_layout_4_open_shortcut"
            android:gravity="right"
            android:padding="3dip" />
    </TableRow>

    <TableRow>
        <TextView
            android:text="@string/table_layout_4_save"
            android:padding="3dip" />
        <TextView
            android:text="@string/table_layout_4_save_shortcut"
            android:gravity="right"
            android:padding="3dip" />
    </TableRow>
</TableLayout>

RelativeLayout
RelativeLayout 은 자식뷰가 자신의 위치를 부모 또는 다른 뷰에 대한 상대적 위치로 지정한다. 그래서 두 엘리먼트를 오른쪽으로 또는 하나가 다른 뷰의 아래에 있도록, 화면의 중앙, 중앙 왼쪽 등으로 지정할 수 있다. 엘리먼트는 주어진 순서에 따라 그려지므로, 첫 번째 엘리먼트가 화면의 중앙에 그려진다면, 다른 엘리먼트는 중앙 엘리먼트에 대해 배치되므로 화면 중앙에 대한 상대적 위치로 그려진다. 또한, 이런 순서 때문에, 이 레이아웃을 정하는데 XML을 사용한다면, 참조할 엘리먼트는 (다른 뷰 객체를 배치하기 위해) 참조ID로 다른 뷰를 참조하기 전에 미리 XML 파일에 나타나 있어야 한다.
정의된 RelativeLayout의 파라메터는: width, height, below, alignTop, toLeft, padding[Bottom|Left|Right|Top], margin[Bottom|Left|Right|Top] 등이 있다.



ex) 텍스트뷰, 버튼을 배치한 RelativeLayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/blue"
                android:padding="10px" >

    <TextView android:id="@+id/label"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:text="Type here:" />

    <EditText android:id="@+id/entry"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:background="@android:drawable/editbox_background"
              android:layout_below="@id/label" />

    <Button android:id="@+id/ok"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/entry"
            android:layout_alignParentRight="true"
            android:layout_marginLeft="10px"
            android:text="OK" />

    <Button android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toLeftOf="@id/ok"
            android:layout_alignTop="@id/ok"
            android:text="Cancel" />
</RelativeLayout>



AbsoluteLayout
절대좌표 형태로 뷰의 위치를 지정하는 방식이다. 아주 쉽게 화면에 뷰를 배치할 수 있지만, 안드로이드 단말기의 해상도가 워낙 다양하므로 가급적 사용하지 않는 것이 좋다.
며칠 전 Google에서 AbsoluteLayout 엘리먼트를 deprecate 하겠다고 밝혔다. 즉, 이후 SDK 버전에서는 이 레이아웃을 사용할 수 없다는 의미이다. 역시, 사용하지 않는 것이 좋겠다.

TabHost
탭메뉴 형태의 화면을 만들 때 TabHost 를 이용한다.

ex) 탭별 화면을 정적으로 구성할 경우의 TabHost
<?xml version="1.0" encoding="utf-8"?>
<TabHost
   xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent">
            <TextView
                android:id="@+id/textview1"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:text="this is a tab" />
            <TextView
                android:id="@+id/textview2"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:text="this is another tab" />
            <TextView
                android:id="@+id/textview3"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:text="this is a third tab" />
        </FrameLayout>
    </LinearLayout>
</TabHost>


ex) 탭별 화면을 동적으로 구성할 경우의 TabHost
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:padding="5dp">
        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:padding="5dp" />
    </LinearLayout>
</TabHost>

* FrameLayout 내에 TextView 가 없으므로 화면을 나타내려면 코드 요소에서 intent를 사용하여 나타나도록 해야 한다.

ScrollView
ex) 스크롤뷰 설정 기본 예
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TableLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="0">
<TableRow>
<View
android:layout_height="80px"
android:background="#000000"/>
<TextView android:text="#000000"
android:paddingLeft="4px"
android:layout_gravity="center_vertical" />
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#440000" />
<TextView android:text="#440000"
android:paddingLeft="4px"
android:layout_gravity="center_vertical" />
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#884400" />
<TextView android:text="#884400"
android:paddingLeft="4px"
android:layout_gravity="center_vertical" />
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#aa8844" />
<TextView android:text="#aa8844"
android:paddingLeft="4px"
android:layout_gravity="center_vertical" />
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#ffaa88" />
<TextView android:text="#ffaa88"
android:layout_gravity="center_vertical" />
</TableRow>
<TableRow>
<View
android:layout_height="80px"
android:background="#ffffff" />
<TextView android:text="#ffffff"
android:paddingLeft="4px"
android:layout_gravity="center_vertical" />
</TableRow>
</TableLayout>
</ScrollView>

GridView
ex) 그리드뷰 기본 설정 예
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<GridView
android:id="@+id/grid"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:verticalSpacing="35px"
android:horizontalSpacing="5px"
android:numColumns="auto_fit"
android:columnWidth="100px"
android:stretchMode="columnWidth"
android:gravity="center"
/>
</LinearLayout>


import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.TextView;
import android.widget.AdapterView;

public class GridDemo extends Activity
implements AdapterView.OnItemSelectedListener {
TextView selection;
String[] items={"lorem", "ipsum", "dolor", "sit", "amet",
"consectetuer", "adipiscing", "elit", "morbi", "vel",
"ligula", "vitae", "arcu", "aliquet", "mollis",
"etiam", "vel", "erat", "placerat", "ante",
"porttitor", "sodales", "pellentesque", "augue", "purus"};

public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.grid);
selection=(TextView)findViewById(R.id.selection);
GridView g=(GridView) findViewById(R.id.grid);
g.setAdapter(new FunnyLookingAdapter(this,
android.R.layout.simple_list_item_1,items));
g.setOnItemSelectedListener(this);
}
public void onItemSelected(AdapterView parent, View v, int position, long id) {
selection.setText(items[position]);
}
public void onNothingSelected(AdapterView parent) {
selection.setText("");
}
private class FunnyLookingAdapter extends ArrayAdapter {
Context ctxt;
FunnyLookingAdapter(Context ctxt, int resource, String[] items) {
super(ctxt, resource, items);
this.ctxt=ctxt;
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView label=(TextView)convertView;
if (convertView==null) {
convertView=new TextView(ctxt);
label=(TextView)convertView;
}
label.setText(items[position]);
return(convertView);
}
}
}


뷰그룹 객체의 속성 지정
Attribute

모든 View와 ViewGroup 객체는 자신의 다양한 XML 속성을 지원한다. 어떤 속성은 특정 View 객체에 한정되지만(예를 들어 TextView 는 textSize 속성을 지원), 이들 속성은 View 객체에서 상속된 것이고, 따라서 모든 View 객체의 공통속성들이 있다. 그리고, "layout parameter" 로 간주되는 속성이 있으며, 이것은 ViewGroup 객체에 의해 정의된 View 객체의 레이아웃 상의 특정 위치를 지정하는 속성이다.

ID: 모든 뷰 객체는 xml 트리상에서 자신을 고유하게 식별하는 정수값 ID를 가질수 있다. XML 태그 안의 ID의 신택스는 다음과 같다:
android:id="@+id/my_button"
문자열 시작 위치에 있는 @ 기호는 XML 해석기가 나머지 DI 문자열을 해석하고 그것을 ID 자원으로 가져가야 한다는 의미이다. + 기호는 이것이 새로운 자원이며 따라서 애플리케이션의 리소스(R.java 파일)에 생성하고 추가해야 한다는 의미이다. 그 외에도 안드로이드 프레임웍이 제공하는 많은 ID 자원들이 있다. 안드로이드 자원 ID를 참조할 때는 더하기 기호를 사용할 필요가 없고, 대신에 다음과 같이 안드로이드 패키지 네임스페이스를 추가해야 한다:
android:id="@android:id/empty"
안드로이드 패키지 네임스페이스를 사용함으로써, 로컬 리소스 클래스가 아닌 android.R 리소스 클래스를 참조하게 된다.
뷰를 생성하고 애플리케이션에서 그것을 참조하려면 통상적인 패턴은:
1. 레이아웃 파일에 뷰/위젯을 정의하고 거기에 고유한 ID를 할당한다:
2. <Button android:id="@+id/my_button"
3.        android:layout_width="wrap_content"
4.        android:layout_height="wrap_content"
        android:text="@string/my_button_text"/>
5. 다음에 뷰 객체의 인스턴스를 생성하고 레이아웃에서 그것을 찾아낸다(보통 onCreate() method 안에서 코딩):
Button myButton = (Button) findViewById(R.id.my_button);
ID는 전체 XML 트리 안에서 고유할 필요는 없지만, 검색하고 있는 트리 안(이것이 전체 트리가 될 수 있으므로, 가능하다면 완전히 고유하도록 정하는 것이 좋다)에서는 고유해야 한다.

Layout Parameters: layout_something 형태의 XML 레이아웃 속성이 View에 대한 레이아웃 파라메터이다. 모든 ViewGroup 클래스는 ViewGroup.LayoutParams 를 상속하는 네스티드 클래스를 구현한다. 이 서브클래스에는 각 차일드 뷰의 크기와 위치를 정의하는 속성이 들어 있다. 각 부모 뷰 그룹은 각 자식 뷰의 레이아웃 파라메터를 결정한다.(레이아웃 파라메터 상속 그림 참조)
모든 LayoutParams 서브클래스는 값을 설정하는 자신의 신택스를 갖고 있다. 각 차일드 엘리먼트는 부모에게 맞는 LayoutParams를 정의해야 한다. 하지만 이것은 자신의 차일드를 위해 다른 LayoutParams로 정의할 수도 있다.

모든 뷰 그룹에는 layout_width and layout_height이 포함되어 있고, 각 뷰는 이것을 정의해야 한다. 많은 LayoutParams에는 선택사항인 margin과 border도 포함되어 있다. 정확한 width, height 수치를 지정할 수도 있다. 부모와의 상대적 크기인 wrap_content, fill_parent 로 지정하는 것을 더 권장한다.

ex) LayoutParams 속성 지정
<?xml version="1.0" encoding="utf-8"?>
<!-- res/main_screen.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"    // The object's own orientation property
              android:padding="4"               // Inherited View property
              android:gravity="center"          // The object's own property
              android:layout_width="fill_parent"  // Parent object's LinearLayout.LayoutParams.width
              android:layout_height="fill_parent"> // Parent object's LinearLayout.LayoutParams.height

   <TextView android:layout_width="fill_parent"   // TextView.LayoutParams.width
             android:layout_height="wrap_content" // TextView.LayoutParams.height
             android:layout_weight="0"            // TextView.LayoutParams.weight
             android:paddingBottom="4"            // TextView.paddingBottom
             android:text="@string/redirect_getter"/> // TextView.text

   <EditText android:id="@+id/text"
             android:layout_width="fill_parent"   // EditText.LayoutParams.width
             android:layout_height="wrap_content" // EditText.LayoutParams.height
             android:layout_weight="0"            // EditText.LinearLayoutParams.weight
             android:paddingBottom="4">           // EditText.paddingBottom
       <requestFocus />
   </EditText>

   <Button android:id="@+id/apply"
           android:layout_width="wrap_content"  // Button.LayoutParams.width
           android:layout_height="wrap_content" // Button.LayoutParams.height
           android:text="@string/apply" />      // TextView.text
</LinearLayout>

Position

뷰의 표시영역은 사각형이다. 뷰는 왼쪽 최상단을 나타내는 로케이션과, 넓이와 높이를 나타내느 디멘전을 갖고 있다. 로케이션과 디멘전의 단위는 픽셀이다.
getLeft와 getTop 메쏘드를 사용하여 뷰의 위치를 알아낼 수 있다. getLeft는 사각형의 왼쪽 위치 즉 X좌표를 돌려주고, getTop은 사각형의 최상단, 즉 Y좌표를 돌려준다. 이들 메쏘드는 모두 부모 뷰에 대한 상대적 위치로 좌표를 돌려준다. 예를 들어 getLeft()가 20으로 나오면, 이것은 그 뷰가 부모 뷰의 왼쪽 끝에서 오른쪽으로 20 픽셀 이동한 위치에 있다는 의미이다.
이외에도 getRight, getBottom 등 유용한 메쏘드들이 있다. 사용예로는, getRight() 를 쓰면 getLeft() + getWidth() 를 계산한 것과 같은 결과를 받는다.

Size, Padding, Margin

뷰의 크기는 넓이와 높이로 표현된다. 실제로 뷰는 넓이와 높이 값의 두 쌍을 갖고 있다.
첫번째 쌍은 측정 넓이와 높이라 한다. 이 디멘전은 그 뷰가 부모 뷰 안에서 얼마나 크게 나타날지를 정한다. 이 측정 디멘전은 getMeasuredWidth() 와 getMeasuredHeight()로 구할 수 있다.
두 번째 쌍은 단순한 넓이와 높이 또는 드로잉 넓이와 드로잉 높이이다. 이 디멘전은 화면에서의 실제 뷰의 크기를 정의한다. 이 값은 측정 넓이, 높이와 다를 수 있다. 이 값은 getWidth() 와 getHeight() 메쏘드로 구할 수 있다.
디멘전을 측정하기 위해 뷰는 패딩을 고려한다. 패딩은 뷰의 왼쪽,상단,우측,하단 부분에 대한 픽셀값으로 표현된다. 패딩은 뷰의 내용을 특정 픽셀만큼 떨어져서 표현할 때 사용할 수 있다. 예를 들어, 왼쪽 패딩 2 를 하면 뷰의 내용이 왼쪽 끝에서 2 픽셀만큼 떨어져서 나타난다. 패딩을 설정하려면  setPadding(int, int, int, int) 메쏘드를 이용하고, getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom() 메쏘드로 조회할 수 있다.
뷰는 패딩을 정할 수 있지만, 마진은 전혀 지원하지 않는다. 하지만 뷰 그룹은 지원한다.

댓글 없음:

댓글 쓰기