ANDROID 프로그래밍/LAYOUT

안드로이드 테이블레이아웃. (Android TableLayout)

뽀따 2017. 7. 28. 17:55


1. 안드로이드 TableLayout

지금까지 살펴 본 여러 Layout 클래스, 그 중에서 뷰의 화면 배치가 주 목적인 Layout 클래스를 사용하는 과정을 떠올려보면, 각 뷰(View)가 어디에 배치될 것인가에 대한 설정을, 개발자가 의도적으로 지정해야 하는 상황이 많습니다. 그런데 어떤 경우에는, 뷰(View)들 간의 위치 관계를 깊게 고민하지 않고 화면을 구성해도 되는 경우가 있습니다. 아래 그림과 같은 레이아웃을 구성한다고 가정해보죠.


그림과 같은 구조를 만들기 위해 RelativeLayout을 사용한다면, 모든 자식(Children) 뷰에 상대적 위치 지정을 위한 속성을 일일이 지정해야 하므로, 레이아웃 작성이 번거로워질 수 있습니다. 그나마 LinearLayout을 사용하면 좀 더 편하게 만들 수 있지만, 자식(Children) 뷰들의 가중치(weight)를 하나하나 지정해줘야 하는 문제가 있습니다. 이보다 더 좋은 방법이 없을까요? 음.. 있습니다. 그것은 바로 TableLayout을 사용하는 것입니다.


TableLayout은 앞서 살펴 본 그림에서와 같이, 표 형태의 레이아웃을 만들 때 사용하는 Layout 클래스입니다. "Table"이라는 단어의 여러 의미 중에 "표"가 있다는 것을 떠올리면 TableLayout이 어떻게 표시되는지 쉽게 이해할 수 있습니다.



일반적으로 테이블(Table)은 가로 방향으로 나열되는 열(Column)과 세로 방향으로 나열되는 행(Row)으로 구성됩니다. 문서에서 표를 작성하는 과정을 떠올려보면 그 구조를 쉽게 이해할 수 있을텐데요. TableLayout에서도 비슷한 개념으로 자식(Children) 뷰 위젯들을 배치할 수 있습니다. 단, 하나의 행(Row)을 추가할 때는 "<TableRow>"라는 요소를 명시적으로 사용하지만, 열(Column)을 추가하는 요소는 별도로 정의되어 있지 않습니다. 대신, "<TableRow>"에 뷰(View) 위젯을 선언하는 것만으로 새로운 열(Column)이 추가됩니다.


그러므로 TableLayout에서는 하나의 뷰(View)가 하나의 셀(Cell)이 됩니다.


HTML 태그(Tag)를 다뤄본 개발자라면, <table> 태그를 통해 TableLayout의 동작 방식을 좀 더 쉽게 이해할 수 있습니다. 웹(Web) 문서 작성 중, 정적(static) 요소를 정의하기 위해 HTML 태그를 사용하는데, 이 중 표 모양의 문서를 만들 때 <table> 태그가 사용됩니다.


<table> 태그를 사용해 표를 만들 때는 반드시 하나 이상의 행(Row)이 추가되어야 하며, 이 때 <tr> 태그가 사용됩니다. 그리고 하나의 행(Row) 안에 하나의 열(Column)을 추가하려면 <td> 태그를 사용합니다. 만약 동일한 행(Row)에 또 다른 열을 추가하고자 한다면 <td>를 연속적으로 사용하면 됩니다. 물론, </td>라는 끝(End) 태그는 빠트리지 말아야겠죠.


즉, <table> 태그를 사용하여 표를 만든다면, <tr> 태그를 사용해 행(Row)을 하나 추가하고, 여러 <td> 태그를 연속으로 사용하여 열(Column)들을 추가합니다. 그리고 새로운 행(Row)을 추가하려면 새로운 <tr> 태그를 추가하고, 다시 <td>로 열을 추가하게 되는 것이죠.


TableLayout의 TableRow는 <table> 태그 아래의 <tr>과 같은 역할을 수행합니다. 하지만 <td> 역할을 수행하는 요소는 따로 존재하지 않습니다. 그냥 TableRow 밑에 자식(Children) 뷰 위젯을 추가는 것만으로 <td> 태그와 같은 결과를 낼 수 있습니다.


2. TableLayout 사용법

2.1 TableLayout 기본 사용법

TableLayout을 사용하는 기본적인 방법은, TableLayout 아래에 TableRow를 선언하여 행(Row)을 추가하고, TableRow아래에 뷰(View) 위젯을 선언하여 열(Column)를 추가하는 것입니다.

TableLayout 기본 사용법 : TableLayout에 TableRow와 뷰(View) 위젯 추가.
    <TableLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TableRow>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:background="#F44336"
                android:textColor="#FFFFFF"
                android:text="A" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:background="#9C27B0"
                android:textColor="#FFFFFF"
                android:text="B" />
        </TableRow>
        <TableRow>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:background="#3F51B5"
                android:textColor="#FFFFFF"
                android:text="1" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:background="#00BCD4"
                android:textColor="#000000"
                android:text="2" />
        </TableRow>
    </TableLayout>


2.2 TableLayout 열(Column)의 갯수(count)

TableLayout 열(Column)의 갯수는 가장 많은 셀(Cell)을 가진 TableRow의 셀(Cell)의 갯수에 맞춰집니다. 예를 들어, 세 개의 TableRow가 각각 4, 2, 1개의 셀(Cell)을 가지는 경우, TableLayout의 전체 열(Column) 갯수는 4개가 됩니다. 이 때, TableLayout 열(Column)의 갯수보다 적은 수의 셀(Cell)을 가진 TableRow는 모자란 셀(Cell) 영역을 빈 공간으로 남겨두게 됩니다.

TableLayout의 최대 열(Column)의 갯수 : 가장 많은 자식(Chilren)을 가진 TableRow에 맞춰짐.
    <TableLayout ...>
        <TableRow ...>
            <TextView ...  androidid:text="A" />
            <TextView ...  androidid:text="B" />
            <TextView ...  androidid:text="C" />
            <TextView ...  androidid:text="D" />
        </TableRow>
        <TableRow ...>
            <TextView ...  androidid:text="1" />
            <TextView ...  androidid:text="2" />
        </TableRow>
        <TableRow ...>
            <TextView ...  androidid:text="a" />
        </TableRow>
    </TableLayout>


2.3 TableLayout 열(Column)의 너비(width)

기본적으로 TableLayout 열(Column)의 너비는 다른 행(Row), 같은 열(Column)의 셀(Cell) 중에서 가장 넓은 열(Column)의 너비에 맞춰집니다. 즉, TableLayout의 여러 TableRow에 추가된 같은 열(Column)의 자식(Children) 뷰 위젯 중에서, 가장 큰 너비를 가진 뷰(View) 위젯의 너비에 맞춰진다는 것입니다.

TableLayout 열(Column)의 너비 : 가장 넓은 열(Column)의 너비에 맞춰짐.
    <TableLayout ...>
        <TableRow>
            <TextView ...  android:text="AAAAAAAAAA" />
            <TextView ...  android:text="B" />
        </TableRow>
        <TableRow>
            <TextView ...  android:text="1" />
            <TextView ...  android:text="2" />
        </TableRow>
    </TableLayout>


예제 코드의 실행 화면을 보면, 첫 번째 열(Column)의 너비가 첫 번째 셀(Cell)인 "AAAAAAAAAA"의 크기와 정확히 일치하는 것을 확인할 수 있습니다.


그런데 예제 화면에서, TableLayout의 내용이 한쪽으로 치우쳐진 것을 볼 수 있습니다. 이는 모든 셀이 기본적으로 자신의 내용에 맞춰진 너비로 지정되기 때문입니다. 하지만 TableLayout을 사용하고자 하는 대부분의 경우, 이렇게 한쪽으로 쏠린 형태로 표시되길 원하는 경우는 거의 없을 것입니다. 대신 TableLayout의 전체 너비를 균등 분할(1/N)한 크기로 열(Column)의 너비를 지정하거나, 열(Column)의 위치(index)에 따라 어떤 열(Column)은 내용의 크기대로 표시하고, 다른 열은 나머지 공간을 모두 차지하도록 늘려서(stretch) 표시하고자 하는 경우가 더 많겠죠.


자 그럼, 어떻게 하면 TableLayout의 전체 너비를 고르게 사용할 수 있도록 만들 수 있을까요? 바로, TableLayout의 stretchColumns 속성을 사용하는 것입니다. stretchColumns 속성은, "stretch(늘이다)"와 "Columns(열)"라는 단어의 의미대로, TableLayout의 열 중에서 그 너비를 늘여서(stretch) 표시할 열(Column)을 지정하는 속성입니다.

  * android:stretchColumns - 늘이고자 하는 열(Column)의 인덱스 지정.
        > 열(Column)의 인덱스는 0부터 시작. (zero-based)
        > 하나 이상의 열(Column)을 ','(comma)로 분리하여 지정. (예. "0,2,4")
        > 모든 열(Column)을 지정하려면, '*'기호 사용.

아래와 같은 구조를 예를 들어 stretchColumns 속성의 동작을 알아보도록 하겠습니다.

    <TableLayout ...>
        <TableRow>
            <TextView ...  android:text="A" />
            <TextView ...  android:text="B" />
            <TextView ...  android:text="C" />
            <TextView ...  android:text="D" />
        </TableRow>
        <TableRow>
            <TextView ...  android:text="1" />
            <TextView ...  android:text="2" />
            <TextView ...  android:text="3" />
            <TextView ...  android:text="4" />
        </TableRow>
    </TableLayout>


예제의 TableLayout은 전체 4개의 열(Column)로 구성되어 있는데요, 만약 첫 번째와 세 번째는 원래 크기대로 표시하고, 두 번째와 네 번째 열만 너비를 늘이고자 한다면, stretchColumns에 "1,3"을 지정하면 됩니다. (첫 번째 열이 0부터(zero-based) 시작한다는 것에 주의하세요.)

TableLayout의 stretchColumns 속성 : 지정된 열 너비 늘이기.
    <TableLayout ...
        android:stretchColumns="1,3">
        <TableRow>
            <TextView ...  android:text="A" />
            <TextView ...  android:text="B" />
            <TextView ...  android:text="C" />
            <TextView ...  android:text="D" />
        </TableRow>
        <TableRow>
            <TextView ...  android:text="1" />
            <TextView ...  android:text="2" />
            <TextView ...  android:text="3" />
            <TextView ...  android:text="4" />
        </TableRow>
    </TableLayout>


만약, 전체 열(Column)의 너비가 늘어나게 만드려면, stretchColumns에 "*" 값을 지정하면 됩니다.

TableLayout의 stretchColumns 속성 : 모든 열 너비 늘이기.
    <TableLayout ...
        android:stretchColumns="*">
        <TableRow>
            <TextView ...  android:text="A" />
            <TextView ...  android:text="B" />
            <TextView ...  android:text="C" />
            <TextView ...  android:text="D" />
        </TableRow>
        <TableRow>
            <TextView ...  android:text="1" />
            <TextView ...  android:text="2" />
            <TextView ...  android:text="3" />
            <TextView ...  android:text="4" />
        </TableRow>
    </TableLayout>


2.4 TableLayout 행(Row)의 높이(height)

TableLayout 행(Row)의 높이도 열(Column)의 너비와 마찬가지로, 한 행(Row)의 셀(Cell) 중에서 가장 높은 셀(Cell)의 높이에 맞춰집니다.

TableLayout 행(Row)의 높이 : 가장 높은 셀(Cell)의 높이에 맞춰짐.
    <TableLayout ...
        android:stretchColumns="*">
        <TableRow>
            <TextView ...
                android:layout_height="match_parent"
                android:textSize="48sp"
                android:text="A" />
            <TextView ...
                android:layout_height="match_parent"
                android:text="B" />
            <TextView ...
                android:layout_height="match_parent"
                android:text="B" />
            <TextView ...
                android:layout_height="match_parent"
                android:text="B" />
        </TableRow>
        <TableRow>
            <TextView ...
                android:layout_height="match_parent"
                android:text="1" />
            <TextView ...
                android:layout_height="match_parent"
                android:text="2" />
            <TextView ...
                android:layout_height="match_parent"
                android:text="3" />
            <TextView ...
                android:layout_height="match_parent"
                android:textSize="60sp"
                android:text="4" />
        </TableRow>
    </TableLayout>


참고로, 행(Row) 높이와 관련하여, 열(Column)에서 사용했던 stretchColumns와 같은 속성은 존재하지 않습니다.

2.5 TableLayout, 개별 행(Row), 개별 셀(Cell)의 배경색 지정.

전체 TableLayout의 배경 색상을 변경하려면 "<TableLayout>" 요소에 "background" 속성을 사용하면 됩니다. 그리고 개별 행(Row) 전체의 배경 색상을 변경하려면 "<TableRow>"에 "background" 속성을 사용합니다. 마지막으로 하나의 셀(Cell) 영역의 배경 색상만을 변경하고자 한다면, 셀(Cell)에 해당하는 뷰(View) 위젯의 배경을 직접 변경하면 됩니다.


아래 예제는 TableLayout, TableRow, 그리고 뷰(View) 위젯에 bacground 속성을 사용하여 배경을 지정한 예제입니다.

TableLayout 또는 개별 행(Row)의 배경색 지정.
    <TableLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:background="#FF0000"
        android:padding="8dp"
        android:stretchColumns="*">

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#00FF00"
            android:padding="8dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="4dp"
                android:gravity="center"
                android:textSize="24sp"
                android:textColor="#FFFFFF"
                android:background="#3F51B5"
                android:text="A" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:textColor="#FFFFFF"
                android:background="#9C27B0"
                android:text="B" />
        </TableRow>
    </TableLayout>


TableLayout, TableRow의 배경색과 padding, layout_margin 속성을 이용하여 TableLayout에 테두리가 그려진 효과를 낼 수 있습니다.

TableLayout에 테두리 그리기
    <TableLayout
        android:background="#FF0000"
        android:padding="8dp"
        android:stretchColumns="*">

        <TableRow
            android:background="#000000">
            <TextView ... android:layout_margin="1dp"/>
            <TextView ... android:layout_margin="1dp"/>
            ...
        </TableRow>
        <TableRow
            android:background="#000000">
            <TextView ... android:layout_margin="1dp"/>
            <TextView ... android:layout_margin="1dp"/>
            ...
        </TableRow>
        ...
    </TableLayout>


3. TableLayout의 속성과 TableLayout.LayoutParams.

앞서 살펴본 기본 사용법에 더하여, TableLayout에는 다양한 방법과 형태의 레이아웃 구성을 위한 속성 및 LayoutParams가 제공됩니다. 지금부터 TableLayout 속성과 LayoutParams에 대해 살펴보도록 하겠습니다.

3.1 열(Column) 번호를 지정하여 셀(Cell) 추가하기. (layout_column)

아무런 옵션 없이 TableLayout의 행(Row)에 셀(Cell)을 추가하면, 자동적으로 열(Column) 번호가 증가합니다. 최초 추가되는 열(Column) 번호는 0부터 시작하며, 그 다음에 추가되는 셀(Cell)은 열(Column) 번호가 하나씩 증가하는 것이죠. 그런데 TableRow의 LayoutParams에는 순차적으로 셀(Cell)을 추가하지 않고, 열(Column)의 순서를 직접 지정하여 원하는 열(Column)에 바로 셀(Cell)을 추가하는 속성이 있습니다. layout_column이라는 속성입니다.

  * android:layout_column - 뷰(View)가 추가될 열(Column) 인덱스 지정.
        > 정수 값 사용. (예. 3)
TableRow.LayoutParams layout_column 속성 : 지정한 열(Column)에 셀(Cell) 추가하기.
    <TableLayout ...
        android:stretchColumns="*">

        <TableRow>
            <TextView ...
                android:text="A" />
            <TextView ...
                android:text="B" />
        </TableRow>

        <TableRow>
            <TextView ...
                android:text="1" />
            <TextView ...
                android:text="2"
                android:layout_column="4" />
        </TableRow>
    </TableLayout>


예제 코드의 실행 화면을 보면, 두 번째 행(Row), 두 번째 셀(Cell)의 layout_column 속성에 "4"를 지정하여, 1~3번 열(Column)을 건너뛰고 4번 열(Column)에 셀(Cell)이 바로 추가된 것을 확인할 수 있습니다.

3.2 두 개 이상의 셀(Cell) 합치기. (layout_span)

앞서 HTML "<table>" 태그와 TableLayout의 유사성에 대해 언급하였습니다. 요소의 이름과 사용법에는 다소 차이가 있지만, 테이블을 구성하는 기본 절차는 유사하죠. 그리고 또 하나의 유사성이 있습니다. 바로 셀(Cell) 합치기 기능이죠.


HTML의 "<td>" 태그에는 두 개 이상의 셀(Cell)을 합치는데 사용되는 colspan과 rowspan 속성이 존재합니다. colspan은 열(Column) 방향으로 셀(Cell)을 합치는 속성이고, rowspan은 행(Row) 방향으로 두 개 이상의 셀(Cell)을 합치는 속성입니다.


그리고 안드로이드의 TableLayout에도 셀(Cell)을 합치는데 사용되는 속성이 있습니다. 바로 layout_span 속성입니다.

  * android:layout_span - 합치고자 하는 셀(Cell)의 개수 지정.
        > 정수 값 사용. (예. 3)
        > 열(Column) 방향으로만 셀(Cell) 합치기 가능.
        > 값이 지정된 셀(Cell)의 오른쪽 방향으로 셀(Cell)이 합쳐짐.
        > 1이상의 값 사용 가능.

layout_span 속성을 사용하여 두 개 이상의 셀(Cell)을 합치는 예제는 아래와 같습니다.

TableRow.LayoutParams layout_span 속성 : 두 개 이상의 셀(Cell) 합치기.
    <TableLayout ...
        android:stretchColumns="*">

        <TableRow>
            <TextView ...
                android:text="A" />
            <TextView ...
                android:text="B" />
            <TextView ...
                android:text="C" />
        </TableRow>

        <TableRow>
            <TextView ...
                android:text="1" />
            <TextView ...
                android:text="2"
                android:layout_span="2" />
        </TableRow>
    </TableLayout>


layout_span을 사용함에 있어 주의할 점은, layout_span이 열(Column) 방향으로의 셀(Cell) 합치기만 지원한다는 것입니다. TableLayout에는 두 개의 행(Row)에 걸쳐 셀(Cell)을 합치는 기능은 제공되지 않습니다.

3.3 셀(Cell) 감추기. (collapseColumns)

TableLayout을 사용하다보면, 필요에 따라 특정 열(Column)을 감춰야 하는 경우가 있습니다. 표시해야 할 정보의 종류가 늘어나 열의 개수가 많아지면, 중요도가 낮은 정보를 가진 열(Column)은 보이지 않게 만듦으로써 내용을 좀 더 간결하게 표시할 수 있는 것이죠. 이 때, 특정 열(Column)을 보이지 않게 만드는 속성을 사용하면 되는데, 바로 collapseColumns 속성입니다.

  * android:collapseColumns - 감추고자 하는 열(Column)의 인덱스 지정.
        > 열(Column)의 인덱스는 0부터 시작. (zero-based)
        > 하나 이상의 열(Column)을 ','(comma)로 분리하여 지정. (예. "0,2,4")
        > 모든 열(Column)을 지정하려면, '*'기호 사용.

아래 예제는 TableLayout에 collapseColumns 속성을 사용하여, 1번과 2번 열(Column)을 보이지 않도록 만든 코드입니다.

TableLayout collapseColumns 속성 : 지정한 열(Column) 감추기.
    <TableLayout ...
        android:stretchColumns="*"
        android:collapseColumns="1,2">

        <TableRow>
            <TextView ...  android:text="A" />
            <TextView ...  android:text="B" />
            <TextView ...  android:text="C" />
            <TextView ...  android:text="D" />
            <TextView ...  android:text="E" />
        </TableRow>

        <TableRow>
            <TextView ...  android:text="1" />
            <TextView ...  android:text="2" />
            <TextView ...  android:text="3" />
            <TextView ...  android:text="4" />
            <TextView ...  android:text="5" />
        </TableRow>
    </TableLayout>


3.4 열(Column) 너비(width)가 자동으로 줄어들게 만들기. (shrinkColumns)

일반적인 경우, TableLayout 열(Column)의 너비에 대한 고민은, "어떻게 늘일 수 있는가"에 맞춰집니다. 열(Column)의 너비가 셀(Cell)의 내용에 맞춰지게 되면, 모든 내용이 한쪽으로 쏠리게 되기 때문에, 어떤 셀(Cell)들은 그 너비를 적당히 늘려서 표시할 필요가 있는 것이죠. 이를 위해 stretchColumns 라는 속성을 사용하는 방법에 대해 알아보았습니다.


그런데 반대의 경우를 생각해보죠. 만약 셀(Cell)의 너비가 점점 커져서 TableLayout의 전체 너비를 넘어버리게 되면, 오른쪽 열(Column)의 내용이 화면에 표시되지 않는 문제가 발생하게 됩니다. 아래의 경우처럼 말이죠.

    <TableLayout ...
        android:stretchColumns="*">

        <TableRow>
            <TextView ...  android:text="AAAAAAAAAA" />
            <TextView ...  android:text="BBBBBBBBBB" />
            <TextView ...  android:text="CCCCCCCCCC" />
            <TextView ...  android:text="DDDDDDDDDD" />
            <TextView ...  android:text="EEEEEEEEEE" />
        </TableRow>

        <TableRow>
            <TextView ...  android:text="1111111111" />
            <TextView ...  android:text="2222222222" />
            <TextView ...  android:text="3333333333" />
            <TextView ...  android:text="4444444444" />
            <TextView ...  android:text="5555555555" />
        </TableRow>
    </TableLayout>


자, 이렇게 TableLayout의 내용이 전체 너비를 넘어버리는 경우 우리가 선택할 수 있는 가장 직관적인 방법은, 특정 열(Column)의 너비를 자동으로 줄여서 TableLayout 전체 너비를 넘어서지 않도록 만드는 것입니다. 이 때 사용하는 속성이 바로 shrinkColumns 입니다.

  * android:shrinkColumns - 줄이고자 하는 열(Column)의 인덱스 지정.
        > 열(Column)의 인덱스는 0부터 시작. (zero-based)
        > 하나 이상의 열(Column)을 ','(comma)로 분리하여 지정. (예. "0,2,4")
        > 모든 열(Column)을 지정하려면, '*'기호 사용.

아래 예제는 shrinkColumns 속성에 "*"를 지정하여, 모든 열을 TableLayout의 너비에 맞게 줄인 결과를 보여줍니다.

TableLayout shrinkColumns 속성 : 지정한 열(Column)의 너비 줄이기.
    <TableLayout ...
        android:stretchColumns="*"
        android:shrinkColumns="*">

        <TableRow>
            <TextView ...  android:text="AAAAAAAAAA" />
            <TextView ...  android:text="BBBBBBBBBB" />
            <TextView ...  android:text="CCCCCCCCCC" />
            <TextView ...  android:text="DDDDDDDDDD" />
            <TextView ...  android:text="EEEEEEEEEE" />
        </TableRow>

        <TableRow>
            <TextView ...  android:text="1111111111" />
            <TextView ...  android:text="2222222222" />
            <TextView ...  android:text="3333333333" />
            <TextView ...  android:text="4444444444" />
            <TextView ...  android:text="5555555555" />
        </TableRow>
    </TableLayout>


4. TableLayout과 TableRow, 그리고 LinearLayout.

TableLayout의 동작과 화면 표시 형태가, 앞서 다른 글에서 살펴본 여러 레이아웃들과는 전혀 다른 새로운 형태의 레이아웃으로 여겨질 수도 있지만, 가만히 살펴보면 LinearLayout과 유사한 특징이 있음을 눈치챌 수 있습니다. 바로, 가로와 세로 방향으로 자식(Children) 뷰 위젯을 정렬한다는 것이죠.


LinearLayout은 orientation 속성에 따라, 가로 또는 세로 방향으로 자식(Children) 뷰 위젯을 정렬할 때 사용하는 레이아웃입니다. 그리고 TableLayout은 가로와 세로, 두 가지 방향으로 자식(Children) 뷰 위젯을 정렬하는 레이아웃이죠. 특히 TableRowTableLayout의 자식(Children)으로 사용된다는 것을 생각하면, TableLayout은 orientation이 vertical인 LinearLayout과 그 동작이 매우 유사하다고 할 수 있습니다.


그렇다면, 안드로이드에서는 이러한 유사점을 무시한 채, LinearLayoutTableLayout을 전혀 상관없는 클래스로 디자인했을까요? 아니오. 그렇지 않습니다. 좀 더 확장된 형태로 동작하는 TableLayoutLinearLayout을 상속받도록 설계하였습니다. 즉, LinearLayoutTableLayout의 부모인 것입니다.


TableRow도 마찬가지입니다. TableLayout에 하나의 행을 추가할 때 사용하는 TableRow는 orientaion이 horizontal인 LinearLayout과 동일한 방식으로 동작하죠. 그래서 TableRowLinearLayout을 상속합니다.


자, 그렇다면 TableLayoutTableRowLinearLayout을 상속받았다는 것이 무엇을 의미할까요? 바로, LinearLayout에 구현된 기능들을 사용할 수 있다는 것이죠. 어떤 기능들이 사용될 수 있는지는 지금부터 여러 예제들을 통해 살펴보도록 하겠습니다.

4.1 layout_weight를 사용하여 TableRow의 셀(Cell) 너비 조절하기.

[안드로이드 레이아웃 (Android Layout) - 3. 가중치(weight)를 이용한 영역 분할]에서 확인할 수 있듯이, LinearLayout에는 가중치(weight)를 이용하여, 지정된 비율에 따라 레이아웃의 영역을 분할하는 기능이 제공됩니다. layout_weight라는 속성으로 말이죠.


마찬가지로, LinearLayout을 상속받은 TableLayout에서도 layout_weight 속성을 사용하여 자식(Children)들의 영역을 비율에 따라 분할할 수 있습니다. 그리고 다시 한번 강조하자만, layout_weight 속성을 사용할 때는 layout_width 속성 값이 "0dp"로 지정되어야 정확히 의도하는 결과가 표시됩니다.

layout_weight 속성으로 열(Column) 너비 조절하기.
    <TableLayout ...>

        <TableRow>
            <TextView ...
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="A" />
            <TextView ...
                android:layout_width="0dp"
                android:layout_weight="2"
                android:text="B" />
            <TextView ...
                android:layout_width="0dp"
                android:layout_weight="3"
                android:text="C" />
        </TableRow>

        <TableRow>
            <TextView ...
                android:text="1" />
            <TextView ...
                android:text="2" />
            <TextView ...
                android:text="3" />
        </TableRow>
    </TableLayout>


단, layout_weight에 의한 열(Column) 너비 조절은 해당 TableRow에만 적용됩니다. 앞서 "2.3 TableLayout 열(Column)의 너비(width)"에서 설명했던, TableLayout의 전체 열(Column) 너비 결정 과정에 layout_weight에 의해 조절된 너비는 포함되지 않는 것이죠. 예제 화면의 두 번째 TableRow를 보면 그 의미를 확인할 수 있습니다.


그리고 layout_weight에 의해 조절된 열(Column)의 너비는 stretchColumns나 shrinkColumns 속성에도 영향을 받지 않습니다. 그러므로 layout_weight를 사용하여 모든 행(Row)이 같은 너비의 열을 갖도록 만드려면, 모든 셀(Cell)에 layout_weight 속성 값을 지정해야 합니다.

4.2 layout_weight를 사용하여 TableLayout의 TableRow 높이 조절하기.

TableLayout의 열(Column)들이 전체 영역을 고르게 사용할 수 있게 만드는 방법은, stretchColumns 속성을 사용하여 특정 열(Column) 또는 모든 열(Column)이 전체 너비에 맞게 늘어나게 만드는 것입니다. 그런데 행(Row)의 높이가 전체 영역에 맞게 늘어나게 만드려면 어떻게 해야 할까요? 단어 의미대로라면 stretchRows라는 속성이 있을 것 같은데, 그런 이름의 속성은 존재하지 않죠. 아니, 애초에 행(Row)의 높이를 전체 영역에 맞게 늘어나도록 만드는 속성은 존재하지 않습니다. 그럼 전혀 방법이 없는 걸까요?


이 때, TableLayoutLinearLayout의 자식이라는 것을 떠올려보면, 그 방법을 쉽게 유추해낼 수 있습니다. 바로, layout_weight 속성을 사용하는 것이죠. 물론, TableLayout의 layout_height는 "0dp"로 지정되어야 하며, layout_weight 속성은 TableLayout의 자식(Children)으로 사용되는 TableRow에 사용되어야 합니다.

layout_weight 속성으로 행(Row) 높이 조절하기.
    <TableLayout ...
        android:stretchColumns="*">

        <TableRow
            android:layout_height="0dp"
            android:layout_weight="1">

            <TextView ...  android:text="A" />
            <TextView ...  android:text="B" />
            <TextView ...  android:text="C" />
        </TableRow>

        <TableRow
            android:layout_height="0dp"
            android:layout_weight="1">

            <TextView ...  android:text="1" />
            <TextView ...  android:text="2" />
            <TextView ...  android:text="3" />
        </TableRow>

    </TableLayout>


4.3 TableRow가 아닌 뷰 위젯을 TableLayout의 자식으로 추가하기.

TableLayout에 하나의 행(Row)을 추가하기 위해서 TableRow를 사용하는게 일반적이지만, 다른 뷰(View) 위젯을 사용하여 TableLayout의 행(Row)을 추가할 수도 있습니다.

TableLayout의 행으로 TableRow가 아닌 일반 뷰(View) 사용하기.
    <TableLayout ...
        android:stretchColumns="*">

        <TableRow>
            <TextView ...  android:text="A" />
            <TextView ...  android:text="B" />
            <TextView ...  android:text="C" />
        </TableRow>

        <TextView ...
            android:text="TextView as Row">

        <TableRow>
            <TextView ...  android:text="1" />
            <TextView ...  android:text="2" />
            <TextView ...  android:text="3" />
        </TableRow>

    </TableLayout>


5. TableLayout 사용 예제

5.1 균등한 화면 분할이 필요한 화면. (계산기)

아마도 TableLayout을 사용하는 대부분의 경우는, 전체 영역을 동일한 비율로 나눈, 같은 크기의 셀(Cell)들로 레이아웃을 구성하려는 경우일 것입니다. 계산기 화면이 그 예가 될 수 있겠죠.

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="8dp"
        android:background="#404040"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:stretchColumns="*">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="4"
            android:textSize="60sp"
            android:textColor="#FFFFFF"
            android:background="#607D8B"
            android:gravity="right|center_vertical"
            android:text="0" />

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="7" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="8" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="9" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_span="2"
                android:textSize="32sp"
                android:text="DEL" />

        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="4" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="5" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="6" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="+" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="-" />
        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="1" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="2" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="3" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="*" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="/" />

        </TableRow>

        <TableRow
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_span="2"
                android:textSize="32sp"
                android:text="0" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:textSize="32sp"
                android:text="." />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_span="2"
                android:textSize="32sp"
                android:text="=" />

        </TableRow>
    </TableLayout>


5.2 표 형태의 화면.

TableLayout 사용의 또 다른 예로, 아래와 같은 표 형태의 레이아웃을 들 수 있습니다.

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:stretchColumns="*"
        android:shrinkColumns="*">

        <TableRow>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_span="7"
                android:background="#00BCD4"
                android:gravity="center"
                android:textSize="32sp"
                android:text="TITLE" />

        </TableRow>

        <TableRow>

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="16sp"
                android:text="1" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="16sp"
                android:text="2" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="16sp"
                android:text="3" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="16sp"
                android:text="4" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="16sp"
                android:text="5" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="16sp"
                android:text="6" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="16sp"
                android:text="7" />

        </TableRow>

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <ImageView
                android:layout_width="300dp"
                android:layout_height="150dp"
                android:background="#FFC107"
                android:id="@+id/image1" />

            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/text2"
                android:layout_toRightOf="@id/image1"
                android:text="INSERT" />

            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/text3"
                android:layout_below="@id/text2"
                android:layout_alignLeft="@id/text2"
                android:text="MODIFY" />

            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/text4"
                android:layout_below="@id/text3"
                android:layout_alignLeft="@id/text2"
                android:text="DELETE" />
        </RelativeLayout>

        <TableRow
            android:layout_marginTop="4dp">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_span="2"
                android:background="#8BC34A"
                android:textSize="24sp"
                android:gravity="center"
                android:text="ITEM-1" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_span="5"
                android:background="#CDDC39"
                android:textSize="24sp"
                android:gravity="center"
                android:text="DESC-1" />
        </TableRow>

        <TableRow
            android:layout_marginTop="4dp">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_span="2"
                android:background="#8BC34A"
                android:textSize="24sp"
                android:gravity="center"
                android:text="ITEM-2" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_span="5"
                android:background="#CDDC39"
                android:textSize="24sp"
                android:gravity="center"
                android:text="DESC-2" />
        </TableRow>
    </TableLayout>


6. 참고.

.END.