本篇主要是在Hello World的基础上添加一个文本输入框和按钮,组成一个简单的UI界面。
View和ViewGroup
首先要说说下面这两个对象,我们的UI组件基本都是他们的后代:
View:View通常是UI控件,例如按钮、文本输入框、进度条等;
ViewGroup: ViewGroup是个看不见的容器,就是用来装View和其他ViewGroup的,同时也用来定义这些View/ViewGroup们如何来布局,例如网格型或者是垂直排列。
安卓提供了一套与View和ViewGroup子类对应的XML标签,你可以直接在布局XML文件里直接使用这些标签来定义UI。
图1:ViewGroup和View之间可能的关系 —— 来自亲妈谷歌
创建一个线性布局
线性布局(LinearLayout),官方API参考。
从官方文档我们可以看到,它继承(extend)的是ViewGroup,也就是说它是一个容器,可以用来盛装其他的ViewGroup子类或者是View子类。
线性布局会将在它里面的View和ViewGroup都朝一个方向对其,要么水平,要么竖直(默认是水平的),而且是按在代码里出现的先后顺序进行显示。
打开...\res\layout\路径下的acitvity_main.xml布局文件,正常情况下我们看到的应该是类似下图的预览界面,在这个界面下设计UI的话就是WYSIWYG(你看到的就是你得到的,左边控件拖拖拖~),选择左下角的Text进到文本模式;
之后看到的应该是这样的内容:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.wenhe.myfirstapp.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>
上面的内容其实是定义了一个RelativeLayout容器,并在里面放了个TextView文本框写着“Hello World!”。目前我们不关心上面这些code,统统删掉,换成下面的LinearLayout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
android:orientation 这个属性确定了在LinearLayout中的View和ViewGroup是按照竖直还是水平方式显示;
android: layout_width 这个属性修饰的事LinearLayout的宽度,由于这个LinearLayout是这个布局文件中的根View,所以它应该使用的属性值是“match_parent”,即扩展它的宽度直到与它的父View宽度相同,简单说“能占多宽就占多宽”;
android:layout_height 这个属性修饰的是LinearLayout的高度,原理同上,所以大小用的也是“match_parent”,简单说“能长多高长多高”。
到这里,一个简单的线性布局就做好啦~~
添加一个文本输入框
容器做好了,接下来要往里面放东西了,首先先放一个文本输入框EditText,添加好之后的布局文件是这样的:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText android:id="@+id/edit_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/edit_message" />
</LinearLayout>
从上面可以看到,其实就是添加了一个EditText标签以及一些修饰它的属性,一个一个来:
android:id 控件唯一的标识,在整个项目中应该都是唯一的,这样我们在代码中就可以找到这个控件并对它进行操作了;当我们在XML中要引用一个资源对象的时候一定要在最前面写上@符号,然后跟着资源的类型,如id,之后跟斜杠/,然后是资源的名字,如edit_message;对于这里的+符号,只有在我们第一次创建这个资源的时候才需要跟在id前面,之后当你编译app的时候,SDK工具就会帮你在gen/R.java中使用你定义的ID名为你的资源创建一个新的资源ID,在这之后你引用这个资源都不需要再加+符号了;
android:layout_width和android:layout_height 和布局属性中表示的含义相同,只是这里用的值时“wrap_content”,意思是说不非得占满屏,够用就好;
android:hint 当文本输入框是空的时候默认提示的一小段文字,你可以直接在后面的双引号里面写想要提示的话,也就是hard code在这里,但是强烈建议使用上面的引用资源的方法,这样更好维护。
添加字符串资源
当你把上面的代码复制到XML中之后,发现编译是会失败的,原因是不知道编译器根本不知道edit_message表示的是哪段话,所以要在res/valuse/strings.xml中添加edit_message表示的字符串:
<resources>
<string name="app_name">My First App</string>
<string name="edit_message">Enter a message</string>
</resources>
值得注意的是,文本输入框EditText的id和提示语hint的资源名称都是edit_message,但是不会提示错误,原因是他们的资源类型不同:一个是id,一个是string,所以实际使用的时候并不会冲突。
编译运行,效果如下:
添加一个按钮
和文本输入框同理,就是在最前面的LinearLayout里面添加一个Button标签(当然用到的string资源也要自己去strings.xml定义),添加好之后的布局文件activity_main.xml是这样的:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText android:id="@+id/edit_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/edit_message" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_send" />
</LinearLayout>
android:layout_width和android:layout_height属性与前面介绍的一样,略;
android:text 修饰按钮上显示什么文字,这里我们在strings.xml文件里定义了一个叫做button_send的字符串,表示的是“SEND”这几个字。
编译在真机上运行下吧,效果如下:
让文本输入框占满剩余屏幕宽度
总体看起来可以,但是一般情况下用户在文本输入框里面写下的可能是要更长的一段文字,那肿么办捏?答案就是在LinearLayout中用weight这个属性,来修饰不同子View的权重,写法上就是android:layout_weight。
权重是一个数值,表示的是每一个view相对于同级的其他兄弟view所占用的剩余空间的比例。这就像是做菜的菜谱,两份糖和一份盐,那么糖在这道菜里的比例就是2/3。
在举个例子,比如你给一个View A赋予的权重值是2,另外一View B是1,那么这个View A将会占据剩余空间的2/3,View B将会占据剩下的1/3;如果你再添加一个View C权重也是1的话,那么总和就是2+1+1=4,也就是说View A占1/2,View B和View C各占1/4。
默认所有View的权重都是0,如果你有一个View大于0,那么除了其他View本身需要占用的空间大小之外,这个View将会占据所有剩余的空间。
所以,为了将布局中剩余的空间都给文本输入框,我们需要进行以下操作:
1. 在EditText标签中添加android:layout_weight,值为1;
2. 将EditText标间中的android:layout_width属性值改为0dp;
值得注意的是,将width改为0dp有利于提高布局的效率,为什么?因为如果width定义为wrap_content的话那么会要求系统计算一个宽度值,而这个宽度值最终又不会被用到,因为权重值要求计算这里的EditText宽度要填满剩余空间。
修改之后,我们的布局xml最终是这样的:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText android:id="@+id/edit_message"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="@string/edit_message" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_send" />
</LinearLayout>
编译运行,效果如下: