Android UI
Android UI的重要概念
Android UI 的最重要的三个概念,Activity、View、Layout。
下面对这三个概念作一个初步的理解,先浅尝辄止。这样做的目的是先了解一下这些概念,从总体上对这些概念有一个把握和串通,方便后面的学习。
(一)Activity
一个应用程序至少有一个Activity。且必须有一个默认的启动Activity。
Android应用程序启动的流程如下:
首先,Android操作系统会去访问你的应用程序中的AndroidManifest.xml这个文件,决定启动哪一个Activity。启动默认的Activity之后,会生成这个Activity的对象,一般情况下是MainActivity。生成这个对象后,会去调用这个对象的onCreat方法。然后在onCreate方法里去读取layout目录下的activity_main.xml布局文件,来决定在应用程序里显示什么内容。
我们可以在布局文件里修改文字显示的样式,比如
1 android:textSize="80px" 2 android:background="#FF0000"
所有在res里的文件都会在gen目录里R.java这个文件里生成ID,布局文件也有它的ID。
1 public static final class layout { 2 public static final int activity_main=0x7f030000; 3 }
R.layout.activity_main就代表这个布局文件
然后在activity的onCreate函数中
1 protected void onCreate(Bundle savedInstanceState) { 2 super.onCreate(savedInstanceState); 3 setContentView(R.layout.activity_main); 4 }
setContentView就是显示布局文件的内容。
为了满足动态显示的需要,写死在布局文件里的控件显然是不行的。
因此我们用代码取代布局,如图所示。这样就可以动态的控制控件的样式。
下面这行就是获取代表控件的对象:
1 TextView textView = (TextView)findViewById(R.id.textView);
这涉及到View对象,下面就开始介绍View。
(二)View
1.什么是View
View就是控件。如下图:
每一种控件都对应有一个类,View是所有控件的父类。
首先,我们需要在layout文件里给控件加上一个唯一的ID:
1 android:id="@+id/textView"
然后,在Activity里声明这个控件:
1 private TextView textView;
注:这里需要引用textView所在的包
在Activity的函数里用findViewById获取这个View,由于findViewById函数的返回类型是View,所以需要向下转型为TextView:
注:这里不是生成对象,只是找到这个对象,对象的生成是由布局文件完成的。
1 textView = (TextView)findViewById(R.id.textView);
3.设置View的属性
转型完毕以后可以在Activity中对这个控件进行修改,控制它的属性。例如:
1 textView.setText(“hello world”); 2 textView.setBackgroundColor(Color.BLUE);
在布局里能做到的事情绝大部分代码里也能做到。
4.为View设置监听器
监听器也是一种对象,它监控着控件对象状态的变化。当控件遇到了某种事件,被点击了,被滑动了等等,控件会通知监听器,监听器得到通知之后就执行一些操作。
控件与监听器之间是绑定关系。
一个控件可以绑定多个监听器,不同的监听器用来响应不同的事件。比如监听器一用来响应点击事件,监听器二用来响应长点击事件……
我们以做一个计数器的应用为例,设计一个按键和一个文本框,每点击一下按钮,文本框里的数字加1。用这个例子来说明使用监听器的步骤:
1)获取代表控件的对象
首先,我们在布局文件里加一个Button和TextView,TextView初始值为0。
1 <TextView 2 android:id="@+id/textView" 3 android:layout_width="wrap_content" 4 android:layout_height="wrap_content" 5 android:textSize="40sp" 6 android:background="#FF0000" 7 android:text="0" /> 8 <Button 9 android:id="@+id/button" 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:text="Click Me" 13 />
再到Activity里引入包,声明它
并获取它
2)定义一个类,实现监听器接口
我们在activity里定义一个监听器,要使用一个内部类,并实现监听器接口OnClickListener
1 class ButtonListener implements OnClickListener{ }
要注意的是,在导入包(CTRL+SHIFT+O)的时候,会弹出一个对话框让你选择引入哪个包,我们选择View.OnClickListener。
Finish之后,我们要实现OnClickListener这个接口的抽象方法,在前面打叉的灯泡上点击会弹出你需要的操作:
直接点击Add unimplemented methods,Eclipse帮你完成抽象方法的复制,这个抽象方法就是onClick:
1 @Override 2 public void onClick(View v) { 3 // TODO Auto-generated method stub 4 5 }
这个方法的作用是,当我们把ButtonListener监听器绑定到Button控件上后,当Button被点击时,会执行这个onClick方法。
声明一个全局变量
接着我们完成onClick函数
1 @Override 2 public void onClick(View v) { 3 count++; 4 textView.setText(count+""); 5 }
至此,我们的监听器就写好了。
3)生成监听器对象
在onCreate函数里生成监听器对象:
1 ButtonListener buttonListener = new ButtonListener();
4)为控件绑定监听器对象
然后绑定到button上
完成。
在Android里监听器种类非常多,而且不同控件监听的方法也不一样,但使用监听器的流程永远是这个流程。
(三)控件布局
所谓的控件布局方法,就是指控制控件在Activity当中的位置、大小、颜色以及其他控件样式属性的方法。
控件布局关系到不仅仅是好看的问题,更重要的是用户的使用体验。
可以在布局文件中完成布局,也可以在JAVA代码中完成控件布局。
第一种布局方式是LAYOUT布局方式:
Linear Layout
线性布局,或者横,或者竖,一个个的把控件按顺序摆上去。
Relative Layout
相对布局,通过控件之间的相对位置决定控件的摆放方式。比如先摆好第一个控件,然后指定第二个控件摆放在第一个控件的下方,再指定第二个控件与第一个控件靠左对齐。有点类似DIV+CSS。
第二种布局方式是VIEW布局方式,主要是ADAPTER VIEW:
ListView
定义一个列表,每一项有自己的内容。这个布局是极其常用的布局。
Grid View
即网格布局,把屏幕分成几等分的格子。
Eclipse默认生成项目的布局文件是Relative Layout。我们来尝试下自己生成一个布局文件:
在layout文件夹上右键点new->Android XML file,弹出对话框如图:
选择根元素是LinearLayout,Finish。
生成代码如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 </LinearLayout>
android:layout_width="match_parent"表示宽度匹配父控件,也就是和activity宽度一样。
android:orientation="vertical"表示方向垂直,因为LinearLayout还可以水平方向的排放控件,即将vertical改为horizontal
我们来加两个控件:
1 <TextView 2 android:layout_width="match_parent" 3 android:layout_height="wrap_content" 4 android:background="#FF0000" 5 android:text="老大"/> 6 7 <TextView 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:background="#00FF00" 11 android:text="老二"/>
android:layout_height="wrap_content"表示高度自适应内容。
然后把activity里设置为显示我们自己写的这个布局:
显示结果如下:
如果我们现在什么都不改,只改LinearLayout的方向,由垂直改为水平,会出现什么情况呢?按理应该是在同一行显示,但结果如下:
老二被老大挤跑了,这是因为我们第一个控件的宽度是指定的匹配父控件,把一行全占满了,老二被挤出了屏幕,所以看不到了。
如果我们把老大的宽度改成wrap_content,也就是内容自适应,老二就出现了。
开发过程中,相对布局其实用得更多一些。这里是为了初步了解。
1)距离单位之px
px即像素,如800*480的分辨率,是把屏幕分成横向480个列,纵向800个行,第一个小格子就是一个像素。
但像素不是我们最好的选择,原因下面解释。
我们知道,市面上有各种各样的手机,不同尺寸与不同分辨率都可能搭配在一台手机上。如果我们用像素作单位,可以会出现下面这一种麻烦:
比如,我们设定了一个文本框,我们想让它宽度占到屏幕宽度的一半,在一个320*480像素的手机上,宽度的一半自然是160px,于是我们给这个文本框宽度为160px。这个程序在320*480像素的手机上是正常的,但如果运行在一个640*960分辨率的手机上,就会在视觉上明显变窄,如下图所示:
这就是像素作为长度单位的缺陷。
怎么解决这个问题?我们必须用相对长度取代像素这种绝对长度。这个长度单位就是dp。
2)距离单位之dp
在解释dp之前,必须要先解释一个概念dpi。
前面解释过像素实际上是指点的数量,而dpi(dots per inch),是指每英寸点的数量。
同样尺寸的屏幕,分辨率可能会不同,也就是像素排列的密集程度不同。因此dpi表示的是屏幕的细腻程度。
如下图:
在dpi的概念下,不同手机有了标准化的衡量指标。
由此,引申出一个新的长度单位dp。dp=dip(Device Independent pixels)
dp是一个相对的长度概念,它不代表实际长度,只代表实际长度,即像素基于dpi的系数。也就是说,不同dpi的手机屏幕上,1dp表示的像素长度是不同的。具体怎么不同,看dp换算成px的公式:
px=dp*(dpi/160)
1dp代表多少px呢?答案是不固定的,取决于手机的dpi是多少。
在dpi为160的屏幕上,也就是每英寸160像素的屏幕上,1dp代表1px。这是Android定义的基准值。
那么按照公式,一个20dp宽度的控件,在dpi160的屏幕上,显示为20px。同样的程序,换到dpi320的屏幕上运行,该控件的宽度就会显示为40px。
3)距离单位之sp
sp没有特殊的意思,它是为了适应手机用户对系统字体大小的设置。
如果用户将手机字体设为大,sp的值就会变大,反之亦然。
我们可以得出这样一个推论:
在设置控件的高度和宽度时,一般用dp,而在设置字体大小的时候,一般用sp。
4)控件的外边距与内边距
如图:
外边距与内边距之间就是控件的边界。所以外边距其实就是margin,内边距其实就是padding。
内外边距各有上、下、左、右四个属性: