视图中的布局

试用 Compose 方式
Jetpack Compose 是推荐在 Android 设备上使用的界面工具包。了解如何在 Compose 中使用布局。
<ph type="x-smartling-placeholder"></ph> 了解 Compose 布局基础知识 →

布局定义了应用中的界面结构,例如 一 activity。在 是使用层次结构 ViewViewGroup 对象的操作。View 通常会绘制用户可见的内容, 互动ViewGroup 是一个不可见的容器,用于定义 View 和其他 ViewGroup 的布局结构 对象,如图 1 所示。

图 1. 视图层次结构的图示,它定义了一个 界面布局。

View 对象通常称为微件,可以是 许多子类,如 ButtonTextView。通过 ViewGroup 对象通常称为布局,可以是 提供不同布局结构的多种类型,例如 LinearLayoutConstraintLayout

您可通过两种方式声明布局:

  • 在 XML 中声明界面元素。Android 提供了一个简单的 XML 对应的 View 类和子类的词汇表, 例如 widget 和布局。您还可以使用 Android Studio 的 布局编辑器:用于构建 XML 使用拖放界面来调整布局

  • 在运行时实例化布局元素。您的应用可以创建 ViewViewGroup 对象,并操纵其 以编程方式创建

通过在 XML 中声明界面,您可以将应用呈现方式与 控制其行为的代码使用 XML 文件还可让您更轻松地: 为不同的屏幕尺寸和方向提供不同的布局。这是 本专精课程 支持不同的屏幕 尺寸

Android 框架可让您灵活地使用 来构建应用界面例如,您可以声明应用的 默认布局,然后在运行时修改布局。

<ph type="x-smartling-placeholder">

编写 XML

利用 Android 的 XML 词汇,您可以快速设计界面布局和 它们包含的屏幕元素,方法与使用 HTML 创建网页相同 一系列嵌套元素组成。

每个布局文件都必须只包含一个根元素,该元素必须是 ViewViewGroup 对象。定义根 元素,您可以将其他布局对象或微件作为子元素添加到 逐步构建一个用于定义布局的 View 层次结构。对于 例如,这是一个使用垂直 LinearLayout 来 同时持有一个 TextView 和一个 Button

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_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>

在 XML 中声明布局后,使用 Android 项目的 res/layout/ 中的 .xml 扩展程序 以便其正确编译。

如需详细了解布局 XML 文件的语法,请参阅 布局资源

加载 XML 资源

当您编译应用时,系统会将每个 XML 布局文件编译成 View 资源。在应用的 Activity.onCreate() 回调实现。为此,请调用 setContentView(), 以如下形式向其传递对布局资源的引用: R.layout.layout_file_name。例如,如果您的 XML 保存为 main_layout.xmlActivity

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

Android 框架调用 onCreate() 回调方法, 在 Activity 启动时,您的 Activity 会同步。有关 有关 activity 生命周期的信息,请参阅 activity

属性

每个 ViewViewGroup 对象都支持自己的 各种 XML 属性某些属性特定于 View 对象。例如,TextView 支持 textSize 属性。不过,任何 View 也会继承这些属性。 用于扩展此类的对象。有些属性是所有View所共有的 对象,因为它们继承自 View 根类,例如 id 属性。其他属性则被视为版式 参数,这些是用于描述特定布局方向的属性 View 对象的父级元素,由该对象的父级定义 ViewGroup 对象。

ID

任何 View 对象都可以拥有与其关联的整数 ID, 在树中唯一标识 View。当应用 编译后,此 ID 以整数形式引用,但通常分配有 作为 id 属性中的字符串包含在布局 XML 文件中。这是一个 所有 View 对象通用的 XML 属性,由 View 类。它的使用非常频繁。内部 ID 的语法 XML 标记如下所示:

android:id="@+id/my_button"

字符串开头的 at 符号 (@) 表示 XML 解析器解析并展开 ID 字符串的其余部分,并将其标识为 ID 资源。加号 (+) 表示这是新的资源名称 您必须在R.java中创建并将其添加到资源中 文件。

Android 框架提供了许多其他 ID 资源。引用 Android 资源 ID,则无需添加加号,但必须添加 android 软件包命名空间,如下所示:

android:id="@android:id/empty"

android 软件包命名空间表明您要引用 来自 android.R 资源类(而非本地 资源类。

如需创建视图并从您的应用引用它们,您可以使用常用的 如下所示:

  1. 在布局文件中定义一个视图,并为其分配一个唯一 ID,如 示例:
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
    
  2. 创建 View 对象的实例并从布局中捕获该实例, 通常位于 onCreate() 方法,如以下示例所示:
    val myButton: Button = findViewById(R.id.my_button)
    
    Button myButton = (Button) findViewById(R.id.my_button);
    

在创建 RelativeLayout。 在相对布局中,同级视图可以定义其相对于另一个视图的布局 同级视图,由唯一 ID 引用。

ID 不必在整个结构树中保持唯一,但必须是唯一的 在您搜索的树状结构部分是唯一的。有时 因此,最好尽可能使其具有唯一性。

布局参数

定义名为 layout_something 的 XML 布局属性 View 的适合 ViewGroup

每个 ViewGroup 类都会实现一个嵌套类,该类扩展 ViewGroup.LayoutParams。 此子类包含的属性类型会定义每个子类的大小和位置。 子视图。如图 2 所示,父级 视图组为每个子视图定义布局参数,包括 视图群组。

图 2. 带布局的视图层次结构的可视化 与每个视图相关联的参数。

每个 LayoutParams 子类都有自己的设置语法 值。每个子元素都必须定义一个 LayoutParams,该 它的父项,不过它也可能会定义一个不同的 LayoutParams

所有视图组均使用 layout_width 包含宽度和高度 和 layout_height,并且需要每个视图来定义它们。很多 LayoutParams 包含可选的外边距和边框。

您可以指定具有确切尺寸的宽度和高度,但可能不会 经常执行这项操作更常见的情况是,您需要使用其中一个常量来设置 宽度或高度:

  • wrap_content:指示您的视图根据 内容所需的尺寸
  • match_parent:告知视图变得与其父级一样大 视图组允许。

一般情况下,我们不建议使用 绝对单位,例如像素。更好的方法是使用相对测量值, 例如密度无关像素单位 (dp)、wrap_contentmatch_parent,因为它有助于您的应用在 各种设备屏幕尺寸可接受的衡量类型请参见 布局资源

布局位置

视图具有矩形几何图形。它包含一个位置,表示为 坐标以及两个维度,以 宽度和高度。位置和尺寸的单位是像素。

您可以通过调用 getLeft()getTop()。 前者会返回矩形的左侧 (x) 坐标,代表 视图。后者会返回矩形的上 (y) 坐标 代表视图的视图。这些方法会返回视图相对于 其父级。例如,当 getLeft() 返回 20 时,则表示 视图位于 。

此外,您还可以使用一些便捷方法来避免不必要的计算: 也就是 getRight()getBottom()。 这些方法会返回 代表视图的矩形例如,调用 getRight() 是 类似于以下计算:getLeft() + getWidth()

尺寸、内边距和外边距

视图的大小由宽度和高度表示。一个视图有两对 宽度值和高度值的组合

第一对称为“测量宽度”测量高度。这些尺寸决定了视图所需的尺寸 父对象中的资源。您可以通过调用 getMeasuredWidth()getMeasuredHeight()

第二对称为宽度高度,有时 绘图宽度绘图高度。这些维度决定了 绘制时和布局后,视图在屏幕上的实际尺寸。这些 值可能(但不必)与测量宽度和测量高度不同。您 可以通过调用 getWidth()getHeight()

为了测量尺寸,视图需将其内边距考虑在内。内边距 以像素表示视图左侧、顶部、右侧和底部各部分的像素。 您可以使用内边距将视图内容偏移特定的 像素。例如,左侧内边距为 2,会将视图的内容推送两个像素 放在左边缘右侧您可以使用 setPadding(int, int, int, int) 方法调用该方法,并通过调用 getPaddingLeft(), getPaddingTop(), getPaddingRight(), 和 getPaddingBottom()

尽管视图可以定义内边距,但它不支持外边距。不过, 视图组确实支持外边距请参阅 ViewGroupViewGroup.MarginLayoutParams

有关维度的详细信息,请参阅 维度

除了以编程方式设置外边距和内边距之外,您还可以 ,如以下示例所示:

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
      <TextView android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="16dp"
                android:padding="8dp"
                android:text="Hello, I am a TextView" />
      <Button android:id="@+id/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="16dp"
              android:paddingBottom="4dp"
              android:paddingEnd="8dp"
              android:paddingStart="8dp"
              android:paddingTop="4dp"
              android:text="Hello, I am a Button" />
  </LinearLayout>
  

上面的示例展示了应用的外边距和内边距。通过 TextView 四周都应用了统一的外边距和内边距,以及 Button展示了如何将这些选项单独应用于不同的 边缘。

<ph type="x-smartling-placeholder">

常见布局

ViewGroup 类的每个子类都提供了一种独特的方法来 显示您嵌套在其中的视图。这是最灵活的布局类型, 是让布局层次结构保持浅层的最佳工具, ConstraintLayout

以下是 Android 中内置的一些常用布局类型 平台。

<ph type="x-smartling-placeholder">
创建线性布局

将其子项整理到单个水平行或垂直行中, 滚动条。

构建动态列表

如果布局的内容是动态内容或未预先确定的内容,您可以 使用 RecyclerView 或 是 AdapterViewRecyclerView 通常是更好的选择,因为它会使用内存 比 AdapterView 效率更高。

RecyclerViewAdapterView 包含以下内容:

列表

显示滚动的单列列表。

网格

显示滚动的行列网格。

RecyclerView提供了更多可能性 您可以 创建自定义 布局管理器

使用数据填充适配器视图

您可以填充 AdapterView 例如 ListViewGridView 由 将 AdapterView 实例绑定到 Adapter, 该函数会从外部来源检索数据,并创建 View, 来代表每个数据条目

Android 提供了一些实用的 Adapter 子类, 用于检索不同类型的数据并构建 AdapterView。两种最常见的适配器是:

ArrayAdapter
请在数据源为数组时使用此适配器。默认情况下 ArrayAdapter 通过调用以下方法来为每个数组项创建一个视图: toString() 并将内容放入 TextView 中。

例如,如果您想在 ListView,请使用ArrayAdapter 构造函数来指定每个字符串和字符串数组的布局:

    val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
    
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, myStringArray);
    

此构造函数的实参如下:

  • 您的应用 Context
  • 包含 TextView 的布局,对应于 数组
  • 字符串数组

然后调用 setAdapter() 在您的 ListView 上:

    val listView: ListView = findViewById(R.id.listview)
    listView.adapter = adapter
    
    ListView listView = (ListView) findViewById(R.id.listview);
    listView.setAdapter(adapter);
    

要自定义每个项的外观,您可以覆盖 toString() 方法。或者,创建 为每个项目分配一个视图 TextView - 例如,如果您需要 ImageView 扩展 ArrayAdapter 类和 覆盖 getView() 返回您想要为每个项使用的视图类型。

SimpleCursorAdapter
如果您的数据来自 Cursor。 使用 SimpleCursorAdapter 时,请指定要使用的布局 Cursor中的每一行以及 Cursor。 例如,如果您想创建用户姓名和电话号码列表 您可以执行一个返回 Cursor 的查询 包含每个人的行,以及姓名和号码的列。您 然后创建一个字符串数组,指定 每个结果的布局中所需的 Cursor 值和一个整数 数组,用于指定每列需要的相应视图 地点:

    val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                              ContactsContract.CommonDataKinds.Phone.NUMBER)
    val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
    
    String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
                            ContactsContract.CommonDataKinds.Phone.NUMBER};
    int[] toViews = {R.id.display_name, R.id.phone_number};
    

当您实例化 SimpleCursorAdapter 时,将 使用的 Cursor 布局,其中包含 和以下两个数组:

    val adapter = SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
    val listView = getListView()
    listView.adapter = adapter
    
    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
    ListView listView = getListView();
    listView.setAdapter(adapter);
    

然后,SimpleCursorAdapter 会为 使用提供的布局将 Cursor fromColumns 项复制到相应的 toViews 视图。

如果您在应用的生命周期中更改了 由适配器读取,在 notifyDataSetChanged()。 这会通知附加的视图数据已更改,并且它会刷新 本身。

处理点击事件

您可以在 AdapterView 中响应每件商品的点击事件 来实施 AdapterView.OnItemClickListener 界面。例如:

listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // Do something in response to the click.
}
// Create a message handling object as an anonymous class.
private OnItemClickListener messageClickedHandler = new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View v, int position, long id) {
        // Do something in response to the click.
    }
};

listView.setOnItemClickListener(messageClickedHandler);

其他资源

请参阅 向日葵 演示版应用