本节介绍视图的几个基本概念及其用法,包括:如何设置视图的宽度和高度,如何设置视图的外部间距和内部间距,如何设置视图的外部对齐方式和内部对齐方式,等等。
1.2.1 设置视图的宽和高
手机屏幕是块长方形区域,较短的那条边叫做宽,较长的那条边叫做高。App控件通常也是长方形形状,控件宽度通过属性android:layout_width表达,控件高度通过属性android:layout_height表达,宽和高的取值主要有下列3种:
(1)match_parent:表示与上级视图保持一致。上级视图的尺寸有多大,当前视图的尺寸就有多大。
(2)wrap_content:表示与内容自适应。对于文本视图来说,内部文字需要多大的显示空间,当前视图就要占据多大的尺寸。但最宽不能超过上级视图的宽度,一旦超过就要换行;最高不能超过上级视图的高度,一旦超过就会被隐藏。
(3)以dp为单位的具体尺寸,比如300dp,表示宽度或高度就是这么大。
在XML文件中采用以上任一方式均可设置视图的宽和高,但在Java代码中设置宽和高就有点复杂了,首先确保XML中的宽、高属性值为wrap_content,这样才允许在代码中修改宽和高。接着打开该页面对应的Java代码,依序执行以下3个步骤:
01 调用控件对象的getLayoutParams方法来获取布局参数,参数类型为ViewGroup.LayoutParams。
02 布局参数的width属性表示宽度,height属性表示高度,修改这两个属性值,即可调整控件的宽和高。
03 调用控件对象的setLayoutParams方法,填入修改后的布局参数使之生效。
不过布局参数的width和height两个数值默认是px单位,需要将dp单位的数值转换为px单位的数值,然后才能赋值给width属性和height属性。下面是把dp大小转为px大小的方法代码:
public class Utils {
//根据手机的分辨率从dp的单位转成为px(像素)
public static int dip2px(Context context,float dpValue){
//获取当前手机的像素密度(1个dp对应几个px)
float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue*scale+0.5f); //四舍五入取整
}
}
有了上面定义的公共方法dip2px,就能将某个dp数值转换成px数值,比如准备把文本视图的宽度改为300dp,那么调整宽度的Java代码示例如下:
<TextView
android:id="@+id/tv_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00ffff"
android:text="通过代码指定视图宽度"
android:textColor="#000000"
android:textSize="17sp" />
//获取名为tv_code的文本视图
TextView tv_code = findViewById(R.id.tv_code);
//获取tv_code的布局参数(含宽度和高度)
ViewGroup.LayoutParams params = tv_code.getLayoutParams();
//修改布局参数中的宽度数值,注意默认是px单位,需要把dp数值转成px数值
params.width = Utils.dip2px(this,300);
tv_code.setLayoutParams(params); //设置tv_code的布局参数
接下来通过演示页面并观察几种尺寸设置方式的界面效果,主要通过背景色区分当前视图的宽高范围,详细的XML文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ViewBorderActivity">
<TextView
android:id="@+id/tv_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00ffff"
android:text="通过代码指定视图宽度"
android:layout_marginTop="5dp"
android:textColor="#000000"
android:textSize="17sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:background="#00ffff"
android:text="视图宽度采用wrap_content定义"
android:textColor="#000000"
android:textSize="17sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:background="#00ffff"
android:text="视图宽度采用match_parent定义"
android:textColor="#000000"
android:textSize="17sp" />
<TextView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:background="#00ffff"
android:text="视图宽度采用固定大小"
android:textColor="#000000"
android:textSize="17sp" />
</LinearLayout>
运行测试App,打开的演示界面如图所示,依据背景色判断文本视图的边界,可见wrap_content方式刚好包住了文本内容,match_parent方式扩展到了与屏幕等宽,而300dp的宽度介于前两者之间(安卓手机的屏幕宽度基本为360dp)。
1.2.2 设置视图的间距
在上一小节末尾的XML文件中,每个TextView标签都携带新的属性android:layout_marginTop="5dp",该属性的作用是让当前视图与上方间隔一段距离。同理,android:layout_marginLeft让当前视图与左边间隔一段距离,android:layout_marginRight让当前视图与右边间隔一段距离,android:layout_marginBottom让当前视图与下方间隔一段距离。如果上下左右都间隔同样的距离,还能使用android:layout_margin一次性设置四周的间距。
layout_margin不单单用于文本视图,还可用于所有视图,包括各类布局和各类控件。因为不管布局还是控件,它们统统由视图基类View派生而来,而layout_margin正是View的一个通用属性,所以View的子子孙孙都能使用layout_margin。在View的大家族中,视图组ViewGroup尤为特殊它既是View的子类,又是各类布局的基类。布局下面能容纳其他视图,而控件却不行,这正源自ViewGroup的组装特性。View、ViewGroup、控件、布局四者的继承关系如图所示。
除了layout_margin之外,padding也是View的一个通用属性,它用来设置视图的内部间距,并且padding也提供了paddingTop、paddingBottom、paddingLeft、paddingRight四个方向的距离属性。同样是设置间距,layout_margin指的是当前视图与外部视图(包括上级视图和平级视图)之间的距离,而padding指的是当前视图与内部视图(包括下级视图和内部文本)之间的距离。为了观察外部间距和内部间距的差异,接下来做个实验,看看layout_margin与padding究竟有什么区别。
首先创建新的活动页面,并给该页面的XML文件填入以下的布局内容:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:orientation="vertical"
android:background="#00aaff"
android:layout_width="match_parent"
android:layout_height="300dp"
tools:context=".ViewMarginActivity">
<!--中间层的布局背景为黄色-->
<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>
上面的XML文件有两层视图嵌套,第一层是蓝色背景布局里面放黄色背景布局,第二层是黄色背景布局里面放红色背景视图。中间层的黄色背景布局,同时设置了20dp的layout_margin,以及60dp的padding,其中padding是layout_margin的三倍宽(60/20=3)。接着运行测试App,看到的演示界面如图所示。
从效果图可见,外面一圈间隔较窄,里面一圈间隔较宽,表示20dp的layout_margin位于外圈,而60dp的padding位于内圈。这种情况印证了:layout_margin指的是当前图层与外部图层的距离,而padding指的是当前图层与内部图层的距离。
1.2.3 设置视图的对齐方式
App界面上的视图排列,默认靠左朝上对齐,这也符合日常的书写格式。然而页面的排版不是一成不变的,有时出于美观或者其他原因,要将视图排列改为朝下或靠右对齐,为此需要另外指定视图的对齐方式。在XML文件中通过属性android:layout_gravity可以指定当前视图的对齐方向,当属性值为top时表示视图朝上对齐,为bottom时表示视图朝下对齐,为left时表示视图靠左对齐,为right时表示视图靠右对齐。如果希望视图既朝上又靠左,则用竖线连接top与left,此时属性标记为android:layout_gravity="top|left";如果希望视图既朝下又靠右,则用竖线连接bottom与right,此时属性标记为android:layout_gravity="bottom|right"。
注意layout_gravity规定的对齐方式,指的是当前视图往上级视图的哪个方向对齐,并非当前视图的内部对齐。若想设置内部视图的对齐方向,则需由当前视图的属性android:gravity指定,该属性一样拥有top、bottom、left、right 4种取值及其组合。它与layout_gravity的不同之处在于:layout_gravity设定了当前视图相对于上级视图的对齐方式,而gravity设定了下级视图相对于当前视图的对齐方式;前者决定了当前视图的位置,而后者决定了下级视图的位置。
为了进一步分辨layout_gravity与gravity的区别,接下来做个实验,对某个布局视图同时设置android:layout_gravity和android:gravity属性,再观察内外视图的对齐情况。下面便是实验用的XML文件例子:
<?xml version="1.0" encoding="utf-8"?>
<!--最外层的布局背景为橙色,它的下级视图在水平方向排列-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:background="#ffff99"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="300dp"
tools:context=".ViewGravityActivity">
<!--第一个子布局背景为红色,它在上级视图中朝下对齐,它的下级视图则靠左对齐-->
<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">
<!--内部视图的宽度和高度都是100dp,且背景色为青色-->
<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:background="#ff0000"
android:layout_gravity="top"
android:gravity="right"
android:layout_margin="10dp"
android:padding="10dp">
<!--内部视图的宽度和高度都是100dp,且背景色为青色-->
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#00ffff" />
</LinearLayout>
</LinearLayout>
运行测试App,打开演示界面如图所示。
由效果图可见,第一个子布局朝下,并且它的内部视图靠左;而第二个子布局朝上,并且它的内部视图靠右。对比XML文件中的layout_gravity和gravity取值,证明了二者的对齐情况正如之前所言:layout_gravity决定当前视图位于上级视图的哪个方位,而gravity决定了下级视图位于当前视图的哪个方位。