第1节 布局与控件的常用概念
界面设计中的控件,就是我们常常看到的按钮
滑动条
文字显示区
等等,它们就像房间里的家具,是界面设计的最小单位。
布局是一个可以容纳别的布局(或者控件)的容器。它就像是一个大的房间,房间里面可以放各种家具(控件),也可以再隔离成更多的房间(放入别的布局)。
不过,两者有很多共同的地方,例如定义它们的大小、边距等等。
1.1 尺寸单位
在使用布局或控件时,有时需要指定它们的尺寸。安卓系统提供了三种单位:
-
px
:以像素为单位进行设置,屏幕上每一个点,就是一个像素,例如一部安卓手机,屏幕像素是1920 x 1080
,就是说屏幕高度有1920
个像素点,宽度有1080
个像素点,但是在安卓系统中,最好不要使用这个单位来设置尺寸,而是用dp
; -
sp
:用于字体大小的设置,它可以让字体大小根据用户在设置
中的设置,进行缩放; -
dp
:密度无关像素,这是在布局和控件中应该使用的单位;
1.1.1 dp的引入
如果用px为单位设定设备区域的大小,通常会有很大的问题。
假设有两个物理尺寸都为5寸大小的屏幕,它们的屏幕分辨率不同,一个是1080*1920,而另一个却是450*800.
如果指定一个按钮的大小为200px长,200px宽,那么它们的显示效果就如下图。很明显,两者的差别也太大了,界面就严重变形了。所以,想让一个区域的大小在不同屏幕的上显示的都差不多,就需要考虑像素密度。为了显示同样的长度,在密度大的屏幕上,使用到的像素就要多一些,在密度小的屏幕上,使用到的像素就要少一些。
所以为了解决这个问题,安卓引入了dip的概念-device independed pixel-简称dp。
1.1.2 dpi的定义
在近一步介绍dp
之前,要先讲讲dpi
。dpi
叫做屏幕像素密度,就是每英寸有多少个像素点。我们用简单的数学原理
<nobr><span class="math" id="MathJax-Span-1" style="width: 10.562em; display: inline-block;"><span style="display: inline-block; position: relative; width: 7.691em; height: 0px; font-size: 137%;"><span style="position: absolute; clip: rect(0.1em 1000em 3.652em -0.436em); top: -2.528em; left: 0.002em;"><span class="mrow" id="MathJax-Span-2"><span class="mi" id="MathJax-Span-3" style="font-family: STIXGeneral-Italic;">d<span style="display: inline-block; overflow: hidden; height: 1px; width: 0.051em;"></span></span><span class="mi" id="MathJax-Span-4" style="font-family: STIXGeneral-Italic;">p</span><span class="mi" id="MathJax-Span-5" style="font-family: STIXGeneral-Italic;">i</span><span class="mo" id="MathJax-Span-6" style="font-family: STIXGeneral-Regular; padding-left: 0.294em;">=</span><span class="mstyle" id="MathJax-Span-7" style="padding-left: 0.294em;"><span class="mrow" id="MathJax-Span-8"><span class="mfrac" id="MathJax-Span-9"><span style="display: inline-block; position: relative; width: 4.869em; height: 0px; margin-right: 0.1em; margin-left: 0.1em;"><span style="position: absolute; clip: rect(2.484em 1000em 4.625em -0.338em); top: -4.961em; left: 50%; margin-left: -2.382em;"><span class="msqrt" id="MathJax-Span-10"><span style="display: inline-block; position: relative; width: 4.771em; height: 0px;"><span style="position: absolute; clip: rect(1.365em 1000em 2.873em -0.436em); top: -2.528em; left: 1.073em;"><span class="mrow" id="MathJax-Span-11"><span class="msubsup" id="MathJax-Span-12"><span style="display: inline-block; position: relative; width: 1.219em; height: 0px;"><span style="position: absolute; clip: rect(1.316em 1000em 2.582em -0.436em); top: -2.236em; left: 0.002em;"><span class="texatom" id="MathJax-Span-13"><span class="mrow" id="MathJax-Span-14"><span class="mo" id="MathJax-Span-15"><span style="font-family: STIXGeneral, 'Arial Unicode MS', serif; font-size: 73%; font-style: normal; font-weight: normal;">长</span></span></span></span><span style="display: inline-block; width: 0px; height: 2.241em;"></span></span><span style="position: absolute; top: -2.771em; left: 0.732em;"><span class="mn" id="MathJax-Span-16" style="font-size: 70.7%; font-family: STIXGeneral-Regular;">2</span><span style="display: inline-block; width: 0px; height: 2.241em;"></span></span></span></span><span class="mo" id="MathJax-Span-17" style="font-family: STIXGeneral-Regular; padding-left: 0.246em;">+</span><span class="msubsup" id="MathJax-Span-18" style="padding-left: 0.246em;"><span style="display: inline-block; position: relative; width: 1.219em; height: 0px;"><span style="position: absolute; clip: rect(1.316em 1000em 2.582em -0.436em); top: -2.236em; left: 0.002em;"><span class="texatom" id="MathJax-Span-19"><span class="mrow" id="MathJax-Span-20"><span class="mo" id="MathJax-Span-21"><span style="font-family: STIXGeneral, 'Arial Unicode MS', serif; font-size: 73%; font-style: normal; font-weight: normal;">宽</span></span></span></span><span style="display: inline-block; width: 0px; height: 2.241em;"></span></span><span style="position: absolute; top: -2.771em; left: 0.732em;"><span class="mn" id="MathJax-Span-22" style="font-size: 70.7%; font-family: STIXGeneral-Regular;">2</span><span style="display: inline-block; width: 0px; height: 2.241em;"></span></span></span></span></span><span style="display: inline-block; width: 0px; height: 2.533em;"></span></span><span style="position: absolute; clip: rect(3.019em 1000em 3.36em -0.436em); top: -4.474em; left: 1.073em;"><span style="display: inline-block; position: relative; width: 3.701em; height: 0px;"><span style="position: absolute; font-family: STIXGeneral-Regular; top: -3.988em; left: 0.002em;">‾<span style="display: inline-block; width: 0px; height: 3.993em;"></span></span><span style="position: absolute; font-family: STIXGeneral-Regular; top: -3.988em; left: 3.165em;">‾<span style="display: inline-block; width: 0px; height: 3.993em;"></span></span><span style="font-family: STIXGeneral-Regular; position: absolute; top: -3.988em; left: 0.44em;">‾<span style="display: inline-block; width: 0px; height: 3.993em;"></span></span><span style="font-family: STIXGeneral-Regular; position: absolute; top: -3.988em; left: 0.878em;">‾<span style="display: inline-block; width: 0px; height: 3.993em;"></span></span><span style="font-family: STIXGeneral-Regular; position: absolute; top: -3.988em; left: 1.365em;">‾<span style="display: inline-block; width: 0px; height: 3.993em;"></span></span><span style="font-family: STIXGeneral-Regular; position: absolute; top: -3.988em; left: 1.803em;">‾<span style="display: inline-block; width: 0px; height: 3.993em;"></span></span><span style="font-family: STIXGeneral-Regular; position: absolute; top: -3.988em; left: 2.241em;">‾<span style="display: inline-block; width: 0px; height: 3.993em;"></span></span><span style="font-family: STIXGeneral-Regular; position: absolute; top: -3.988em; left: 2.727em;">‾<span style="display: inline-block; width: 0px; height: 3.993em;"></span></span></span><span style="display: inline-block; width: 0px; height: 3.993em;"></span></span><span style="position: absolute; clip: rect(2.29em 1000em 4.431em -0.338em); top: -3.793em; left: 0.002em;"><span style="font-family: STIXSizeOneSym;">√</span><span style="display: inline-block; width: 0px; height: 3.993em;"></span></span></span></span><span style="display: inline-block; width: 0px; height: 3.993em;"></span></span><span style="position: absolute; clip: rect(1.316em 1000em 2.582em -0.436em); top: -1.457em; left: 50%; margin-left: -1.798em;"><span class="mrow" id="MathJax-Span-23"><span class="texatom" id="MathJax-Span-24"><span class="mrow" id="MathJax-Span-25"><span class="mo" id="MathJax-Span-26"><span style="font-family: STIXGeneral, 'Arial Unicode MS', serif; font-size: 73%; font-style: normal; font-weight: normal;">对</span></span></span></span><span class="texatom" id="MathJax-Span-27"><span class="mrow" id="MathJax-Span-28"><span class="mo" id="MathJax-Span-29"><span style="font-family: STIXGeneral, 'Arial Unicode MS', serif; font-size: 73%; font-style: normal; font-weight: normal;">角</span></span></span></span><span class="texatom" id="MathJax-Span-30"><span class="mrow" id="MathJax-Span-31"><span class="mo" id="MathJax-Span-32"><span style="font-family: STIXGeneral, 'Arial Unicode MS', serif; font-size: 73%; font-style: normal; font-weight: normal;">线</span></span></span></span><span class="texatom" id="MathJax-Span-33"><span class="mrow" id="MathJax-Span-34"><span class="mo" id="MathJax-Span-35"><span style="font-family: STIXGeneral, 'Arial Unicode MS', serif; font-size: 73%; font-style: normal; font-weight: normal;">尺</span></span></span></span><span class="texatom" id="MathJax-Span-36"><span class="mrow" id="MathJax-Span-37"><span class="mo" id="MathJax-Span-38"><span style="font-family: STIXGeneral, 'Arial Unicode MS', serif; font-size: 73%; font-style: normal; font-weight: normal;">寸</span></span></span></span></span><span style="display: inline-block; width: 0px; height: 2.241em;"></span></span><span style="position: absolute; clip: rect(0.878em 1000em 1.219em -0.436em); top: -1.311em; left: 0.002em;"><span style="border-left-width: 4.869em; border-left-style: solid; display: inline-block; overflow: hidden; width: 0px; height: 1.25px; vertical-align: 0.002em;"></span><span style="display: inline-block; width: 0px; height: 1.073em;"></span></span></span></span></span></span></span><span style="display: inline-block; width: 0px; height: 2.533em;"></span></span></span><span style="border-left-width: 0.003em; border-left-style: solid; display: inline-block; overflow: hidden; width: 0px; height: 4.603em; vertical-align: -1.397em;"></span></span></nobr>
<script type="math/tex; mode=display" id="MathJax-Element-1"> dpi = \dfrac{\sqrt{长^2 + 宽^2}}{对角线尺寸} </script>就能算出刚才两个屏幕的dpi,一个是
440dpi
,另一个是
184dpi
。
注意,dpi
与dip(dp)
写法很像,千万不要混淆了,前者和密度有关,后者与像素有关。
1.1.3 dpi的划分
安卓使用dpi
为160的数值作为一个基准--baseline
,
- 如果一个设备的
dpi
刚好等于这个基准,那么它就是mdpi设备; - 如果一个设备的
dpi
是这个基准的1.5倍,那么它就是hdpi设备; - 如果一个设备的
dpi
是这个基准的2倍,那么它就是xhdpi设备; - 如果一个设备的
dpi
是这个基准的3倍,那么它就是xxhdpi设备;
这就是划分不同屏幕密度的依据。
像素密度类型 | 像素密度大小 | 与Baseline比值 |
---|
mdpi | 160 | 1 |
hdpi | 240 | 1.5 |
xhdpi | 320 | 2 |
xxhpi | 480 | 3 |
按照这样的约定划分,
- 刚才1080*1920的屏幕应该属于xxhdpi类型;
- 450*800的屏幕应该属于mdpi类型;
现在的安卓手机几乎都在往高清屏幕发展,所以xhdpi和xxhdpi是最为常见的设备类型。
屏幕的dpi
计算出来不一定就是基准(160)的整数倍,而且安卓系统也不一定会使用我们计算出来的那个值作为设备的dpi。
设备通常会在系统中指定一个接近那个值的dpi值,例如,你的Nexus5,虽然计算出来是440,但系统实际上给它指定的是480,刚好是基准的3倍。
在系统的目录的/system/build.prop
文件中,可以看到,
//这里指定了一个dpi值
ro.sf.lcd_density=480
1.1.4 dp与px的关系
安卓引入了dp(dip)
的概念之后,实际显示的像素就可以通过下面这个公式计算出来
<nobr><span class="math" id="MathJax-Span-39" style="width: 8.421em; display: inline-block;"><span style="display: inline-block; position: relative; width: 6.134em; height: 0px; font-size: 137%;"><span style="position: absolute; clip: rect(1.024em 1000em 3.36em -0.533em); top: -2.528em; left: 0.002em;"><span class="mrow" id="MathJax-Span-40"><span class="mi" id="MathJax-Span-41" style="font-family: STIXGeneral-Italic;">p</span><span class="mi" id="MathJax-Span-42" style="font-family: STIXGeneral-Italic;">x<span style="display: inline-block; overflow: hidden; height: 1px; width: 0.002em;"></span></span><span class="mo" id="MathJax-Span-43" style="font-family: STIXGeneral-Regular; padding-left: 0.294em;">=</span><span class="mi" id="MathJax-Span-44" style="font-family: STIXGeneral-Italic; padding-left: 0.294em;">d<span style="display: inline-block; overflow: hidden; height: 1px; width: 0.051em;"></span></span><span class="mi" id="MathJax-Span-45" style="font-family: STIXGeneral-Italic;">p</span><span class="mo" id="MathJax-Span-46" style="font-family: STIXGeneral-Regular; padding-left: 0.246em;">∗</span><span class="mstyle" id="MathJax-Span-47" style="padding-left: 0.246em;"><span class="mrow" id="MathJax-Span-48"><span class="mfrac" id="MathJax-Span-49"><span style="display: inline-block; position: relative; width: 1.608em; height: 0px; margin-right: 0.1em; margin-left: 0.1em;"><span style="position: absolute; clip: rect(1.706em 1000em 2.873em -0.436em); top: -3.209em; left: 50%; margin-left: -0.679em;"><span class="mrow" id="MathJax-Span-50"><span class="mi" id="MathJax-Span-51" style="font-family: STIXGeneral-Italic;">d<span style="display: inline-block; overflow: hidden; height: 1px; width: 0.051em;"></span></span><span class="mi" id="MathJax-Span-52" style="font-family: STIXGeneral-Italic;">p</span><span class="mi" id="MathJax-Span-53" style="font-family: STIXGeneral-Italic;">i</span></span><span style="display: inline-block; width: 0px; height: 2.533em;"></span></span><span style="position: absolute; clip: rect(1.706em 1000em 2.679em -0.338em); top: -1.847em; left: 50%; margin-left: -0.727em;"><span class="mn" id="MathJax-Span-54" style="font-family: STIXGeneral-Regular;">160</span><span style="display: inline-block; width: 0px; height: 2.533em;"></span></span><span style="position: absolute; clip: rect(0.878em 1000em 1.219em -0.436em); top: -1.311em; left: 0.002em;"><span style="border-left-width: 1.608em; border-left-style: solid; display: inline-block; overflow: hidden; width: 0px; height: 1.25px; vertical-align: 0.002em;"></span><span style="display: inline-block; width: 0px; height: 1.073em;"></span></span></span></span></span></span></span><span style="display: inline-block; width: 0px; height: 2.533em;"></span></span></span><span style="border-left-width: 0.003em; border-left-style: solid; display: inline-block; overflow: hidden; width: 0px; height: 2.937em; vertical-align: -0.997em;"></span></span></nobr>
<script type="math/tex; mode=display" id="MathJax-Element-2"> px = dp*\dfrac{dpi}{160} </script>
相同dp
的情况下,密度大的设备,实际像素就占的大,密度小点设备,实际像素就占用小了。因此使用dp这个单位后,按钮在两种不同的屏幕上的实际显示大小,几乎就是一样的了。
例如刚才两种屏幕。如果我指定按钮的大小为200dp*200dp,那么它们在各自的屏幕上占用的实际像素分别是230px*230px与550px*550px,真实设备上看上去的确差不多。
1.2 大小设置
在设置布局或者控件大小的时候,会使用它们的android:layout_width
和android:layout_height
属性,
<View
android:layout_width="match_parent"
android:layout_width="wrap_content"/>
它们的值可以设定成,
- 特定的数值:例如
5dp
。当然也可以使用除了dp
以外其他的尺寸单位,但是考虑到屏幕的像素密度不同,我们都使用dp
为单位; -
match_parent
:以父布局的宽度(或长度)为界,尽可能占据全部空间; -
wrap_content
:以子布局或控件内容的宽度(或长度)为界,尽可能少占据空间;
1.3 边距
-
margin
:控件或布局相对外面组件的边距叫做margin
,例如一个LinearLayout
与它相邻的另一个FrameLayout
之间的间隔。在布局或控件中,使用以下属性来定义,
android:layout_margin="5dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="5dp"
-
padding
:控件或布局为内部区域保留的边距叫做padding
,例如一个LinearLayout
与它内部的一个FrameLayout
之间的间隔,或者TextView
的边框与它内部文字之间的间隔。在布局或控件中,使用以下属性来定义,
android:padding="5dp"
android:paddingLeft="5dp"
android:paddingTop="5dp"
android:paddingRight="5dp"
android:paddingBottom="5dp"
1.4 可见性
布局和控件都还有个常用的android:visiblility
属性,
<View
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:visibility="visible"/>
它有3个可选的参数,
-
visible
:这个控件或者布局是可见的; -
invisible
:这个控件或者布局是不可见的,但是它的位置还在那里,只是界面上看不到它,别的控件或布局还是需要考虑它的位置; -
gone
:这个控件或者布局不仅不可见,而且它的位置已经不在那里了,在显示的时候,别的控件或布局都不用考虑它的位置了;
默认情况下,如果不指定这个属性,系统通常会为它们使用visible
参数。
1.5 位置
无论控件还是布局都可以设置它本身相对于父布局的偏好位置,例如是居中还是靠左。例如一个放到LinearLayout
中的Button
,它希望自己位于父布局内部靠上的位置,就可以添加android:layout_gravity
属性,设置值为top
,
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_gravity="top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello world!"/>
</LinearLayout>
其它可以设置的值还有bottom
left
right
center
center_horizontal
center_vertical
。
实际效果也要看父布局的类型,比如在上面的例子中,子控件的位置属性只是向LinearLayout
提出请求,但LinearLayout
还是要从自己的实际情况出发--它不一定能满足所有子控件或者布局的要求。
比如这里使用bottom
值,就是没有意义的。因为LinearLayout
本来就是要求子布局(或控件)从上到下进行排列。所以即使设定了bottom
值,Button
还是会在上方。
控件或者布局,也可以设置自己的内容相对于内部的位置。例如上面的例子中,要将Button
放到LinearLayout
底部靠右的位置,可以为其android:gravity
属性设置bottom|right
。这里的|
表示并且
,
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom|right">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello world!"/>
</LinearLayout>
这里可以让LinearLayout
从下方开始排列子控件的原因是:这是LinearLayout
自己的属性,而不是听子控件的安排。android:gravity
属性告诉了LinearLayout
,在排列时优先放下边、左边。
<script type="text/javascript">
$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('\n').length;
var $numbering = $('<ul/>').addClass('pre-numbering').hide();
$(this).addClass('has-numbering').parent().append($numbering);
for (i = 1; i <= lines; i++) {
$numbering.append($('<li/>').text(i));
};
$numbering.fadeIn(1700);
});
});
</script>