分类目录归档:布局与控件

布局与控件 第10节 ListView的Adapter

更新时间 修改意见
2016-08-02 陈敏

第10节 ListView的Adapter

安卓系统为ListView设计了多种Adapter作为它的搭档。每种Adapter不仅为ListView提供数据内容,也会告诉ListView如何展示这些数据-规定好列表项的长相。

这些Adapter都是从Adapter类继承而来的,它们的关系如下:

这里我们选择性的介绍常见的2种AdapterArrayAdapterSimpleAdapter

10.1 ArrayAdapter

ArrayAdapter是最简单的Adapter,我们在前面已经使用过它,

在创建ArrayAdapter的时候,

  1. 要声明这个Adapter要接受什么类型的数据,

  2. 指定布局文件的layout id

    这个布局就是一个TextView,只不过被指定了android:id@android:id/text1

    注意这里在给控件指定ID的时候用的是`@id/text1,而没有用+。这表明,这个ID值是已经存在的,而不需要编译器再单独分配。

    这样ArrayAdapter会自动寻找这个布局文件中ID名字为text1的控件,并认为它就是可以用来显示数据的TextView

    如果不想用系统给TextView指定的ID,也可以给自己定义的布局的TextView取个名字,例如show_content_textview

    然后向ArrayAdapter再传入一个ID,告诉它将要显示的文字,放到这个ID所指示的控件上,

  3. 传入要显示的数据内容,这个数据内容可以以数组的方式传入,也可以以List的方式放入,

10.2 SimpleAdapter

看得出ArrayAdapter是相当的简单,只能在单一的TextView上显示一条数据。如果有好几条不同类型的数据要显示到同一列表项的不同部位,那就不好办了。SimpleAdapter正好能帮我们解决这样的问题。虽然它叫Simple,不过不是说功能simple,而是使用很simple。

它的设计思路是,

  1. 每一条列表项上哪些控件希望显示数据都要告诉SimpleAdaperSimpleAdaper只要知道这些控件的ID就可以了。所以在创建的时候,需要传入这些控件的ID值(以数组的形式);

  2. 每一项待显示的数据,要和列表上的控件一一对应。这就需要一个翻译系统,给每个位置的数据取个名字,将待显示数据挨个放入键值对的列表中(map);并将每个位置的数据名字和控件ID值对应起来;

  3. SimpleAdapter在绑定数据到界面上的时候,就根据对应关系,一个一个把它们放上去。

例如,

  1. 准备要显示的数据,让每个数据项,和一个特定的名字(title pic content)对应上,

  2. 将特定的名称做成数组,和显示区域的ID一一对应上,例如,

    title <-> R.id.title_id
    pic <-> R.id.pic_id
    content <-> R.id.content_id

  3. 创建SimpleAdapter

10.3 自定义Adapter

虽然Android SDK为我们提供了好几种现成的Adapter使用,但有时它们也并不能完全符合我们的要求,要么用起来还是麻烦,要么大材小用。另外,为了把ListView介绍的全面一些,我们准备自定义一个Adapter。

10.3.1 定义数据项的布局

为了让列表的数据项按照我们设计的模样显示,我们需要为它设计一个布局,用展示的视频列表为例,加以说明。

数据项的布局定义在res\layout\video_item.xml文件中,

  1. 数据项显示在水平布局的LinearLayout中;
  2. 视频缩略图用ImageView控件显示,给它的android:scaleType属性设置center,让缩略图居中放置,背景设置成应用主题的色调colorPrimary
  3. 其他视频信息包含标题和创建时间,将它们竖直排列放在一个LinearLayout中,占用高度按照2:1分配,前者使用主题中较大的字体?android:attr/textAppearanceMedium,后者使用主题中较小的字体?android:attr/textAppearanceSmall
  4. 至于各个组件之间的间隔,根据自己的视觉偏好调整就好了,用android:paddingandroid:layout_margin设置;

10.3.2 定义数据项内容

为每个视频信息,定义一个数据结构VideoItem

10.3.3 定义Adapter

所有Adapter都是继承自BaseAdapter的,我们自定义的Adapter也继承自它。

  1. 继承BaseAdapter,准备实现必须实现的基类函数;

  2. 创建构造函数,在构造函数中,保存好布局ID,以便以后使用,通过Context获取Inflater,为以后数据项布局的创建做准备;保存要展示的数据项们;

  3. 实现getCount()函数,返回当前数据项的个数,

  4. 实现getItem()函数,根据传入的索引号,返回对应的数据项,

  5. 实现getItemId()函数,根据传入的索引号,返回对应项的id值,

  6. getView()函数中,创建数据项的布局,并为他们赋值,最后将这个布局返回给ListView,让它显示,

    这里的convertView就是数据项所代表的那个布局,当ListView刚创建,还没有产生任何数据项的时候,它就是为null的,此时我们就需要创建一个布局,并通过getView()将这个布局返回给ListView

    假如ListView上的数据项布局已经足够了,那么这里传入的convertView就不会再是“null”,而是之前的某个数据项布局,我们就不必为此重新创建了,只需要更新上面的内容就好。这样提高了界面刷新的效率。

    当然,这里还能通过其他方法减少使用findViewById(),进一步提高效率,不过目前就不改进了,先把功能实现完成。


综合以上内容,最后的代码就是,

10.3.4 使用自定义Adapter

  1. 在Video List的Activity创建之时,我们在onCreate()中创建并设置VideoAdapter

  2. 当列表中的数据有变化时,在主线程中更新数据列表,并使用notifyDataSetChanged()刷新,

10.3.5 改进提高效率

在前面实现自定义Adapter的getView()函数中,没有每次都创建一个convertView,而是复用已有的布局,这样就节省重新创建的资源。不过每次都使用findViewById()也会花掉不少的开销。

为此,我们可以引入一个数据结构,将这些控件保存下来,在使用的时候直接获取,不需要进行耗时的findViewById()操作了。

  1. 创建一个数据类,准备存放控件,

  2. 创建布局的时候,通过创建Holder将控件保存起来,

  3. 获取Holder,直接使用,提高效率,


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

布局与控件 第9节 ListView

更新时间 修改意见
2016-08-02 陈敏

第9节 ListView

在应用界面当中,经常需要使用列表来展示内容。
Android SDK提供了ListView控件,来实现这种效果。

ListView需要和Adapter配合使用,ListView负责内容的显示,Adapter负责为ListView提供要展示的数据。

列表的设计采用了将数据与展示分离的模式-数视分离。ListView负责数据的展现,Adapter负责向ListView提供要展示的数据,以及每条列表上的数据如何显示。

这是程序设计中常常用到的一种设计方法。这一章节我们主要介绍ListView,而它的搭档Adapter将放到下一章节详细介绍,所以现在我们只需要知道Adapter用来存放数据、规定每条列表项长什么样子。

9.1 ListView的使用方法

使用ListView展示内容,通常分下面几个步骤,

  1. 在布局文件中设置ListView布局;

  2. 在Activity界面创建的时候(例如onCreate()当中),通过代码获取ListView

  3. 创建一个Adapter负责为ListView提供数据。Android SDK提供了很多类型的AdapterArrayAdapter CursorAdapter SimpleAdapter等等,它们都是BaseAdapter的子类,简化了Adapter的使用。要显示的数据和显示这项数据项的布局要设置给Adapter

    Android SDK提供了一些常用的数据项布局方式android.R.layout.simple_list_item_1 android.R.layout.simple_list_item_2等等。我们也可以自己设计每一项的布局方式,后面会讲到。

  4. Adapter设置给ListView,数据将以列表的形式被展示,

  5. 为显示的每个item添加,点击时代响应处理函数;

综合以上的代码,就是,

运行以后,就能看到a b c以列表的形式,在界面上展现出来了。

9.2 ListView内容的更新

如果需要展示的数据有变化,就需要更新ListView,需要注意,

  1. 界面的更新需要在主线程进行(UI线程),如果在其他线程更新,系统有可能报错,并提示你“不能在非UI线程更新界面元素”;
  2. 修改了Adapter中要展示的数据后,需要使用AdapternotifyDataSetChanged(),界面就会刷新,看到修改的效果;

例如,初始化显示列表,并显示内容

主线程中更新数据,并刷新,

9.3 常用效果

9.3.1 修改点按时的背景效果

列表项的背景颜色是可以根据点按的不同状态而变化的,例如点击列表项的时候,背景颜色变深一点,出于选中的状态,背景颜色与别的项不同等等。
这一切只需要为ListViewlistSelector属性设置上一个Selector就好了,例如,

其中,@color/normalres/values/colors.xml中,被设计成,

使用的时候就像这样,

大多数时候,希望那些没有被选中的列表项的背景颜色能和选中了但是没有被点击的列表项颜色一致,例如上面d列表项的背景要和c列表项没有被点击时一个颜色,那么可以为ListView的背景设置一个与没有点击的一样的颜色,

这里有个非常重要的地方需要注意:对于ListView来说,只有当列表项被点击(press)或者获取焦点(focus)的时候,它对应的列表项才会被使用ListView设置的这个Selector

所以对于那些没有被点击或者没有获取焦点的数据项,即使它们满足Selector中的某些条件,Selector也将完全不起作用。

9.3.2 修改分隔线

ListView中每一个列表项之间的分隔栏是可以进行调整的,可以通过android:divider属性设定它的颜色,通过android:dividerHeight属性设定它的高度,

9.3.3 添加header和footer

ListView的顶部和底部,可以各添加上自定义栏。不过添加的过程需要在代码中进行,

  1. 用java代码或者xml的方式,创建要添加到header或者footer的界面;
  2. 使用addHeaderView()将header界面添加到ListView的顶部;
  3. 使用addFooterView()将footer界面添加到ListView的底部;

9.3.4 ScrollBar

假如列表中的数据比较多,而用户又在上下滑动列表的时候,左边会出现一个滚动条,用来指示当前看到的内容在整个列表的比例位置。

可以通过android:scrollbars属性来设置这个bar是否需要显示,

  1. none:永远不显示;
  2. vertial:需要的时候显示在右边栏;
  3. horizontal:需要的时候显示在下边栏;

9.3.5 FastScroller

当列表中的数据非常多的时候,可以通过设置android:fastScrollEnabled属性,来启动FastScroller。可以让用户拖动右边的滑块,加快浏览的速度。不过,假如列表项不能占满4个屏幕的高度,那么即使设置了android:fastScrollEnabled属性,FastScroller也是不会出现的。毕竟数据少,也就不需要Fastscroller出马了。

Fastscroller的滑块和轨道是可以自定义的,在应用的主题中设定android:fastScrollTrackDrawableandroid:fastScrollThumbDrawable属性,就可以做出修改,

9.4 ListView的Choice Mode

ListView本身带有单选和多选功能,也就是说在单选模式下,它能够记住当前选中的唯一的列表项;在多选模式下,它能够记住目前选取的所有的列表项。

这种选择模式共有4种CHOICE_MODE_NONE CHOICE_MODE_SINGLE CHOICE_MODE_MULTIPLE CHOICE_MODE_MULTIPLE_MODAL

使用setChoiceMode()函数,就可以设置ListView的选择模式。

9.4.1 CHOICE_MODE_NONE

这是ListView默认的选择模式,当使用者点击列表项后,不会将任何点击的列表项当成被选择上的列表项。

9.4.2 CHOICE_MODE_SINGLE

使用这个选项模式,当使用者点击列表项后,会将最后一次点击的列表项当成被选择上的列表项。

每次点击列表项,可以在它的监听函数中得知哪个列表项被点击了,

以后就可以通过ListViewgetCheckedItemPositions()获取被选中的数据项的位置列表,

例如,当列表项按照序号为3->2->1-7的顺序被点击后,7将被记录下来,作为当前被选中的数据项。调用getCheckedItemPositions()后,将得到一个键值对,里面只会记录,

这里为了看到被选中的效果,采用了Android SDK提供的android.R.layout.simple_list_item_single_choice布局,作为列表项的布局。

9.4.3 CHOICE_MODE_MULTIPLE

使用这个选项模式,当使用者点击列表项后,会将所有点击的列表项当成被选择上的列表项。

每次点击列表项,可以在它的监听函数中得知哪个列表项被点击了,

通过ListViewgetCheckedItemPositions()获取被选中的数据项的位置列表,

例如,当列表项按照序号为3->2->1->7的顺序被点击后,3 2 1 7将被记录下来,作为当前被选中的数据项。调用getCheckedItemPositions()后,将得到一个个键值对,里面会记录,

这些记录,将按照列表项的位置从小到大排列。

如果用户在已经被选上的数据项上,又再次点击,这将取消对它的选择。例如,当列表项按照序号为3->2->1->7->2->1的顺序被点击后,就变成了,

对于被取消了选择的列表项,并不会从记录中移除,而是改变它的值成false

这里为了看到被选中的效果,采用了Android SDK提供的android.R.layout.simple_list_item_multiple_choice布局,作为列表项的布局。

9.4.4 CHOICE_MODE_MULTIPLE_MODAL

CHOICE_MODE_MULTIPLE_MODAL模式中,用户必须通过长按任意一个列表项,进入多选模式,否则不能进行多选。

这个模式的使用与CHOICE_MODE_MULTIPLE类似,不过它需要设置一个ActionMode,这样才能在长按列表项后,改变ActionBar的菜单栏,打开多选的菜单。

  1. 实现ListViewMultiChoiceModeListener接口,

  2. 使用创建的接口,传递给ListView

CHOICE_MODE_MULTIPLE_MODAL模式下,对列表项的点击,是在ListView.MultiChoiceModeListeneronItemCheckedStateChanged()函数中响应的。

这里为了看到被选中的效果,采用了Android SDK提供的android.R.layout.simple_list_item_multiple_choice布局,作为列表项的布局。

9.4.5 关于单选和多选的状态背景

前面的演示中,分别对不同的选择模式,使用了不同的数据项布局,

我们也可以选择自己的布局来实现。但是不管使用什么布局,我们都希望被选上的列表项有特别的效果,和其他没有被选上的数据项区别开。这里有两种方案,

  1. 使用实现了Clickable接口的控件或者布局,作为数据项的布局。例如前面我们使用的android.R.layout.simple_list_item_multiple_choice android.R.layout.simple_list_item_single_choice等,它们本身就是CheckedTextView。当点击它们的时候,它们就自带了勾选框,可以看到效果。

    所以,可以选择CheckBox Switch这类同样实现了Clickable接口的布局来表现;也可以自定义一个实现了Clickable接口的布局。这里用android.R.layout.simple_list_item_multiple_choice的实现来举个例子,

  2. 给数据项的布局使用一个Selector,告诉这个布局,当它被activated以后要怎么显示。比如,定义一个布局custom_item_layout.xml,

    给这个布局设计一个selector,增加对android:activated属性的设置,

    使用这个这个布局,

    当我们多选的时候,那些选中项,就变成黄色,看到如下的界面,


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

布局与控件 第8节 VideoView

更新时间 修改意见
2016-08-02 陈敏

第8节 VideoView

播放视频可以使用Android SDK提供的现成的控件VideoView。它是对media playersurface的封装,对于初次进行视频播放开发的开发者来说,使用VideoView是最简单和方便的,不用关注太多细节上的实现方式。

8.1 VideoView的使用

VideoView的使用,非常简单,

  1. 在布局文件中放置一个VideoView控件,

  2. 在Activity当中,获取布局文件中的VideoView,然后设置要播放的视频地址,

    1. 使用字符串路径,视频的地址类似于:/storage/emulated/0/Video/【大自然】mothernature(蒋雯丽配音).mp4
    2. 使用uri路径;
  3. 使用VideoView提供的接口,控制视频播放的流程,

8.2 控制面板

还可以为VideoView添加控制面板-MediaController,这个面板集成了播放进度拖动、暂停、继续播放等功能,还可以自动隐藏或显示。

使用Android SDK自带的MediaController

如果VideoView有父布局,那么为它添加的MediaController就会附着在父布局的底部的。

因此为了界面美观,经常在布局文件中,将VideoView单独放到一个FrameLayout当中,并让它居中显示,


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

布局与控件 第7节 TableLayout

更新时间 修改意见
2016-08-02 陈敏

第7节 TableLayout

TableLayout顾名思义,就是像表格一样的布局。它是LinearLayout的子类,所以拥有LinearLayout的所有属性。

7.1 TableLayout的行数

TableLayout需要与它的搭档TableRow配合使用,TableRow也是LinearLayout的子类,表示表格的每一行,例如一个表格有3行,它的布局文件就应该像这样,

TableRow可以不用指定它的android:layout_heightandroid:layout_width属性(对于其它类型的布局来说,这两个属性是必须要设置的,不然编译器会提示错误),默认情况下,android:layout_heightwrap_content,android:layout_widthmatch_parent

7.2 TableLayout的列数

每一个TableRow中包含的子控件(或者子布局)的个数,就是表格的列数。如果各个TableRow中包含的子控件个数不相同,那么就以最多的个数为准,作为整个表格的列数。

例如下面的布局,

效果如下,可以看到这是一个5*4的表格,有的行有2格、有的有3格、有的有4格,于是采用格数最多的作为表格的列数。这里因为屏幕空间比较大,并没有占据完整个屏幕。

7.3 TableLayout界面调整

7.3.1 表格的平分

很多时候,我们希望表格的每一行能够平均分配可用的空间,那么可以为每个TableRow设置android:layout_weight=1

表格的每一列能够平均分配可用的空间,那么可以为每个TableLayout添加android:stretchColumns="*",这样,剩余的空间就被拉伸平分了,

android:stretchColumns用来指定需要拉伸的单元格,*表示所有单元格。

也可以指定部分单元格拉伸,例如指定第2列赫第4列,

注意,可拉伸的单元格序号是从0开始;多个单元格,可以用逗号分隔开。

7.3.2 表格列的收缩

有的时候,如果一个单元格的内容过长,会影响到同一行其它列的显示效果,例如,

如果对TableLayout使用了android:shrinkColumns,并指定可以收缩的列为0,那么这一列的内容就可以朝着下方伸展,

注意,可收缩的单元格序号是从0开始;多个单元格,可以用逗号分隔开。

7.3.3 表格列的隐藏

要隐藏某一列也很容易,使用android:collapseColumns,将要隐藏的列的序号填入即可,例如,

可以看到,第一列已经被隐藏起来不见了。

注意,可收缩的单元格序号是从0开始;多个单元格,可以用逗号分隔开。

7.3.4 单元格的跨列

有时,希望一个按钮能够跨多列,可以使用android:layout_span属性,例如这里让按键0,跨两列

需要注意的是,TableLayout中的单元格并不能跨行合并显示。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

布局与控件 第6节 布局的抽象标签-merge include ViewStub

更新时间 修改意见
2016-08-02 陈敏

第6节 布局的抽象标签-merge include ViewStub

include标签、merge标签,以及ViewStub标签是安卓布局使用的抽象标签。它们并不代表某个具体的布局或者控件,而是起布局的辅助作用,提高布局执行的效率和易用性。

例如,对于那些会重复使用到的布局结构,可以它们放到一个单独的layout文件中。当我们在任何要使用这个布局的地方,就通过复用的方法,将它包含到新的布局文件中。

假设下面这个布局结构会被经常的使用,我们就可以将它单独定义到一个独立的布局文件reuse.xml

6.1 include标签

当别的布局文件main_layout.xml,要使用上面被独立出来的布局时,就可以用标签把这个布局包含进来。在标签的layout属性中,指定要复用的布局的id就可以了。

例如main_layout.xml使用标签,

此时main_layout.xml实际上,就变成了,

6.2 merge标签

再来看看merge标签,将reuse.xml中的FrameLayout修改成merge

main_layout.xml继续使用标签,

此时main_layout.xml实际就变成了,

与之前相比,少了FrameLayout一层。在布局时少一个层次的包裹,可以增加布局的效率。

6.3 ViewStub标签

这是一个布局的占位符。当一个布局包含了这种标签,它并不会加载这个标签的内容,而是在需要的时候,通过代码来动态加载。

例如,一个布局main_layout.xml里面,有一个ProgressBar,布局被加载时,这个ProgressBar就会被创建出来,

如果我们将ProgressBar放到一个单独的布局文件progress_layout.xml当中,

然后main_layout.xml中使用ViewStub引用这个布局,

布局被加载时,这个ProgressBar并不会被创建出来。使用如下方式,就可以把ViewStub,替换成android:layout指定的布局,

  1. 在代码中使用setVisibility()函数,

  2. 在代码中使用inflate()函数,

替换之后,ViewStub就被从布局当中移除了,这个布局就变成了,


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

布局与控件 第5节 Toast

更新时间 修改意见
2016-08-02 陈敏

第5节 Toast

5.1 使用效果

Toast用来向用户弹出一个提示框,然后自动消失,就像这样,

面包机烤好面包后,就腾的一下把面包从面包机里弹出来。而这个控件显示时也像是从面包机里弹出来的,所以取了这个名字-Toast(吐司)。

使用Toast显示消息的时候,即使启动它的Activity并没有显示在屏幕上,Toast提示的消息也会被显示到最前面,让用户看到。例如,

  1. Activity A正通过网络下载一个文件,
  2. 此时用户点击Home回到主界面,又启动了另一个应用的Activity B;现在Activity A不再显示了;
  3. 下载完成后,隐藏起来的Activity A使用Toast给用户一个提示;
  4. 虽然Activity A现在没有显示出来,但是它使用Toast给出的提示,还是会被显示到整个界面的最上面,被用户看到;

5.2 显示原理

这是因为Toast会显示在一个特别的窗口层次上,这个窗口比任何Activity使用的窗口层次更高,更优先的显示到上层。

关于窗口系统具体的原理,我们会在安卓系统的窗口机制相关章节进行介绍。

5.3 使用方式

调用Toast的makeText函数生成一个Toast对象,再调用它的show函数显示出来。makeText有三个参数,第一个是Context对象,第二个是要显示的字符串,第三个是要显示的时长。

  1. 第一个参数Context:是当前Activity运行的上下文环境,也就是它运行时所拥有的各种系统资源。Activity是从Context继承而来的,所以Activity本身就是我们要找的context;
  2. 第二个参数:是要显示的内容;
  3. 第三个参数:显示的时间长短,它只能设置长或短。
    Toast.LENGTH_SHORT,Toast.LENGTH_LONG。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

布局与控件 第4节 Button

更新时间 修改意见
2016-08-02 陈敏

第4节 Button

按钮是需要与用户进行互动的控件。

Button继承自TextView,凡是TextView有的特定,它都有。

4.1 与TextView的不同

Button是使用了特别style的TextView。在Button创建的过程中,它使用了一个系统内部的style,

4.2 Button的使用方式

Button被点击后,需要有所响应。其实不仅是Button,所有的View(包括TextView)都可以添加响应。

安卓提供了两种响应的方式。

4.2.1 在xml布局文件中设置响应

  1. Button添加一个属性android:onClick,让它的值等于监听函数的函数名。例如,叫onClick

  2. 在java源码当中,创建一个onClick()函数,声明成public, 返回void类型,传入参数View

  3. 使用ViewgetId(),识别需要响应的对象,添加响应代码,

4.2.2 在java源码中设置响应

  1. 在java源码当中,创建监听器,

  2. 通过findViewById()得到Button;再通过ViewsetOnClickListener设置监听器mListener

虽然这里讲的是Button响应的onClick,但实际上,所有的View都可以响应点击操作。

所有的控件或者布局都是View的子类,所以它们设置监听的方法也都是一样的。

4.3 按钮点击效果

除了使用系统设计的按钮样式,也可以自己定义按钮按下去和没有按下去的效果。

  1. res\drawable\目录上,点击右键,启动创建drawable resource的向导;

  2. 创建selector drawable的xml文件,文件名为button_selector

    这样,就在res\drawable\目录下创建了一个名为button_selector.xml的drawable文件。

  3. 根据Button是否被按下的状态android:state_pressed,分别为它们设置不同的drawable。

    android:state_pressed=true,说明当前按钮被按下,
    android:state_pressed=false,说明当前按钮没有被按下;

    这里我们用ColorDrawable演示设置Drawable。在res\values\colors.xml中定义的颜色的名字;给android:drawable属性设置颜色,

    如果使用其它类型的Drawable,使用方式类似。

    例如背景是图片:按下使用normal.png,未按下使用pressed.png。将这些图片放在res\drawable\目录下。对应的selector就应该写成,

  4. Buttonandroid:background属性,使用drawable selector,


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

布局与控件 第3节 TextView

更新时间 修改意见
2016-08-02 陈敏

第3节 TextView

这是界面设计最为常用的控件,也是很多别的控件的父类,例如Button

3.1 文字常用属性

最常使用到的属性,通过它们的名字就可以判断出它们的作用:

android:text:为TextView设置显示的文字内容;

android:textColor:设置文字的颜色;

android:textSize:设置文字的大小,文字大小最好使用sp为单位;

android:textAllCaps:显示的字符都是大写,

3.2 文字字体

android:textStyle:设置文字的样式,粗体bold、斜体italic等等,默认情况下是normal

android:fontFamily:设置文字显示用的的特定字体家族,可用的字体家族可以参考安卓系统源码的system_fonts.xml文件,里面fileset标签中的名字,就可以用作android:fontFamily的参数,

android:typeface:设置文字的字型,有normal sans serif monospace四种字型设置;它会与font family对应的字体家族合作,查找合适的字体用来显示文字;

如果你使用了fontFamily,那么系统会优先从该fontFamily中,结合typefacetextStyle查找合适的字体;假如没有找到,就会在默认的fontFamily中,结合typefacetextStyle查找合适的字体。

3.3 文字空间利用

android:singleLine:即使文字内容很长,也不要换行显示;

android:lines:如果文字内容很长,会换行显示,并且显示行数为设置的行数;如果你的文字没有设定的行数(例如10行)那么多,那么整个TextView也会占据10行文字的高度;

android:maxLines:如果文字内容很长,会换行显示,但是行数不能超过设置的行数;如果你的文字没有设定的行数那么长,那么整个TextView的高度也就是文字实际占用的高度;

android:maxLength:指定要显示的字符数量,一个中文汉字也认为是一个字符;

android:ellipsize:如果文字内容很长,可以通过这个属性设定它的显示效果,

  1. end:显示文字内容的前面部分,后面显示不下的内容用...表示;

  2. start:显示文字内容的后面部分,前面显示不下的内容用...表示;还有其它的条件需要满足:文字必须只有一行;

  3. middle:显示文字的中间部分:两头显示不下的内容用...表示;还有其它的条件需要满足:文字必须只有一行;

  4. marquee:以走马灯的方式(文字从右到左滚动)显示;不过要显示这个效果,还有其它的条件需要满足:文字必须只有一行,TextView可以获取焦点,并处于被选中的状态;

3.4 文字显示效果

android:textAppearance用来指定TextView显示文字时的风格-style。这种style的定义如下,设置了字体的大小、颜色等属性

使用的时候,可以使用上面那种自己定义的style,

也可以使用系统预设好的style,
android:textAppearance="?android:attr/textAppearanceSmall
android:textAppearance="?android:attr/textAppearanceMedium
android:textAppearance="?android:attr/textAppearanceLarge
android:textAppearance="?android:attr/textAppearanceLarge等等,是最经常使用的系统提供的style,

文字阴影
给文字添加阴影效果需要使用android:shadowRadius android:shadowDy android:shadowDx属性。

3.5 文字内容修饰

android:autoLink:如果文字中包含了特定格式的字符串,那么会高亮这个字符串,并且让用户可以点击进行进一步的操作;这个属性有none web email phone map all等多个值选用,

3.6 配图的使用

android:drawableTop android:drawableBottom android:drawableLeft android:drawableRight属性可以在文字的上下左右位置设置一个Drawable(例如图片)。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

布局与控件 第2节 LinearLayout

更新时间 修改意见
2016-08-02 陈敏

第2节 LinearLayout

线性布局是安卓应用开发中最经常用到的布局之一。它能让放置与内部的控件或者子布局按照水平或竖直放置。

这里我们将介绍它最为常用的几个性质。

2.1 orientation属性

它可以让内部的控件或者子布局按照水平或者竖直方向,呈线性排列。设置android:orientation为:

  1. vertical:按照竖直方向排列;
  2. horizontal:安卓水平方向排列;

2.2 layout_weight属性

在LinearLayout包含的子布局(或者控件)中,使用这个属性,就能告诉LinearLayout,子布局(或者控件)的权重有多大,然后按照权重的比例将剩余的空间分配给它们。

举个例子:有个工地,长和宽都是100dp,它用围墙围起来,里面竖着修房子。

  1. 第一间房要修30dp长,但是设置了layout_weight2;第二间房要修20dp长,但是设置了layout_weight1;第三间要修10dp长,但是设置了layout_weight1

    因为每个房间都设置了高度,那么这个工地最后会被分割成100 - 30 - 20 - 10,还剩下40pd的长度。

    因为都设置了layout_weight,那么这剩下的长度,将会被按照比例分配给它们。
    第一间房,额外分配 2/(2+1+1) * 40 = 20dp
    第二间房,额外分配 1/(2+1+1) * 40 = 10dp
    第三间房,额外分配 1/(2+1+1) * 40 = 10dp

    最后就是,
    第一间房 30+20 = 50dp;
    第二间房 20+10 = 30dp;
    第三间房 10+10 = 20dp;

  2. 如果我把每个房间的高度设置成0dplayout_weight都设置成1

    那么就剩下100 - 0 - 0 - 0 = 100的高度等待分配;
    第一间房,额外分配 1/(1+1+1) * 100 = 33dp
    第二间房,额外分配 1/(1+1+1) * 100 = 33dp
    第三间房,额外分配 1/(1+1+1) * 100 = 33dp

    最后就是,
    第一间房 0+33 = 33dp;
    第二间房 0+33 = 33dp;
    第三间房 0+33 = 33dp;

  3. 如果我把每个房间的高度设置成match_parentlayout_weight设置成1

    那么就剩下100 - 100 - 100 - 100 = -200的高度等待分配;
    第一间房,额外分配 1/(1+1+1) * (-200) = -66dp
    第二间房,额外分配 1/(1+1+1) * (-200) = -66dp
    第三间房,额外分配 1/(1+1+1) * (-200) = -66dp

    最后就是,
    第一间房 100+(-66) = 33dp;
    第二间房 100+(-66) = 33dp;
    第三间房 100+(-66) = 33dp;

2.3 weightSum属性

这个属性用来控制各个子布局(或者控件)的权重之和。默认情况下,权重值为各个子控件的权重之和,但是如果设置了这个属性,那么就以这个设定的值为准,将父LinearLayout设置android:weightSum属性,值为2

剩下的长度100dp,将会被按照比例分配。
第一间房,额外分配 1/2 * 100 = 50dp(1/2是因为weightSum被设置成了2)
因为只有一间房,所以剩下的空间不用分配了

最后就是,
第一间房 0+50 = 50dp;
还剩下50dp的空间不做任何分配;

weightSum通常在布局占位的情况下使用。比如,LinearLayout中,只包含一个TextView控件,而又只希望TextView只占用LinearLayout上半空间,那么就可以这样设计,


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

布局与控件 第1节 布局与控件的常用概念

更新时间 修改意见
2016-08-02 陈敏

第1节 布局与控件的常用概念

界面设计中的控件,就是我们常常看到的按钮 滑动条 文字显示区等等,它们就像房间里的家具,是界面设计的最小单位。

布局是一个可以容纳别的布局(或者控件)的容器。它就像是一个大的房间,房间里面可以放各种家具(控件),也可以再隔离成更多的房间(放入别的布局)。

不过,两者有很多共同的地方,例如定义它们的大小、边距等等。

1.1 尺寸单位

在使用布局或控件时,有时需要指定它们的尺寸。安卓系统提供了三种单位:

  1. px:以像素为单位进行设置,屏幕上每一个点,就是一个像素,例如一部安卓手机,屏幕像素是1920 x 1080,就是说屏幕高度有1920个像素点,宽度有1080个像素点,但是在安卓系统中,最好不要使用这个单位来设置尺寸,而是用dp
  2. sp:用于字体大小的设置,它可以让字体大小根据用户在设置中的设置,进行缩放;
  3. dp:密度无关像素,这是在布局和控件中应该使用的单位;

1.1.1 dp的引入

如果用px为单位设定设备区域的大小,通常会有很大的问题。

假设有两个物理尺寸都为5寸大小的屏幕,它们的屏幕分辨率不同,一个是1080*1920,而另一个却是450*800.

如果指定一个按钮的大小为200px长,200px宽,那么它们的显示效果就如下图。很明显,两者的差别也太大了,界面就严重变形了。所以,想让一个区域的大小在不同屏幕的上显示的都差不多,就需要考虑像素密度。为了显示同样的长度,在密度大的屏幕上,使用到的像素就要多一些,在密度小的屏幕上,使用到的像素就要少一些。

所以为了解决这个问题,安卓引入了dip的概念-device independed pixel-简称dp。

1.1.2 dpi的定义

在近一步介绍dp之前,要先讲讲dpidpi叫做屏幕像素密度,就是每英寸有多少个像素点。我们用简单的数学原理\[ dpi = \dfrac{\sqrt{长^2 + 宽^2}}{对角线尺寸} \]就能算出刚才两个屏幕的dpi,一个是440dpi,另一个是184dpi

注意,dpidip(dp)写法很像,千万不要混淆了,前者和密度有关,后者与像素有关。

1.1.3 dpi的划分

安卓使用dpi为160的数值作为一个基准--baseline,

  1. 如果一个设备的dpi刚好等于这个基准,那么它就是mdpi设备;
  2. 如果一个设备的dpi是这个基准的1.5倍,那么它就是hdpi设备;
  3. 如果一个设备的dpi是这个基准的2倍,那么它就是xhdpi设备;
  4. 如果一个设备的dpi是这个基准的3倍,那么它就是xxhdpi设备;

这就是划分不同屏幕密度的依据。

像素密度类型 像素密度大小 与Baseline比值
mdpi 160 1
hdpi 240 1.5
xhdpi 320 2
xxhpi 480 3

按照这样的约定划分,

  1. 刚才1080*1920的屏幕应该属于xxhdpi类型;
  2. 450*800的屏幕应该属于mdpi类型;

现在的安卓手机几乎都在往高清屏幕发展,所以xhdpi和xxhdpi是最为常见的设备类型。

屏幕的dpi计算出来不一定就是基准(160)的整数倍,而且安卓系统也不一定会使用我们计算出来的那个值作为设备的dpi。

设备通常会在系统中指定一个接近那个值的dpi值,例如,你的Nexus5,虽然计算出来是440,但系统实际上给它指定的是480,刚好是基准的3倍。

在系统的目录的/system/build.prop文件中,可以看到,

1.1.4 dp与px的关系

安卓引入了dp(dip)的概念之后,实际显示的像素就可以通过下面这个公式计算出来\[ px = dp*\dfrac{dpi}{160} \]

相同dp的情况下,密度大的设备,实际像素就占的大,密度小点设备,实际像素就占用小了。因此使用dp这个单位后,按钮在两种不同的屏幕上的实际显示大小,几乎就是一样的了。

例如刚才两种屏幕。如果我指定按钮的大小为200dp*200dp,那么它们在各自的屏幕上占用的实际像素分别是230px*230px与550px*550px,真实设备上看上去的确差不多。

1.2 大小设置

在设置布局或者控件大小的时候,会使用它们的android:layout_widthandroid:layout_height属性,

它们的值可以设定成,

  1. 特定的数值:例如5dp。当然也可以使用除了dp以外其他的尺寸单位,但是考虑到屏幕的像素密度不同,我们都使用dp为单位;
  2. match_parent:以父布局的宽度(或长度)为界,尽可能占据全部空间;
  3. wrap_content:以子布局或控件内容的宽度(或长度)为界,尽可能少占据空间;

1.3 边距

  1. margin:控件或布局相对外面组件的边距叫做margin,例如一个LinearLayout与它相邻的另一个FrameLayout之间的间隔。在布局或控件中,使用以下属性来定义,

  2. padding:控件或布局为内部区域保留的边距叫做padding,例如一个LinearLayout与它内部的一个FrameLayout之间的间隔,或者TextView的边框与它内部文字之间的间隔。在布局或控件中,使用以下属性来定义,

1.4 可见性

布局和控件都还有个常用的android:visiblility属性,

它有3个可选的参数,

  1. visible:这个控件或者布局是可见的;
  2. invisible:这个控件或者布局是不可见的,但是它的位置还在那里,只是界面上看不到它,别的控件或布局还是需要考虑它的位置;
  3. gone:这个控件或者布局不仅不可见,而且它的位置已经不在那里了,在显示的时候,别的控件或布局都不用考虑它的位置了;

默认情况下,如果不指定这个属性,系统通常会为它们使用visible参数。

1.5 位置

无论控件还是布局都可以设置它本身相对于父布局的偏好位置,例如是居中还是靠左。例如一个放到LinearLayout中的Button,它希望自己位于父布局内部靠上的位置,就可以添加android:layout_gravity属性,设置值为top

其它可以设置的值还有bottom left right center center_horizontal center_vertical

实际效果也要看父布局的类型,比如在上面的例子中,子控件的位置属性只是向LinearLayout提出请求,但LinearLayout还是要从自己的实际情况出发--它不一定能满足所有子控件或者布局的要求。

比如这里使用bottom值,就是没有意义的。因为LinearLayout本来就是要求子布局(或控件)从上到下进行排列。所以即使设定了bottom值,Button还是会在上方。

控件或者布局,也可以设置自己的内容相对于内部的位置。例如上面的例子中,要将Button放到LinearLayout底部靠右的位置,可以为其android:gravity属性设置bottom|right。这里的|表示并且

这里可以让LinearLayout从下方开始排列子控件的原因是:这是LinearLayout自己的属性,而不是听子控件的安排。android:gravity属性告诉了LinearLayout,在排列时优先放下边、右边。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/