Android Layouts

本文详细介绍了Android应用中用户界面的构建与管理过程,包括View和ViewGroup的基本概念、XML布局文件的使用方法、属性定义及加载流程,以及如何通过AdapterView实现动态布局填充。重点阐述了布局参数、位置、尺寸、填充和边距的概念,并提供了多种常见的ViewGroup类型及其应用。同时,介绍了如何使用AdapterView和Adapter进行数据驱动的界面展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

Android中所有的用户界面都是由View和ViewGroup对象构成的. View是可以显示在屏幕上的一些控件, 它们可以跟用户交互. ViewGroup则是”包含”其它View和ViewGroup的容器, 用于定义界面的布局.

Android提供了很多现成的View(比如Button和TextView等)和ViewGroup(LinearLayout和RelativeLayout等)可以使用.

View和ViewGroup之间的协作如下图:


每个ViewGroup都是不可见的容器, 它可以包含其它的ViewGroup和View. View则用来处理用户的输入输出,显示等, 负责与用户交互. 上图中的tree可以很复杂也可以很简单, 但是建议不要太复杂, 否则在绘制的时候会影响性能.

为了构建这样的一棵tree, 我们可以在代码里实例化View来实现, 但是最简单和常用的方法还是在XML文件中构建, XML提供了易读的标签给我们, 更加便于实现这样的结构, 就好像HTML那样. 每个XML中的标签都对应一个Android类, 比如<TextView>对应一个TextView类, <LinearLayout>对应一个LinearLayout的ViewGroup. Layout为用户界面定义了虚拟的结构, 比如activity和widget的UI. 我们有两种方法可以定义一个layout:

1.      在XMl文件中声明一个layout. Android为View类和其子类提供了一个简单的关键字对照表. 这样在XML文件中就可以直接声明一个View的类或者子类.

2.      在运行时实例化一个layout. APP可以通过编码在运行时实例化View或者ViewGroup.

两种方法可以灵活交叉运用. 比如可以在XML文件中定义默认的layout, 然后可以在代码中增加或者修改它们. 这样做的好处有一大把, 比如将UI的定义和操作分开来, 以达到降低耦合的目的. 还可以在不需要重新编译的情况下使用新的XML文件支持不同的屏幕尺寸, 配置等.


构建一个XML:

使用Android XML的关键词, 可以很快的构建出一个UI layout和它包含的元素. 这一点跟HTML有些类似. 每个layout文件必须包含一个根标签, 它必须是一个View或者ViewGroup的对象. 有了根标签之后就可以在其内部添加其它的layout标签或者widget标签作为子项. 比如下面这段代码是一个包含TextView和Button的垂直LinearLayout:

<?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, Iam a TextView"/>
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, Iam a Button"/>
</LinearLayout>

该文件必须以.xml为扩展名, 并且保存在/res/layout下, 才能被系统正常识别并且编译.


加载一个XML文件资源:

当我们编译工程的时候, 每个XML layout文件都会被编译成一个view资源. 我们应该在Activity.onCreate()方法中调用setContentView()方法来加载一个layout资源文件. 假如文件名是main_layout.xml, 那么加载该xml文件的代码如下:

public void onCreate(BundlesavedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

onCreate由Android framework在activity加载的时候调用.


属性:

每个View和ViewGroup都支持自己的XML属性. 有些是指定给特定属性的(比如TextView的textSize属性), 有些属性则可以应用于所有的View对象(比如id属性).

ID:

任何一个View对象都可以包含一个整型的ID与其关联, 以便于识别该对象. 这是一个极为常用的属性, 下面是它的语法:

android:id="@+id/my_button"

“@”表示XML解析器应该展开这个ID字符串的剩余部分, 作为一个ID资源处理它. “+”表示这是一个新的资源名称,必须新建并且添加到资源文件(R.java)中. Android framework提供了一些ID资源, 使用这些ID资源的时候, 不需要使用”+”, 但是需要指定android的命名空间, 比如:

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

当使用android的命名空间的时候, 我们需要使用android.R资源类而不是本地的资源类.

我们通常这样让xml中的view与APP中的代码关联起来:

1.      在layout文件中定义一个view或者widget, 为其指定一个ID:

<Button android:id="@+id/my_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/my_button_text"/>

2.      在代码中创建实例, 并且将其与layout文件中的对象关联起来(通常在onCreate()方法中):

Button myButton = (Button) findViewById(R.id.my_button);

在RelativeLayout中定义ID十分重要, 因为同级的view之间可以通过ID指定它们相对的布局关系. 


Layout参数:

XML layout使用layout_name格式的属性为layout内部的View指定参数. 每个ViewGroup都实现了一个ViewGroup.LayoutParams.这个类包含一些定义子View的尺寸和位置的属性. 如下图, 每个ViewGroup为其子View定义了layout参数:


注意图中的每个LayoutParams都有其自己的语法, 所以子View中LayoutParam的语法是由其父ViewGroup决定的.

所有的ViewGroup都包含宽和高属性(layout_width和layout_height), 并且每个view都需要定义它们. 很多LayoutParams都指定了自己的边距和边界格式. 宽高可以通过精确的数值指定, 也可以通过相对常量指定, 比如:

warp_content指定view的尺寸由它的内容决定.

match_parent(API8之前叫fill_parent)指定其尺寸跟父ViewGroup一样.

通常我们不推荐使用绝对的尺寸单位比如像素, 推荐使用密度无关的像素单位(dp), 或者warp_content或者match_parent, 这些值会让我们的APP在不同尺寸的屏幕下具有更好的兼容性.


Layout位置:

View的几何形状是矩形, Android通过指定左上角的坐标和宽高来确定一个View的位置. 坐标和宽高的单位是像素. 可以通过调用getLeft()和getTop()方法来获取到View的X, Y坐标. getRight()相当于getLeft()+getWidth(), getBottom()相当于getTop()+getHeight().


尺寸, 填充和边距:

View的尺寸由宽高决定. 事实上View有两对宽高.

第一对是测量宽高: 这对宽高指定View想要在父容器里所占的尺寸大小. 可以通过getMeasuredWidth()和getMeasuredHeight()获取.

第二对是前文提到的普通的width和height, 即实际尺寸, 可以跟测量宽高不同. 通过getWidth()和getHeight()方法获得.

测试尺寸的时候还应该考虑填充的问题, 可以使用setPadding(int, int, int, int)方法来设置填充. 比如左填充2pixel表示将view的内容从左边缘向右推2个像素. 查询填充尺寸则可以使用getPaddingLeft(),getPaddingTop(), getPaddingRight()和getPaddingBottom()这些方法.

View可以定义自己的填充值, 但是不能定义边距, 边距是由ViewGroup定义的.


普通Layout:

每个ViewGroup的子类都提供了子View不同的排列规则, 下面是几个常见的ViewGroup类型:

Linear layout:


线性布局, 将其内部的子模块按照水平或者垂直方向排列, 如果超出了屏幕尺寸, 就会产生一个滚动条.

Relative layout:


相对布局, 可以指定子模块相对于其它子模块或者父模块的位置(比如A在B的下面, 或者与父模块顶部对其等).

Web View:


显示一个网页.

嵌套ViewGroup可以实现更加复杂的布局, 但是也会加重绘制的负担, 所以应该尽量少嵌套ViewGroup.


通过Adapter构建一个layout:

当我们的布局需要在运行时确定, 可以使用ViewGroup的子类AdapterView来解决这一问题. AdapterView使用Adapter来绑定数据与layout, Adapter相当于数据与layout之间的搬运工, Adapter检索数据(通常是从Array或者数据库中获取数据)并转换为AdapterView认识的条目添加到AdapterView中. 常见的支持Adapter的布局有:

ListView:


显示一个可以滚动的单列列表.

GridView:


显示一个可以滚动的多列列表.

 

使用数据填充一个Adapter:

我们可以通过绑定AdapterView和Adapter为一个AdapterView(比如ListView和GridView)填充数据, Adapter从外部数据源检索数据, 然后创建一个View, 填充数据到View中, 再把View和AdapterView关联起来.

根据数据源的不同, Android提供了两种常用的Adapter:

1.      ArrayAdapter:

当数据源是一个Array的时候, 可以使用这种ArrayAdapter, 默认情况下ArrayAdapter通过Array中的每条数据的toString()方法获取字符串, 然后将其放入一个TextView中. 比如如果想在一个ListView中显示一个字符串Array中的内容, 那么可以像如下这样通过构造方法创建一个ArrayAdapter:

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

其中的三个参数分别是:APP的Context, 为Array的每条数据指定一个TextView的layout, 字符串Array.

然后在ListView中调用setAdapter():

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

如果想要自定义每个Item的样子, 可以通过重写Array中的对象的toString()方法. 或者为ListView的每个Item创建View, 典型的就是我们的聊天程序, 我们经常需要在Text旁边放上一个图像代表头像. 只需要扩展ArrayAdapter类, 重写它的getView()方法, 返回想要的View即可.

2.      SimpleCursorAdapter:

当我们的数据来源是一个Cursor的时候, 我们可以使用SimpleCursorAdapter. 在使用SimpleCursorAdapter的时候需要为Cursor中的每一行指定对应的layout中的view. 比如如果我们想要创建一个电话簿的list, 那么我们就需要人名和对应的号码, 我们可以提供一个返回Cursor的查询, 然后创建一个string数组, 用来保存想要显示的字段, 还有一个整型数组, 用来保存要显示的View的ID:

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

然后实例化一个SimpleCursorAdapter, 并传入layout, cursor和两个数组:

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

SimpleCursorAdapter会用传入的layout为cursor中的每行创建一个view,然后将fromColumns中的每个元素与toViews中的view对应.

如果在运行期间更改了Adapter的数据, 那么需要调用notifyDataSetChanged()来告知Adapter, 这样它才会更新自己的界面.


处理点击事件:

我们可以通过实现AdapterView.OnItemClickListener()方法来监听每一个AdapterView上的点击事件:

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

listView.setOnItemClickListener(mMessageClickedHandler);

 

 

参考: http://developer.android.com/guide/topics/ui/declaring-layout.html



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值