Android用戶界面設計:表格布局
表格布局圖可以用來顯示表格式數據或者像網頁上的HTML表格一樣制作排列整齊的界面。本教程講述如何分別運用XML布局文件和通過代碼來創建表格布局。
理解布局對于良好的Android程序設計來說是非常重要的。在這個教程里,你將學到所以關于框架布局的知識,它主要用于在界面上以整潔的行和列方式組織用戶界面控件或小工具。使用得當的話,表格布局圖可以成為強大的范例,Android程序可以基于它們設計他們的界面和顯示表格數據。
什么是表格布局?
正如其字面的意思,表格布局就是一系列行和列組成的網格,并可以在這些網格的單元格中顯示視圖控件。從用戶界面設計的角度看,一個TableLayout由一系列TableRow控件組成,每個TableRow控件對應表格里的一行。TableRow的內容由單元格中的視圖控件組成。
TableLayout的外觀通過一些附加的規則來管理。首先,整個表格的列數要與表中列數最多的行的列數一致。其次,列寬被定義為顯示最大寬度內容的列的寬度。TableLayout的子行與子單元的layout_width屬性總是設置為MATCH_PARENT——盡管它們可以在XML文件中定義,但其實際寬度是不能被覆蓋的。TableLayout的單元格的layout_height也可以定義,但是TableRow的layout_height屬性值總是WRAP_CONTENT。單元格可以跨列,但不能跨行。這個功能可以通過TableRow的子視圖的layout_span屬性來實現。一個單元格就是TableRow中的單個子視圖。如果想得到一個更復雜的具有多視圖單元格,就要用一個布局視圖來封裝其它的視圖。
也就是說,有些規則可以被修改。列可以被標識為可拉伸的,這意味其著寬度可以擴展到父容器的寬度。列也可以被標識成可壓縮的,這意味著可以縮小其寬度使整個行能符合父容器所能提供的空間。你也可以合并一整列。
如果想參閱有關表格布局的完整文檔,請訪問TableLayout類的Android SDK文檔。用于XML資源中的相關XML屬性在文獻中也有定義。
設計一個簡單的表格布局
布局通過實例來解釋最有說服力,表格布局也不例外。假設我們希望設計一個界面來顯示未來幾天的天氣狀況。用表格布局來組織信息就是一個很好的選擇:
- 在第一個TableRow中,我們可以顯示界面的標題。
- 在第二個TableRow中,我們可以用類似日歷的形式來顯示日期。
- 在第三個TableRow中,我們可以顯示每天的高溫信息。
- 在第四個TableRow中,我們可以顯示每天的低溫信息。
- 在第五個TableRow中,我們可以顯示一些能說明天氣狀況的圖案,比如雨天,下雪,晴天,或者多云。
第一張圖顯示了布局編輯器中的預覽畫面:
用XML布局資源來定義表格布局
設計程序用戶界面最方便和可維護的方法是創建XML布局資源。這個方法極大地簡化了UI設計過程,將很多靜態創建和用戶界面控件的布局以及控件屬性的定義移到XML中去,取代了寫代碼。
XML布局資源必須存儲在/res/layout項目目錄下。讓我們看看前一節介紹的表格布局。這個布局資源文件命名為/res/layout /framed.xml,在XML中如下定義:
<?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tableLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:shrinkColumns="*" android:stretchColumns="*"> <TableRow android:id="@+id/tableRow4" android:layout_height="wrap_content" android:layout_width="match_parent" android:gravity="center_horizontal"> <TextView android:id="@+id/textView9" android:layout_width="match_parent" android:layout_height="wrap_content" android:textStyle="bold" android:typeface="serif" android:textSize="18dp" android:text="Weather Table" android:gravity="center" android:layout_span="6"></TextView> </TableRow> <TableRow android:id="@+id/tableRow1" android:layout_height="wrap_content" android:layout_width="match_parent"> <TextView android:id="@+id/TextView04" android:text=""></TextView> <TextView android:id="@+id/TextView04" android:text="Feb 7" android:textStyle="bold" android:typeface="serif"></TextView> <TextView android:id="@+id/TextView03" android:text="Feb 8" android:textStyle="bold" android:typeface="serif"></TextView> <TextView android:id="@+id/TextView02" android:text="Feb 9" android:textStyle="bold" android:typeface="serif"></TextView> <TextView android:id="@+id/TextView01" android:text="Feb 10" android:textStyle="bold" android:typeface="serif"></TextView> <TextView android:text="Feb 11" android:id="@+id/textView1" android:textStyle="bold" android:typeface="serif"></TextView> </TableRow> <TableRow android:layout_height="wrap_content" android:id="@+id/tableRow2" android:layout_width="match_parent"> <TextView android:text="Day High" android:id="@+id/textView2" android:textStyle="bold"></TextView> <TextView android:id="@+id/textView3" android:text="28°F" android:gravity="center_horizontal"></TextView> <TextView android:text="26°F" android:id="@+id/textView4" android:gravity="center_horizontal"></TextView> <TextView android:text="23°F" android:id="@+id/textView5" android:gravity="center_horizontal"></TextView> <TextView android:text="17°F" android:id="@+id/textView6" android:gravity="center_horizontal"></TextView> <TextView android:text="19°F" android:id="@+id/textView7" android:gravity="center_horizontal"></TextView> </TableRow> <TableRow android:layout_height="wrap_content" android:id="@+id/tableRow2" android:layout_width="match_parent"> <TextView android:text="Day Low" android:id="@+id/textView2" android:textStyle="bold"></TextView> <TextView android:text="15°F" android:id="@+id/textView3" android:gravity="center_horizontal"></TextView> <TextView android:text="14°F" android:id="@+id/textView4" android:gravity="center_horizontal"></TextView> <TextView android:text="3°F" android:id="@+id/textView5" android:gravity="center_horizontal"></TextView> <TextView android:text="5°F" android:id="@+id/textView6" android:gravity="center_horizontal"></TextView> <TextView android:text="6°F" android:id="@+id/textView7" android:gravity="center_horizontal"></TextView> </TableRow> <TableRow android:id="@+id/tableRow3" android:layout_height="wrap_content" android:layout_width="match_parent" android:gravity="center"> <TextView android:id="@+id/textView8" android:text="Conditions" android:textStyle="bold"></TextView> <ImageView android:id="@+id/imageView1" android:src="@drawable/hot"></ImageView> <ImageView android:id="@+id/imageView2" android:src="@drawable/pt_cloud"></ImageView> <ImageView android:id="@+id/imageView3" android:src="@drawable/snow"></ImageView> <ImageView android:id="@+id/imageView4" android:src="@drawable/lt_snow"></ImageView> <ImageView android:id="@+id/imageView5" android:src="@drawable/pt_sun"></ImageView> </TableRow> </TableLayout>
回憶一下,在Activity中,只需要在onCreate()方法中添加一行代碼來在屏幕上加載和顯示布局資源。如果布局資源存放在/res/layout/framed.xml文件中,這行代碼應該是:
setContentView(R.layout.table);
該表格布局將所有列都通過在值中使用“*”設置為可以壓縮和拉伸。如果只是特定的列需要壓縮或者拉伸,那么就需要用一組由逗號分隔的數字來設置(列數索引從0開始)。
下圖就是豎屏和橫屏模式下該表格的樣子。
用程序定義表格布局
你也可以用程序創建和配置表格布局。這通過使用TableLayout類和TableRow類(android.widget.TableLayout和android.widget.TableRow)來實現。你會在TableLayout.LayoutParams和TableRow.LayoutParams中找到針對每一個控件的唯一的顯示參數。同樣地,典型的布局參數 (android.view.ViewGroup.LayoutParams),比如layout_height和layout_width,以及邊距參 數(ViewGroup.MarginLayoutParams),也能用在TableLayout和TableRow對象上,但不必用在表格單元格上。對于表格單元格(TableRow中的任意視圖),寬始終是MATCH_PARENT。高可以定義,但是默認值是WRAP_CONTENT,不需要特別指定。
你必須用Java創建屏幕內容,然后向setContentView()方法提供一個包含所有要作為子視圖顯示的控件內容的父布局對象,而不是像前面所示 直接使用setContentView()方法來加載布局資源。在這里,你的父布局就是創建的表格布局。例如,下面的代碼示例了如何用程序在Activity中實例化TableLayout布局參數并重新創建前面XML描述的相同的布局。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TableLayout table = new TableLayout(this); table.setStretchAllColumns(true); table.setShrinkAllColumns(true); TableRow rowTitle = new TableRow(this); rowTitle.setGravity(Gravity.CENTER_HORIZONTAL); TableRow rowDayLabels = new TableRow(this); TableRow rowHighs = new TableRow(this); TableRow rowLows = new TableRow(this); TableRow rowConditions = new TableRow(this); rowConditions.setGravity(Gravity.CENTER); TextView empty = new TextView(this); // title column/row TextView title = new TextView(this); title.setText("Java Weather Table"); title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); title.setGravity(Gravity.CENTER); title.setTypeface(Typeface.SERIF, Typeface.BOLD); TableRow.LayoutParams params = new TableRow.LayoutParams(); params.span = 6; rowTitle.addView(title, params); // labels column TextView highsLabel = new TextView(this); highsLabel.setText("Day High"); highsLabel.setTypeface(Typeface.DEFAULT_BOLD); TextView lowsLabel = new TextView(this); lowsLabel.setText("Day Low"); lowsLabel.setTypeface(Typeface.DEFAULT_BOLD); TextView conditionsLabel = new TextView(this); conditionsLabel.setText("Conditions"); conditionsLabel.setTypeface(Typeface.DEFAULT_BOLD); rowDayLabels.addView(empty); rowHighs.addView(highsLabel); rowLows.addView(lowsLabel); rowConditions.addView(conditionsLabel); // day 1 column TextView day1Label = new TextView(this); day1Label.setText("Feb 7"); day1Label.setTypeface(Typeface.SERIF, Typeface.BOLD); TextView day1High = new TextView(this); day1High.setText("28°F"); day1High.setGravity(Gravity.CENTER_HORIZONTAL); TextView day1Low = new TextView(this); day1Low.setText("15°F"); day1Low.setGravity(Gravity.CENTER_HORIZONTAL); ImageView day1Conditions = new ImageView(this); day1Conditions.setImageResource(R.drawable.hot); rowDayLabels.addView(day1Label); rowHighs.addView(day1High); rowLows.addView(day1Low); rowConditions.addView(day1Conditions); // day2 column TextView day2Label = new TextView(this); day2Label.setText("Feb 8"); day2Label.setTypeface(Typeface.SERIF, Typeface.BOLD); TextView day2High = new TextView(this); day2High.setText("26°F"); day2High.setGravity(Gravity.CENTER_HORIZONTAL); TextView day2Low = new TextView(this); day2Low.setText("14°F"); day2Low.setGravity(Gravity.CENTER_HORIZONTAL); ImageView day2Conditions = new ImageView(this); day2Conditions.setImageResource(R.drawable.pt_cloud); rowDayLabels.addView(day2Label); rowHighs.addView(day2High); rowLows.addView(day2Low); rowConditions.addView(day2Conditions); // day3 column TextView day3Label = new TextView(this); day3Label.setText("Feb 9"); day3Label.setTypeface(Typeface.SERIF, Typeface.BOLD); TextView day3High = new TextView(this); day3High.setText("23°F"); day3High.setGravity(Gravity.CENTER_HORIZONTAL); TextView day3Low = new TextView(this); day3Low.setText("3°F"); day3Low.setGravity(Gravity.CENTER_HORIZONTAL); ImageView day3Conditions = new ImageView(this); day3Conditions.setImageResource(R.drawable.snow); rowDayLabels.addView(day3Label); rowHighs.addView(day3High); rowLows.addView(day3Low); rowConditions.addView(day3Conditions); // day4 column TextView day4Label = new TextView(this); day4Label.setText("Feb 10"); day4Label.setTypeface(Typeface.SERIF, Typeface.BOLD); TextView day4High = new TextView(this); day4High.setText("17°F"); day4High.setGravity(Gravity.CENTER_HORIZONTAL); TextView day4Low = new TextView(this); day4Low.setText("5°F"); day4Low.setGravity(Gravity.CENTER_HORIZONTAL); ImageView day4Conditions = new ImageView(this); day4Conditions.setImageResource(R.drawable.lt_snow); rowDayLabels.addView(day4Label); rowHighs.addView(day4High); rowLows.addView(day4Low); rowConditions.addView(day4Conditions); // day5 column TextView day5Label = new TextView(this); day5Label.setText("Feb 11"); day5Label.setTypeface(Typeface.SERIF, Typeface.BOLD); TextView day5High = new TextView(this); day5High.setText("19°F"); day5High.setGravity(Gravity.CENTER_HORIZONTAL); TextView day5Low = new TextView(this); day5Low.setText("6°F"); day5Low.setGravity(Gravity.CENTER_HORIZONTAL); ImageView day5Conditions = new ImageView(this); day5Conditions.setImageResource(R.drawable.pt_sun); rowDayLabels.addView(day5Label); rowHighs.addView(day5High); rowLows.addView(day5Low); rowConditions.addView(day5Conditions); table.addView(rowTitle); table.addView(rowDayLabels); table.addView(rowHighs); table.addView(rowLows); table.addView(rowConditions); setContentView(table); }
我們來分析一下上面的源代碼。首先我們創建了一個TableLayout控件,并用setStretchAllColumns()和setShrinkAllColumns()方法將所有列的壓縮和伸展屬性值設置為true。接下來,我們又陸續創建了五個TableRow。每個TableRow都包含有視圖控件(標題,日期,顯示高低溫數據的TextView控件和顯示天氣狀況圖的ImageView控件)??梢钥吹降谝粋€TableRow中是如何處理列跨距的。每個TableRow用addView()方法按順序添加到TableLayout控件中。最后我們再加載TableLayout并用setContentView()方法將其顯示在界面上。
可以看到,當越來越多的控件要添加到界面上時,代碼量會很快地增長。為了易組織和可維護性,用程序定義并使用布局最好是用在特殊情況而不是一般情況。另外,在類似的情況下,數據通常是來自別的源而非我們自己編寫的字符串,因此循環可能對于許多程序來說更加合適。
下圖顯示的是運行結果。如你所預期的一樣,得到的結果與上圖是相同的。
表格布局要點
盡管可以用表格布局圖來設計整個用戶界面,但這通常不是最好的工具,因為表格式布局(TableLayout)繼承自線性布局(LinearLayout),并且而不是最有效率的布局控件。仔細推敲就可以發現,TableLayout無非是一系列LinearLayout的有序嵌套,而且從性能上考慮一般不鼓勵層次太深的嵌套。盡管如此,對于本來就是表格形式的數據而言(如電子表格數據),表格式布局可能是一個合理的選擇。
此外,表格布局數據會隨屏幕尺寸和分辨率大小而改變。當需要顯示大批量數據時就需要設置滾動條。例如,如果需要在上面的例子中加入天氣狀況的評析,那么文本就可能是一句話甚至二十句話,因此設置垂直或者水平的滾動條就是明智的選擇。
總結
Android程序使用布局來定義用戶界面,表格布局非常好地處理按行和列方式顯示的視圖數據和控件。合理使用表格布局可以使界面設計變得簡單快捷。盡管如此,還要清楚表格式布局繼承自線性布局,因而會有許多與線性布局一樣的性能方面的局限。[English]
轉載請注明:
作者:RockUX–WEB前端
出自:Android用戶界面設計:表格布局
- 目前還沒評論,等你發揮!