IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

Android用户界面设计:表格布局

RockUX | WEB、前端、JavaScript、PHP 2011-04-28 00:00:48 累计浏览 6,189 次
本机暂存

   表格布局图可以用来显示表格式数据或者像网页上的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中如下定义:

       回忆一下,在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]

同分类推荐文章

  1. 「置顶」我做了什么 (2026-05-05 12:13:28)
  2. 万字长文推演:手机不再从 App 开始,Agent OS 如何接管任务入口 (2026-04-28 14:57:22)
  3. Android Perfetto 系列 10 - Binder 调度与锁竞争 (2025-11-16 15:33:30)

查看更多 移动开发 文章 →

建议继续学习

  1. 十个最容易犯的用户体验错误及规避方案 (累计阅读 79,500)
  2. 情绪版(Mood board)操作流程的新思考 (累计阅读 41,756)
  3. android 开发入门 (累计阅读 19,531)
  4. Android 连接SSID隐藏网络以及 LEAP 认证的方法 (累计阅读 9,540)
  5. 让安卓手机通过代理翻墙的方法 (累计阅读 9,117)
  6. 手机产品设计方向 (累计阅读 7,954)
  7. 实时监控Android设备网络封包 (累计阅读 6,558)
  8. Eclipse开发Android应用程序入门:重装上阵 (累计阅读 6,463)
  9. 基于 PhoneGap 与 Java 开发的 Android 应用的性能对比 (累计阅读 6,411)
  10. Windows下使用VMware安装Android (累计阅读 5,633)