新書推薦:
《
石油帝国的兴衰:英国的工业化与去工业化
》
售價:NT$
445.0
《
古典的回響:溪客舊廬藏明清文人繪畫
》
售價:NT$
1990.0
《
根源、制度和秩序:从老子到黄老学(王中江著作系列)
》
售價:NT$
550.0
《
索恩丛书·北宋政治与保守主义:司马光的从政与思想(1019~1086)
》
售價:NT$
345.0
《
掌故家的心事
》
售價:NT$
390.0
《
农为邦本——农业历史与传统中国
》
售價:NT$
340.0
《
郊庙之外:隋唐国家祭祀与宗教 增订版 (三联·哈佛燕京学术丛书)
》
售價:NT$
480.0
《
小麦文明:“黄金石油”争夺战
》
售價:NT$
445.0
內容簡介:
本书以最新Android?5.X进行开发示范,让读者可以快速开发智能手机、平板电脑的应用程序。全书共分为14章,内容包括Android基础、开发工具的下载与安装、Android项目与系统架构、UI基本设计、UI高级设计、Activity与Fragment、数据存取、移动数据库SQLite、Google地图、传感器的应用、多媒体与相机功能、AdMob广告的制作以及发布应用程序到Play商店等,使读者不仅可以从销售应用程序而获利,而且可以在面试工作时展示自己的作品。
本书适合Android初学者、在职开发人员、游戏开发爱好者、程序员阅读参考,也可作为大中专院校相关专业的学习用书和培训学校的教材。
關於作者:
黄彬华
学历
英国Essex大学Computer Science硕士
经历
台湾科技大学、台中科技大学 兼任业师
台中科大、联合大学业界讲师
健行、开南、万能等大学资管系兼任讲师
奇科计算机、资策会、自强基金会、巨匠计算机等教育训练中心 兼任讲师
宏达国际电子股份有限公司(HTC)智能型手机项目经理
宝成国际供应链研发中心 课长级系统分析师
映管股份有限公司 CIM工程师
著作:
Android 2.X手机程序开发教战手册
SCJP 6.0认证教战手册
第三版 第二版 第一版
可视化Visual
Basic的易想世界
可视化Java的易想世界
SCJP认证应试教战手册
目錄 :
目 录
第1章 Android导论
1-1 认识Android
1-1-1 Android属于Linux移动平台
1-1-2 Android历史
1-1-3 版本更新过程
1-1-4 开放手机联盟的介绍
1-2 Android成功的原因
1-2-1 开放源代码与采用Apache授权方式
1-2-2 Android向Java招手
1-3 Google Play的介绍与获利实例
1-3-1 Google Play的介绍
1-3-2 Android应用程序能否获利
第2章 开发工具的下载与安装
2-1 开发工具的下载与安装
2-1-1 JDK下载、安装与设置
2-1-2 Android Studio下载与安装
2-2 Android各版本的市场占有率
第3章 Android项目与系统架构
3-1 管理Android项目
3-1-1 创建Android项目
3-1-2 打开已有的Android Studio项目
3-1-3 导入官方范例程序
3-1-4 导入非Android Studio项目
3-1-5 关闭项目
3-2 管理Android仿真器
3-2-1 建立Android仿真器
3-2-2 运行Android项目
3-2-3 删除Android应用程序
3-2-4 DDMS使用
3-3 Android系统架构介绍
3-4 Android项目的目录与结构
3-4-1 manifest文件
3-4-2 java与res目录
3-4-3 Android项目架构
3-5 应用程序本地化
第4章 UI(用户界面)设计的基本概念
4-1 Android UI设计的基本概念
4-1-1 Android Layout Editor
4-1-2 非程序资源
4-2 UI事件处理
4-2-1 按钮单击事件处理Java传统型
4-2-2 按钮单击事件处理Android简易型
4-3 layout组件介绍
4-3-1 常用layout组件的说明
4-3-2 ScrollView与HorizontalScrollView
4-4 style与theme
4-4-1 定义style
4-4-2 继承style
4-4-3 套用theme
4-4-4 继承theme
4-5 触控与手势
4-5-1 触击事件处理
4-5-2 手势
4-6 常用UI组件
4-6-1 WebView
4-6-2 RatingBar
4-6-3 SeekBar
4-6-4 CompoundButton
4-7 Menu
第5章 UI高级设计
5-1 Spinner
5-2 AutoCompleteTextView
5-3 ListView
5-4 GridView
5-5 CardView与RecyclerView
5-6 自定义View组件与2D绘图
5-7 Frame Animation
5-8 Tween Animation
第6章 Activity与Fragment
6-1 Activity生命周期
6-2 Activity之间数据的传递
6-2-1 传递基本数据类型
6-2-2 传递对象类型
6-3 Fragment UI设计概念
6-3-1 Fragment生命周期
6-3-2 页面分割
6-4 DialogFragment
6-4-1 AlertDialog
6-4-2 DatePickerDialog与TimePickerDialog
6-5 ViewPager
第7章 Notification,
Broadcast, Service
7-1 Notification(通知信息)
7-2 Broadcast(广播)
7-2-1 拦截Broadcast
7-2-2 自行发送与拦截Broadcast
7-3 Service生命周期
7-3-1 调用startService启动Service
7-3-2 调用bindService绑定Service
7-3-3 IntentService
第8章 数据存取
8-1 Android数据存取概论
8-2 Assets
8-3 Shared Preferences
8-4 Internal Storage
8-5 External Storage
第9章 移动数据库SQLite
9-1 SQLite数据库概论与数据类型
9-1-1 SQLite数据库概论
9-1-2 SQLite数据类型
9-2 使用命令行创建数据库
9-3 SQL语言
9-3-1 创建数据表
9-3-2 DML语句
9-4 应用程序访问SQLite数据库
9-4-1 插入功能
9-4-2 更新功能
9-4-3 删除功能
9-4-4 查询功能
9-5 查询联系人数据
第10章 Google地图
10-1 Google地图功能的介绍
10-2 产生数字证书指纹
10-3 申请API密钥
10-4 Google Play Services安装与导入
10-5 创建基本的Google地图
10-6 地图种类与UI设置
10-6-1 地图种类设置
10-6-2 地图UI设置
10-7 使用标记与设置镜头焦点
10-7-1 使用标记
10-7-2 信息窗口
10-7-3 标记事件处理
10-7-4 镜头设置
10-8 绘制连续线、多边形与圆形
10-8-1 连续线(Polyline)
10-8-2 多边形(Polygon)
10-8-3 圆形(Circle)
10-9 地名或地址转成位置
10-10 位置信息的应用
10-10-1 定位(Fix)
10-10-2 更新位置
10-10-3 计算两点间的距离
10-10-4 导航功能
第11章 传感器的应用
11-1 传感器的介绍
11-2 加速度传感器
11-3 陀螺仪传感器
11-4 方位传感器
11-5 接近传感器
11-6 亮度传感器
第12章 多媒体与相机功能
12-1 Android多媒体功能介绍
12-2 播放Audio文件
12-2-1 播放资源文件
12-2-2 播放外部文件
12-3 Video播放器
12-4 录制Audio文件
12-5 拍照与选取照片
12-5-1 拍照
12-5-2 选取照片
12-6 录制Video文件
第13章 AdMob广告的制作
13-1 AdMob简介
13-2 注册AdMob账户
13-3 创建广告单元并获取编号
13-4 将移动广告集成到应用程序
13-4-1 Google Play Services安装与导入
13-4-2 设置Android项目的manifest文件
13-4-3 使用AdView加入横幅广告
第14章 发布应用程序到Play商店
14-1 将应用程序发布到Play商店
14-2 产生并签署应用程序
14-3 申请Android开发者账号
14-4 使用开发者管理控制台发布应用程序
14-4-1 应用程序首次发布
14-4-2 应用程序改版
內容試閱 :
第5章 UI高级设计
5-1 Spinner
Spinner是一个非常类似于下拉列表(drop-down list)的UI组件,其优点是节省显示空间,因为用户尚未单击时,仅显示一组数据。如同其他UI组件,可以通过layout文件创建Spinner组件。Spinner与之后的AutoCompleteTextView、ListView、GridView都是属于使用Adapter来设置选项内容的AdapterView,所以如何使用Adapter来完成选项内容的设置必须十分熟悉。
范例
SpinnerDemo
范例说明:
单击任何一个Spinner组件都会将被选取的选项文字显示出来,如图5-1所示。
图5-1 范例SpinnerDemo将被单击的Spinner组件对应的选项文字显示出来
创建步骤:
使用layout文件创建Spinner,选项部分可以使用静态文本文件创建字符串数组成为选项文字。
SpinnerDemo res layout
main_activity.xml
Spinner
android:id="@ idspFood"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@arrayfood_array"
SpinnerDemo res values
strings.xml
string-array
name="food_array"
itemspaghettiitem
itemdumplingitem
itemsushiitem
string-array
调用findViewById找到layout文件上的Spinner,注册OnItemSelectedListener监听器,当用户改变选项时会调用onItemSelected。除了步骤1使用文本文件来创建选项文字外,也可以通过程序代码创建字符串数组供ArrayAdapter使用。
SpinnerDemo java MainActivity.java
public class MainActivity extends
ActionBarActivity {
private TextView tvMessage;
@Override
public
void onCreateBundle savedInstanceState {
super.onCreatesavedInstanceState;
setContentViewR.layout.main_activity;
findViews;
}
private void findViews {
tvMessage = TextView findViewByIdR.id.tvMessage;
Spinner spFood = Spinner
findViewByIdR.id.spFood;
注册OnItemSelectedListener监听器之前先调用setSelection,不仅可以设置开始的预选项目,还可避免Spinner开始就执行OnItemSelectedListener.onItemSelected的问题
spFood.setSelection0, true;
注册OnItemSelectedListener监听器,当用户改变选项时会调用onItemSelected
spFood.setOnItemSelectedListenerlistener;
Spinner spPlace = Spinner
findViewByIdR.id.spPlace;
调用ArrayAdapter构造函数以创建选项的内容与样式。选项的内容来自于places字符串数组;样式则套用系统内置的android.R.layout.simple_spinner_item;
String[] places = {"Australia",
"U.K.", "Japan", "Thailand"};
ArrayAdapterString adapterPlace
= new ArrayAdapterthis,
android.R.layout.simple_spinner_item, places;
调用setDropDownViewResource套用系统内置的下拉菜单样式android.R.layout.
simple_spinner_dropdown_item
adapterPlace
.setDropDownViewResourceandroid.R.layout.simple_spinner_dropdown_item;
Spinner调用setAdapter套用指定的Adapter以加载对应的选项内容与样式。ArrayAdapter是Adapter子类,如果选项只想显示文字,使用ArrayAdapter比较方便
spPlace.setAdapteradapterPlace;
spPlace.setSelection0, true;
spPlace的事件处理与前面的spFood相同,所以可以注册相同的监听器
spPlace.setOnItemSelectedListenerlistener;
}
实现OnItemSelectedListener,当用户改变Spinner选项时会自动调用onItemSelected,parent代表触发事件的Spinner;pos代表被选取项目的索引。调用getItemAtPosition搭配pos参数可以获取设置Spinner选项文字的字符串数组指定的字符串,换句话说就是可以获取选项上面的文字
Spinner.OnItemSelectedListener listener = new
Spinner.OnItemSelectedListener {
@Override
public void onItemSelected
AdapterView? parent,
View view, int pos, long id {
tvMessage.setTextparent.getItemAtPositionpos.toString;
}
@Override
public void onNothingSelectedAdapterView? parent {
tvMessage.setText"Nothing selected!";
}
};
}
5-2 AutoCompleteTextView
AutoCompleteTextView非常类似于EditText,属于文字输入框;不过AutoCompleteTextView会在用户输入几个文字时显示提示文字,方便用户选取而无需输入所有的文字,这是一种体贴用户输入的设计。
AutoCompleteTextView的提示列表与Spinner的选项列表创建方式相同,需要创建字符串数组来存储要提示的文字。
范例AutoCompleteTextViewDemo
范例说明:
输入T,应用程序会进行对比,并自动将符合的提示文字以列表方式显示出来,以便用户选取输入,如图5-2所示。
图5-2 范例程序演示以列表方式显示符合的提示文字供用户选取输入
创建步骤:
使用layout文件创建AutoCompleteTextView,completionThreshold属性设置输入多少个字符才会显示提示文字,如果未设置则默认为两个字符。
AutoCompleteTextViewDemo res layout
main_activity.xml
AutoCompleteTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@ idactvCountry"
android:hint="@stringtext_actvCountry"
android:completionThreshold="1"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="84dp"
调用findViewById找到layout文件上的AutoCompleteTextView。创建ArrayAdapter并以字符串数组存储提示列表上的文字;AutoCompleteTextView再套用此ArrayAdapter。最后AutoCompleteTextView注册OnItemClickListener监听器,当用户选择提示列表上的文字时会调用onItemClick。
AutoCompleteTextViewDemo java
MainActivity.java
public class MainActivity extends
ActionBarActivity {
@Override
protected void onCreateBundle savedInstanceState {
super.onCreatesavedInstanceState;
setContentViewR.layout.main_activity;
final String[] countries = {
"CANADA", "CHINA",
"FRANCE", "GERMANY",
"ITALY",
"JAPAN", "KOREA", "Greece", "UK",
"US"
};
AutoCompleteTextView actvCountry =
AutoCompleteTextView
findViewByIdR.id.actvCountry;
R.layout.list_item是自行创建的layout文件,当作选项内容的样式
ArrayAdapterString arrayAdapter
=
new
ArrayAdapterthis,
R.layout.list_item, countries;
actvCountry.setAdapterarrayAdapter;
AutoCompleteTextView注册OnItemClickListener监听器,当用户选择提示列表上的文字时会调用onItemClick,此时调用getItemAtPosition获取用户选取的文字并以Toast消息框显示
actvCountry.setOnItemClickListenernew
AdapterView.OnItemClickListener {
@Override
public void onItemClick
AdapterView?
parent, View view, int position, long id {
String item =
parent.getItemAtPositionposition.toString;
Toast.makeText
MainActivity.this,
item,
Toast.LENGTH_SHORT
.show;
}
};
}
}
5-3 ListView
ListView组件属于AdapterView,以列表方式显示内容,如果内容过长,用户可以滚动画面进行浏览,此组件非常适合用来显示大量数据。ListView的每一行数据都是一个选项,而这些选项内容是由Adapter动态加载layout文件,再将数据来源(List或数组)的数据取出后设置在layout文件的各个UI组件上;换句话说,Adapter负责管理ListView选项的内容(包含值与样式),这也是所有AdapterView组件的特色。当ListView数据内容有变时,开发者可以调用BaseAdapter.notifyDataSetChanged来刷新画面。
范例
ListViewDemo
范例说明:
l
主页面有两个UI组件:TextView显示会员标题文字与ListView显示各个会员资料。
l ListView每一个选项,需要另外加载layout文件来设置图片与文字等内容。此例所载入的layout文件中有一个ImageView用来显示会员照片;两个TextView分别显示会员ID与会员姓名,如图5-3所示。
l
单击选项后会以Toast(指简易消息框)方式显示对应文字。
图5-3 范例ListViewDemo演示ListView UI组件的功能
创建步骤:
创建主页面的layout文件,并在其中创建ListView。
ListViewDemo res layout
main_activity.xml
LinearLayout
xmlns:android="http:schemas.android.comapkresandroid"
xmlns:tools="http:schemas.android.comtools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimenactivity_horizontal_margin"
android:paddingRight="@dimenactivity_horizontal_margin"
android:paddingTop="@dimenactivity_vertical_margin"
android:paddingBottom="@dimenactivity_vertical_margin"
tools:context=".MainActivity"
TextView
android:id="@ idtvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="@stringtvTitle"
ListView
android:id="@ idlvMember"
android:layout_width="match_parent"
android:layout_height="wrap_content"
LinearLayout
创建ListView各选项所需的layout文件。因为每一个选项的版面设置都是一样的,所以只要创建一个layout文件即可重复套用。在此例中,载入的layout文件listview_item.xml,其父组件为LinearLayout,所以其实加载的是LinearLayout;而三个子组件:一个ImageView用来显示会员照片;两个TextView分别显示会员ID与会员姓名。
ListViewDemo res layout
listview_item.xml
LinearLayout xmlns:android="http:schemas.android.comapkresandroid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
ImageView
android:id="@ idivImage"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginLeft="10dp"
android:padding="6dp"
TextView
android:id="@ idtvId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="6dp"
TextView
android:id="@ idtvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="6dp"
LinearLayout
创建BaseAdapter子类(例如MemberAdapter),并改写下列4个方法以便提供选项的内容。
l
public int getCount:提供选项的总数。
l
public Object getItem(int position):根据索引位置(position)提供该选项对应的对象,这里提供Member对象(会员对象)。
l
public long getItemId(int position):根据索引位置提供该选项对应的ID,这里提供Member ID(会员代号)。
l
public view getView(int position, View convertView, parent):根据索引位置提供该选项对应的View给用户查看。
ListViewDemo java MainActivity.java
... 尚有其他程序
private
class MemberAdapter extends BaseAdapter {
private LayoutInflater layoutInflater;
private ListMember memberList;
public MemberAdapterContext context {
获取LayoutInflater对象以便之后动态加载layout文件供选项使用
layoutInflater =
LayoutInflater.fromcontext;
memberList是此ListView的数据来源,而Member类定义着会员资料如会员ID、照片、姓名
memberList = new ArrayList;
memberList.addnew Member23,
R.drawable.p01, "John";
memberList.addnew Member75, R.drawable.p02, "Jack";
memberList.addnew Member65, R.drawable.p03, "Mark";
memberList.addnew Member12, R.drawable.p04, "Ben";
memberList.addnew Member92, R.drawable.p05, "James";
memberList.addnew Member103, R.drawable.p06, "David";
memberList.addnew Member45, R.drawable.p07, "Ken";
memberList.addnew Member78, R.drawable.p08, "Ron";
memberList.addnew Member234, R.drawable.p09, "Jerry";
memberList.addnew Member35, R.drawable.p10, "Maggie";
memberList.addnew Member57, R.drawable.p11, "Sue";
memberList.addnew Member61, R.drawable.p12, "Cathy";
}
提供选项的总数,系统会按照返回值来决定调用下面getView的次数
@Override
public int getCount {
return memberList.size;
}
根据position位置提供该选项对应的对象,在此返回代表会员的Member对象
@Override
public Object getItemint position {
return memberList.getposition;
}
根据position位置提供该选项对应的ID,在此返回会员ID
@Override
public long getItemIdint position {
return
memberList.getposition.getId;
}
getView是根据position位置提供该选项对应的View。
开始画面尚未显示时,converView为null,调用inflate载入R.layout.listview_item文件其实就是载入LinearLayout这个View。
画面显示时,用户可以看到ListView画面,当用户向下滑动一行,原本第一行会被滑出画面,被滑出选项的View会自动传递给convertView,所以不会为null,可以重复利用该View,只要将值替换成滑入选项的值即可。
@Override
public View getViewint position, View
convertView, ViewGroup parent {
if convertView == null {
convertView =
layoutInflater.inflateR.layout.listview_item, parent, false;
}
按照position获取memberList内的member对象
Member member =
memberList.getposition;
找到convertView子组件imageView,并指定要显示的图片
ImageView ivImage =
ImageView convertView
.findViewByIdR.id.ivImage;
ivImage.setImageResourcemember.getImage;
找到convertView子组件textView,并显示会员ID与姓名
TextView tvId = TextView convertView
.findViewByIdR.id.tvId;
tvId.setTextString.valueOfmember.getId;
TextView tvName = TextView convertView
.findViewByIdR.id.tvName;
tvName.setTextmember.getName;
在此范例,返回convertView其实就是返回LinearLayout(参看listview_item.xml)
return convertView;
}
}
ListView调用setAdapter套用BaseAdapter对象。注册OnItemClickListener监听器,当用户单击选项时会调用onItemClick。
ListViewDemo java MainActivity.java
public class MainActivity extends
ActionBarActivity {
@Override
public void onCreateBundle savedInstanceState {
super.onCreatesavedInstanceState;
setContentViewR.layout.main_activity;
ListView调用setAdapter并套用创建好的MemberAdapter
ListView lvMember = ListView
findViewByIdR.id.lvMember;
lvMember.setAdapternew
MemberAdapterthis;
ListView注册OnItemClickListener监听器,当用户单击选项时会调用onItemClick。
Parent被单击的ListView
View被单击的选项所加载的layout内容,在此为listview_item.xml内的LinearLayout组件
Position被单击的索引位置
Id实现BaseAdapter.getItemId所返回的ID
lvMember.setOnItemClickListenernew
AdapterView.OnItemClickListener {
@Override
public void
onItemClickAdapterView? parent, View view,
int
position, long id {
调用getItemAtPosition会获取BaseAdapter.getItem所返回的对象,在此为会员对象,之后以Toast方式显示此会员的相关信息
Member member = Member
parent.getItemAtPositionposition;
String text = "ID =
" member.getId
", name = "
member.getName;
Toast.makeTextMainActivity.this,
text, Toast.LENGTH_SHORT.show;
}
};
}
... 尚有其他程序
}
5-4 GridView
GridView以网格(grid)方式显示数据,与ListView以列表方式显示有所不同。除此之外,无论是使用BaseAdapter加载选项内容的方式,还是使用单击选项后的事件处理方式以及刷新画面的方式,则是完全相同的,所以请直接参看第5-3节有关 ListView的说明,这里不再赘述。
范例GridViewDemo
范例说明:
l
主页面有两个UI组件:TextView显示会员标题文字,GridView显示各个会员资料。
l
GridView每一个选项网格,需要另外加载layout文件来设置图片与文字等内容。此例所载入的layout文件中有一个ImageView是用来显示会员照片;两个TextView分别显示会员ID与会员姓名,如图5-4所示。
l
单击选项网格后会以Toast方式显示对应图片。
创建步骤:
创建方法完全与前述ListView相同,这里不再赘述。唯一不同的地方是单击选项网格时,在此范例中会以Toast方式(即简易消息框的方式)显示图片,而之前仅以Toast方式显示文字,说明如下。
GridViewDemo java MainActivity.java
public class MainActivity extends
ActionBarActivity {
@Override
public
void onCreateBundle savedInstanceState {
super.onCreatesavedInstanceState;
setContentViewR.layout.main_activity;
GridView gvMember = GridView findViewByIdR.id.gvMember;
gvMember.setAdapternew MemberAdapterthis;
gvMember.setOnItemClickListenernew
AdapterView.OnItemClickListener {
@Override
public void onItemClickAdapterView?
parent, View view,
int
position, long id {
Member member = Member parent.getItemAtPositionposition;
创建ImageView并放上会员照片;再创建Toast并调用setView套用该ImageView即可显示照片
ImageView imageView = new
ImageViewMainActivity.this;
imageView.setImageResourcemember.getImage;
Toast toast = new
ToastMainActivity.this;
toast.setViewimageView;
toast.setDurationToast.LENGTH_SHORT;
toast.show;
}
};
}
...
}
5-5 CardView与RecyclerView
Android 5.0时发表了两个新UI组件:CardView与RecyclerView,它们都属于support函数库的成员,所以可以向前兼容。换句话说,旧版的Android设备也可以显示这两种UI组件。CardView是FrameLayout的子类,特色是可以设置圆角与阴影程度;而RecyclerView则非常类似于ListView GridView,以有限的窗口大小显示大量数据。可以将CardView置入RecyclerView内实现更丰富的显示样式,如图5-5所示。
图5-5 CardView可以实现更丰富的显示样式
CardView
CardView属于FrameLayout,但比原来的FrameLayout多了圆角与阴影两种设置。虽然CardView是最近才发布的UI组件,但从其完整的名称android.support.v7.widget.CardView可知它属于support函数库,所以可以向前兼容。关于CardView的两个重要设置说明如下。
l
圆角设置:可以设置FrameLayout边角的圆弧程度,在layout文件使用cardCornerRadius属性;在程序代码则调用setRadius来设置。
l
阴影设置:可以设置FrameLayout周围的阴影程度,在layout文件使用cardElevation属性;在程序代码则调用setMaxCardElevation。
RecyclerView
RecyclerView就如其英文名字的含义,它会自动重复利用选项的View来显示新的且相同样式的选项。例如,画面上只能显示10个选项,当用户滑动到第11个选项时,比较好的做法是将第1个选项的View放入缓存区以便腾出屏幕位置给滑进来的第11个选项使用,因为它们的样式一样,只不过值不同而已。这样就可以比较有效地利用内存中已存放的数据,而且可以提升执行效率。
RecyclerView最大特色就是将layout设置抽离出来,可以直接调用setLayoutManager设置layout样式。如果搭配LinearLayoutManager,显示的样子就会几乎跟ListView一样;如果搭配GridLayoutManager,就会如同GridView一样。最有趣的是StaggeredGridLayoutManager,其样子像GridView,但是可以水平滑动。
RecyclerView无法像ListViewGridView一样注册OnItemClickListener。如果仍然想要监听选项是否被单击了,可以为选项的View注册我们熟悉的OnClickListener并通过getAdapterPosition来获取被单击项目的位置。
当数据内容发生变化时,开发者可以调用RecyclerView.Adapter.notifyDataSetChanged来刷新画面。
范例
RecyclerCardViewDemo
范例说明:
l
主页面有两个UI组件:TextView显示会员标题文字,RecyclerView显示各个会员资料。
l
RecyclerView每一个选项网格,需要另外加载layout文件来设置图片与文字等内容。此例所载入的layout文件内有一个ImageView用来显示会员照片;两个TextView分别显示会员ID与会员姓名。
l
用户可以左右滑动RecyclerView,单击选项网格后会以Toast方式显示对应图片,范例效果如图5-6所示。
图5-6 范例 RecyclerCardViewDemo演示RecyclerView的功能
创建步骤:
在build.gradle文件内添加cardview与recyclerview套件:Android Studio主菜单File Project Structure app
Dependencies 单击添加按钮,以便添加com.android.support:cardview与com.android.support:recyclerview,如图5-7所示。
图5-7 在build.gradle文件内添加cardview与recyclerview套件
创建主页面的layout文件,并在其中创建RecyclerView。因为RecyclerView不属于android.widget套件,所以必须输入完整的名称android.support.v7.widget.RecyclerView。
RecyclerCardViewDemo res layout
main_activity.xml
LinearLayout
xmlns:android="http:schemas.android.comapkresandroid"
xmlns:tools="http:schemas.android.comtools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimenactivity_horizontal_margin"
android:paddingRight="@dimenactivity_horizontal_margin"
android:paddingTop="@dimenactivity_vertical_margin"
android:paddingBottom="@dimenactivity_vertical_margin"
tools:context=".MainActivity"
TextView
android:id="@ idtvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="@stringtvTitle"
android.support.v7.widget.RecyclerView
android:id="@ idrecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
LinearLayout
创建RecyclerView选项所需的layout文件。因为每一个选项样式都相同,所以只要创建一个layout文件即可重复套用。在此例中为了要搭配CardView,所以创建CardView组件;因为也不属于android.widget套件,所以也必须使用完整的名称android.support.v7.widget.CardView。因为CardView使用了不同的名称空间,所以必须加入"http:schemas.android.comapkres-auto",并以card_view名称表示。CardView重要属性有:cardBackgroundColor用来设置背景色,cardCornerRadius设置圆角弧度,cardElevation设置阴影。
RecyclerCardViewDemo res layout
recyclerview_cardview_item.xml
android.support.v7.widget.CardView
xmlns:android="http:schemas.android.comapkresandroid"
xmlns:card_view="http:schemas.android.comapkres-auto"
android:id="@ idcardview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp"
card_view:cardBackgroundColor="#ffdddddd"
card_view:cardCornerRadius="28dp"
card_view:cardElevation="6dp"
android:layout_margin="6dp"
LinearLayout
xmlns:android="http:schemas.android.comapkresandroid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
ImageView
android:id="@ idivImage"
android:layout_width="120dp"
android:layout_height="160dp"
android:layout_marginLeft="16dp"
LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
TextView
android:id="@ idtvId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginBottom="12dp"
TextView
android:id="@ idtvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:layout_marginBottom="12dp"
LinearLayout
LinearLayout
android.support.v7.widget.CardView
创建RecyclerView.Adapter子类(例如MemberAdapter),并在其中创建RecyclerView.ViewHolder子类(例如MemberAdapter.ViewHolder),ViewHolder目的在于暂存RecyclerView选项的View,以便之后相同样式的选项重复利用。另外RecyclerView.Adapter子类还需要改写(override)下列3个方法,以便提供选项内容。
l
public int getItemCount:提供RecyclerView选项的总数。
l public ViewHolder onCreateViewHolder(ViewGroup
viewGroup, int viewType):当RecyclerView需要一个View来显示特定选项内容时会调用此方法,此时要提供一个View给ViewHolder保存着,然后返回这个ViewHolder让RecyclerView使用。
l public void onBindViewHolder(ViewHolder viewHolder, int position):要显示RecyclerView特定位置(position)的选项内容时会调用此方法,此时要将ViewHolder内的各个View设置好要显示的数据。
RecyclerCardViewDemo java
MainActivity.java
...
private class MemberAdapter extends
RecyclerView.AdapterMemberAdapter. ViewHolder {
private Context context;
private LayoutInflater layoutInflater;
private ListMember memberList;
public MemberAdapterContext context {
this.context = context;
layoutInflater = LayoutInflater.fromcontext;
memberList是数据来源,而Member类定义着会员资料如会员ID、照片、姓名
memberList = new ArrayList;
memberList.addnew Member92,
R.drawable.p05, "James";
memberList.addnew Member103, R.drawable.p06, "David";
memberList.addnew Member234, R.drawable.p09, "Jerry";
memberList.addnew Member35,
R.drawable.p10, "Maggie";
memberList.addnew Member23, R.drawable.p01, "John";
memberList.addnew Member75, R.drawable.p02, "Jack";
memberList.addnew Member65, R.drawable.p03, "Mark";
memberList.addnew Member12, R.drawable.p04, "Ben";
memberList.addnew Member45, R.drawable.p07, "Ken";
memberList.addnew Member78, R.drawable.p08, "Ron";
memberList.addnew Member57, R.drawable.p11, "Sue";
memberList.addnew Member61, R.drawable.p12, "Cathy";
}
ViewHolder目的在于暂存RecyclerView选项的View,便于之后重复利用
public class ViewHolder extends
RecyclerView.ViewHolder {
private ImageView ivImage;
private TextView tvId, tvName;
调用ViewHolder构造函数(constructor)必须提供RecyclerView选项所需要的View
public ViewHolderView itemView {
superitemView;
ivImage = ImageView itemView.findViewByIdR.id.ivImage;
tvId = TextView itemView.findViewByIdR.id.tvId;
tvName = TextView itemView.findViewByIdR.id.tvName;
RecyclerView无法像ListViewGridView同样注册OnItemClickListener;如果仍然想要监听选项是否被单击了,可以为选项的View注册OnClickListener并通过getAdapterPosition来获取被单击项目的位置,不过可能返回NO_POSITION,因此建议要检查。当开发者调用RecyclerView.Adapter.notifyDataSetChanged刷新画面,而选项的View没有实时传入就会导致getAdapterPosition返回NO_POSITION
itemView.setOnClickListenernew View.OnClickListener {
@Override
public void onClickView v
{
if
getAdapterPosition == RecyclerView.NO_POSITION {
Toast.makeText
context,
R.string.msg_ClickAgain,
Toast.LENGTH_SHORT
.show;
return;
}
单击选项会显示对应的会员照片
Member member =
memberList.getgetAdapterPosition;
ImageView imageView =
new ImageViewcontext;
imageView.setImageResourcemember.getImage;
Toast toast = new
Toastcontext;
toast.setViewimageView;
toast.setDurationToast.LENGTH_SHORT;
toast.show;
}
};
}
public ImageView getIvImage {
return ivImage;
}
public TextView getTvId {
return tvId;
}
public TextView getTvName {
return tvName;
}
}
提供RecyclerView选项的总数
@Override
public int getItemCount {
return memberList.size;
}
提供选项所需的View,可以通过LayoutInflater加载,通过调用ViewHolder构造函数将选项的View传给ViewHolder
@Override
public ViewHolder
onCreateViewHolderViewGroup viewGroup, int viewType {
View itemView = layoutInflater.inflate
R.layout.recyclerview_cardview_item, viewGroup, false;
return new ViewHolderitemView;
}
要显示RecyclerView特定position的数据时会调用此方法,开发者应该按照position提供Member对象,并将数据显示在ViewHolder指定的子View上
@Override
public void onBindViewHolderViewHolder
viewHolder, int position {
Member member =
memberList.getposition;
viewHolder.getIvImage.setImageResourcemember.getImage;
viewHolder.getTvId.setTextString.valueOfmember.getId;
viewHolder.getTvName.setTextmember.getName;
}
}
RecyclerView调用setLayoutManager套用RecyclerView.LayoutManager提供的版面设置样式。RecyclerView调用setAdapter套用RecyclerView.Adapter提供的选项设置。
RecyclerCardViewDemo java
MainActivity.java
public class MainActivity extends
ActionBarActivity {
@Override
protected void onCreateBundle savedInstanceState {
super.onCreatesavedInstanceState;
setContentViewR.layout.main_activity;
RecyclerView recyclerView = RecyclerView
findViewByIdR.id.recyclerView;
StaggeredGridLayoutManager样子像GridView,但是可以设置成水平滑动
recyclerView.setLayoutManager
new StaggeredGridLayoutManager
2,
StaggeredGridLayoutManager.HORIZONTAL;
recyclerView.setAdapternew
MemberAdapterthis;
}
...
}
5-6 自定义View组件与2D绘图
当函数库没有为开发者提供所需的UI组件时,开发者可以自行定义,但自定义的UI组件仍然必须继承View类并改写onDraw让Android系统可以绘制此自定义组件。关于绘图部分可以使用Android API提供的2D绘图功能,套件名称为android.graphics,常用到的类为Paint(绘图功能)与Canvas(画布功能)。
范例Draw2dDemo
范例说明:
按下MOVE RIGHT按钮会让下面的几何图形向右移动,如图5-8所示。
图5-8 范例Draw2dDemo演示几何图形的移动
创建步骤:
继承View类并改写onDraw:想绘图就必须有一个可显示的组件以供绘制,可以自行定义类(例如GeometricView类)去继承View类,并且改写onDraw,将想要绘制的图形放入onDraw方法内。除此之外,还需创建至少两个构造函数便于开发者可以通过程序代码或layout文件来创建此UI组件。
Draw2dDemo java GeometricView.java
public
class GeometricView extends View {
private int offset = 0;
private Paint paint = new Paint;
此构造函数方便直接使用程序代码创建GeometricView组件
public GeometricViewContext context {
supercontext;
}
通过layout文件创建GeometricView组件会调用此构造函数,在layout文件使用到的属性会传递给attrs参数
public GeometricViewContext context,
AttributeSet attrs {
supercontext, attrs;
}
调用此方法并传递偏移量给offset参数,会在onDraw绘图时用到
public void setOffsetint offset {
this.offset = offset;
}
@Override
protected void onDrawCanvas canvas {
paint调用setColor设置颜色、setStrokeWidth设置线的粗细
paint.setColorColor.RED;
paint.setStrokeWidth10;
paint调用drawLine画线,需提供起点与终点的x, y坐标;
drawCircle画圆,需提供圆点的x, y坐标与半径长度;
drawRect画方形,需提供左、上、右、下四条边线的坐标
canvas.drawLine10 offset, 10, 210
offset, 10, paint;
paint.setColorColor.YELLOW;
canvas.drawCircle110 offset, 140,
100, paint;
paint.setColorColor.GREEN;
canvas.drawRect10 offset, 260, 210
offset, 460, paint;
}
}
以layout文件创建自行定义的GeometricView组件会自动调用如前所述的GeometricView(Context, AttributeSet)构造函数。因为不属于android.widget套件,所以必须输入完整的名称idv.ron.draw2ddemo. GeometricView。
Draw2dDemo res layout
main_activity.xml
LinearLayout
xmlns:android="http:schemas.android.comapkresandroid"
xmlns:tools="http:schemas.android.comtools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimenactivity_horizontal_margin"
android:paddingRight="@dimenactivity_horizontal_margin"
android:paddingTop="@dimenactivity_vertical_margin"
android:paddingBottom="@dimenactivity_vertical_margin"
tools:context=".MainActivity"
Button
android:id="@ idbtOffset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@stringtext_btOffset"
android:onClick="onOffsetClick"
idv.ron.draw2ddemo.GeometricView
android:id="@ idgeometricView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
LinearLayout
调用View.invalidate重绘组件:如果想要重新绘制UI组件,该组件调用invalidate,系统会先废弃原来的画布,然后再次调用onDraw并提供新的画布,以重新绘制此组件的内容。
Draw2dDemo java MainActivity.java
public class MainActivity extends
ActionBarActivity {
private GeometricView geometricView;
private int offset = 0;
@Override
protected void onCreateBundle savedInstanceState {
super.onCreatesavedInstanceState;
setContentViewR.layout.main_activity;
geometricView = GeometricView findViewByIdR.id.geometricView;
}
用户每按一次Move Right按钮时会将偏移量 10,也就是向右移动10像素,需要调用invalidate废弃原来在GeometricView组件上的画布;系统会自动调用onDraw并传送新的画布以便重新绘制
public void onOffsetClickView view {
if geometricView != null {
offset = 10;
geometricView.setOffsetoffset;
geometricView.invalidate;
}
}
}
5-7 Frame Animation
Frame Animation动画(图框式动画)就是利用ImageView组件加载项目的resdrawable目录内的图片文件后,按照一定顺序与时间播放这一连串的图片,就像传统动画播放一样。这类动画需要通过调用AnimationDrawable类的相关方法完成播放设置后才能开始播放。
范例 FrameAnimationDemo
范例说明:
触击画面,ImageView组件会播放动画,再次触击则会停止播放,如图5-9所示。
图5-9 范例 FrameAnimationDemo演示动画的播放和停止
创建步骤:
使用layout文件创建ImageView用来装载动画的所有图片,并设置单击该ImageView后会调用onPictureClick。
FrameAnimationDemo res layout
main_activity.xml
RelativeLayout
xmlns:android="http:schemas.android.comapkresandroid"
xmlns:tools="http:schemas.android.comtools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimenactivity_horizontal_margin"
android:paddingRight="@dimenactivity_horizontal_margin"
android:paddingTop="@dimenactivity_vertical_margin"
android:paddingBottom="@dimenactivity_vertical_margin"
tools:context=".MainActivity"
ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@ idivPicture"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:onClick="onPictureClick"
RelativeLayout
AnimationDrawable加载动画所需的图片,并做好播放设置。ImageView调用setBackground将AnimationDrawable对象放入后,动画即可在该ImageView上显示。最后AnimationDrawable调用startstop来播放停止动画。
FrameAnimationDemo java
MainActivity.java
public class MainActivity extends
ActionBarActivity {
private AnimationDrawable animationDrawable;
@Override
protected void onCreateBundle savedInstanceState {
super.onCreatesavedInstanceState;
setContentViewR.layout.main_activity;
获取Resource对象后调用getDrawable并指定图片资源ID以获取对应的图片。图片数据类型必须为Drawable方可用于动画
Resources res = getResources;
Drawable p01 =
res.getDrawableR.drawable.p01;
Drawable p02 = res.getDrawableR.drawable.p02;
Drawable p03 = res.getDrawableR.drawable.p03;
Drawable p04 = res.getDrawableR.drawable.p04;
Drawable p05 =
res.getDrawableR.drawable.p05;
Drawable p06 = res.getDrawableR.drawable.p06;
Drawable p07 = res.getDrawableR.drawable.p07;
Drawable p08 = res.getDrawableR.drawable.p08;
Drawable p09 = res.getDrawableR.drawable.p09;
Drawable p10 = res.getDrawableR.drawable.p10;
Drawable p11 = res.getDrawableR.drawable.p11;
Drawable p12 = res.getDrawableR.drawable.p12;
AnimationDrawable对象调用setOneShot设置动画是否仅播放一次,true代表仅播放一次,false代表连续播放。调用addFrame可加入要播放的图片,并设置该图片持续显示的时间
animationDrawable = new
AnimationDrawable;
animationDrawable.setOneShotfalse;
int duration = 200;
animationDrawable.addFramep01,
duration;
animationDrawable.addFramep02, duration;
animationDrawable.addFramep03, duration;
animationDrawable.addFramep04, duration;
animationDrawable.addFramep05, duration;
animationDrawable.addFramep06, duration;
animationDrawable.addFramep07, duration;
animationDrawable.addFramep08, duration;
animationDrawable.addFramep09, duration;
animationDrawable.addFramep10, duration;
animationDrawable.addFramep11, duration;
animationDrawable.addFramep12, duration;
获取ImageView组件当作播放图片的容器,调用setBackground将AnimationDrawable对象放入后即可套用动画设置
ImageView ivPicture = ImageView
findViewByIdR.id.ivPicture;
ivPicture.setBackgroundanimationDrawable;
}
当ImageView被单击后,先调用isRunning判断是否在播放动画。如果没有播放动画,就调用start播放;否则就调用stop停止播放
public void onPictureClickView view {
if !animationDrawable.isRunning {
animationDrawable.start;
} else {
animationDrawable.stop;
}
}
}
[1] View组件也就是UI组件,Android API将View类定义成所有UI组件的根类。换句话说,View类定义的方法,所有UI组件都可以调用。