2.2 简单布局

本节开始介绍Android的基本视图和布局,首先说明基本视图View类的常用属性和方法,接着描述如何使用线性布局LinearLayout,最后介绍滚动视图ScrollView的用法。

2.2.1 视图View的基本属性

View是Android的基本视图,所有控件和布局都是由View类直接或间接派生而来的。故而View类的基本属性和方法是各控件和布局通用的,掌握好基本属性和方法,在哪里都能派上用场,能够举一反三、事半功倍。

下面是视图在XML布局文件中常用的属性定义说明。

● id:指定该视图的编号。

● layout_width:指定该视图的宽度。可以是具体的dp数值;可以是match_parent,表示与上级视图一样宽;也可以是wrap_content,表示与内部内容一样宽(内部内容若超过上级视图的宽度,则该视图保持与上级视图一样宽,超出宽度的内容得进行滚动才能显示出来)。

● layout_height:指定该视图的高度。取值说明同layout_width。

● layout_margin:指定该视图与周围视图之间的空白距离(包括上、下、左、右)。另有layout_marginTop、layout_marginBottom、layout_marginLeft、layout_marginRight分别表示单独指定视图与上边、下边、左边、右边视图的距离。

● minWidth:指定该视图的最小宽度。

● minHeight:指定该视图的最小高度。

● background:指定该视图的背景。背景可以是颜色,也可以是图片。

● layout_gravity:指定该视图与上级视图的对齐方式。对齐方式的取值说明见表2-2,若同时适用多种对齐方式,则可使用竖线“|”把多种对齐方式拼接起来。

表2-2 对齐方式的取值说明

● padding:指定该视图边缘与内部内容之间的空白距离。另有paddingTop、paddingBottom、paddingLeft、paddingRight分别表示指定视图边缘与内容上边、下边、左边、右边的距离。

● visibility:指定该视图的可视类型。可视类型的取值说明见表2-3。

表2-3 可视类型的取值说明

下面是视图在代码中常用的设置方法说明。

● setLayoutParams:设置该视图的布局参数。参数对象的构造函数可以设置视图的宽度和高度。其中,LayoutParams.MATCH_PARENT表示与上级视图一样宽,也可以是LayoutParams.WRAP_CONTENT,表示与内部内容一样宽;参数对象的setMargins方法可以设置该视图与周围视图之间的空白距离。

● setMinimumWidth:设置该视图的最小宽度。

● setMinimumHeight:设置该视图的最小高度。

● setBackgroundColor:设置该视图的背景颜色。

● setBackgroundDrawable:设置该视图的背景图片。

● setBackgroundResource:设置该视图的背景资源id。

● setPadding:设置该视图边缘与内部内容之间的空白距离。

● setVisibility:设置该视图的可视类型。取值说明见表2-3。

前面提到margin和padding两个概念,margin是指当前视图与周围视图的距离,padding是指当前视图与内部内容的距离。这么说可能有些抽象,所谓百闻不如一见,说得再多不如亲眼看看是怎么回事。我们来做一个实验,看看它们的显示效果有什么不同。下面是实验用的布局文件源代码,以背景色观察每个控件的区域范围:

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:background="#00aaff"
            android:orientation="vertical"
            android:padding="5dp" >


            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="20dp"
                android:background="#ffff99"
                android:padding="60dp" >


                <View
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="#ff0000" />
            </LinearLayout>
        </LinearLayout>

最后的界面效果如图2-5所示。布局文件处于中间层的LinearLayout,设置margin是20dp、padding是60dp。从效果图可以看到,中间层与上级视图之间的距离大约是中间层与下级视图之间距离的三分之一,正好是margin和padding两个数值的比例。如此便从实际情况中印证了:layout_margin指的是当前图层与外部图层的距离,而padding指的是当前图层与内部图层的距离。

图2-5 margin和padding的演示画面

视图组ViewGroup是一类特殊视图,所有布局视图类都是从它派生而来的。Android中的视图分为两类,一类是布局,另一类是控件。布局与控件的区别在于:布局本质上是个容器,里面还可以放其他视图(包括子布局和子控件);控件是一个单一的实体,已经是最后一级,下面不能再挂其他视图。打个比方,我们把根节点看作树干,根节点下的各级布局就是树枝,一根树枝可以连着其他小树枝,也可以直接连树叶;树叶只能依附在树枝上,不能再连树枝或其他树叶。

ViewGroup有3个方法,这3个方法也是所有布局类视图共同拥有的。

● addView:往布局中添加一个视图。

● removeView:从布局中删除指定视图。

● removeAllViews:删除该布局下的所有视图。

2.2.2 线性布局LinearLayout

LinearLayout是最常用的布局,名字叫线性布局。顾名思义,LinearLayout下面的子视图就像用一根线串了起来,所以LinearLayout内部视图的排列是有顺序的,要么从上到下依次垂直排列,要么从左到右依次水平排列。LinearLayout除了继承View/ViewGroup类的所有属性和方法外,还有其特有的XML属性,说明如下。

● orientation:指定线性布局的方向。horizontal表示水平布局,vertical表示垂直布局。如果不指定该属性,就默认是horizontal。这真是出乎意料,因为大家感觉手机App理应从上往下垂直布局,所以这里要特别注意垂直布局一定要设置orientation,不然默认的水平布局不符合多数业务场景。

● gravity:指定布局内部视图与本线性布局的对齐方式。取值说明同layout_gravity。

● layout_weight:指定当前视图的宽或高占上级线性布局的权重。这里要注意,layout_weight属性并非在当前LinearLayout节点中设置,而是在下级视图的节点中设置。另外,如果layout_weight指定的是当前视图在宽度上占的权重,layout_width就要同时设置为0dp;如果layout_weight指定的是当前视图在高度上占的权重,layout_height就要同时设置为0dp。

下面是LinearLayout在代码中增加的两个方法。

● setOrientation:设置线性布局的方向。LinearLayout.HORIZONTAL表示水平布局,LinearLayout.VERTICAL表示垂直布局。

● setGravity:设置布局内部视图与本线性布局的对齐方式。具体的取值说明见表2-2。

接下来重点解释layout_gravity和gravity的区别。前面说过,layout_gravity指定该视图与上级视图的对齐方式,而gravity指定布局内部视图与本布局的对齐方式。为方便理解,我们通过一个具体例子演示两种属性的显示效果。下面是演示用的XML布局文件,内部指定了多种对齐方式,其中左边视图的layout_gravity是bottom、gravity是left;右边视图的layout_gravity是top、gravity是right,布局文件内容如下:

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:background="#ffff99"
            android:orientation="horizontal"
            android:padding="5dp" >


            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="200dp"
                android:layout_weight="1"
                android:layout_gravity="bottom"
                android:gravity="left"
                android:background="#ff0000"
                android:layout_margin="10dp"
                android:padding="10dp"
                android:orientation="vertical">


                <View
                    android:layout_width="100dp"
                    android:layout_height="100dp"
                    android:background="#00ffff" />
            </LinearLayout>


            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="200dp"
                android:layout_weight="1"
                android:layout_gravity="top"
                android:gravity="right"
                android:background="#ff0000"
                android:layout_margin="10dp"
                android:padding="10dp"
                android:orientation="vertical">


                <View
                    android:layout_width="100dp"
                    android:layout_height="100dp"
                    android:background="#00ffff" />
            </LinearLayout>
        </LinearLayout>

运行后的界面效果如图2-6所示。从效果图可以看到,左边视图自身向下对齐,符合layout_gravity的设置,下级视图靠左对齐,符合gravity的设置;右边视图自身向上对齐,符合layout_gravity的设置,下级视图靠右对齐,符合gravity的设置。

图2-6 layout_gravity和gravity的演示界面

2.2.3 滚动视图ScrollView

手机屏幕的显示空间有限,常常需要上下滑动或左右滑动才能拉出其余页面内容,可惜Android的布局节点都不支持自行滚动,这时就要借助ScrollView滚动视图实现了。与线性布局类似,滚动视图也分为垂直方向和水平方向两类,其中垂直滚动的视图名是ScrollView,水平滚动的视图名是HorizontalScrollView。这两个滚动视图的使用并不复杂,主要注意以下3点:

(1)垂直方向滚动时,layout_width要设置为match_parent,layout_height要设置为wrap_content。

(2)水平方向滚动时,layout_width要设置为wrap_content,layout_height要设置为match_parent。

(3)滚动视图节点下面必须且只能挂着一个子布局节点,否则会在运行时报错Caused by:java.lang.IllegalStateException:ScrollView can host only one direct child。

下面是滚动视图ScrollView和水平滚动视图HorizontalScrollView的XML用法示例:

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">


            <HorizontalScrollView
                android:layout_width="wrap_content"
                android:layout_height="200dp">


                <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:orientation="horizontal">


                    <View
                        android:layout_width="400dp"
                        android:layout_height="match_parent"
                        android:background="#aaffff" />


                    <View
                        android:layout_width="400dp"
                        android:layout_height="match_parent"
                        android:background="#ffff00" />
                </LinearLayout>
            </HorizontalScrollView>


            <ScrollView
                android:layout_width="match_parent"
                android:layout_height="wrap_content">


                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">


                    <View
                        android:layout_width="match_parent"
                        android:layout_height="400dp"
                        android:background="#00ff00" />


                    <View
                        android:layout_width="match_parent"
                        android:layout_height="400dp"
                        android:background="#ffffaa" />
                </LinearLayout>
            </ScrollView>
        </LinearLayout>

有时ScrollView的实际内容不够,又想让它充满屏幕,怎么办呢?如果把layout_height属性赋值为match_parent,那么结果还是不会充满,正确的做法是再增加一行fillViewport的属性设置,举例如下:

                android:layout_height="match_parent"
                android:fillViewport="true"