描述
开 本: 16开纸 张: 胶版纸包 装: 平装是否套装: 否国际标准书号ISBN: 9787302430018
目 录
第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 应用程序改版
序
从编写第一本《Android
2.X手机程序开发实战》到本书《Android
5.X App开发实战》,共历经了Android的4大版本(2.X, 3.X, 4.X, 5.X),Android系统也历经了许多重大的改变。
l 系统的成长与改进:
- 2.X版仅支持手机系统开发,而且操作流畅度差强人意。
- 3.0版开始支持平板电脑,但不支持手机系统开发,这就苦了开发人员,因为要分别熟悉两套应用程序编程接口(API)。
- 4.0版系统稳定性大幅提升,而且手机、平板电脑共享API而不再分家,方便了开发人员的开发工作。
- 5.0版提倡material design(材料设计)将用户界面(UI)设计提升到高水平,即使与iOS系统对比也毫不逊色。
l 市场占有率大幅提升与各种设备绽放光芒:
- 每年的销售市场占有率从2.X版时代的30%到现在接近85%。
- 从当初只有手机设备到平板电脑、电子书阅读器,到现在有移动电视与可穿戴设备。
对Android应用程序开发人员而言,最大的改变莫过于Android Studio在2014年12月8日正式成为开发Android应用程序的官方IDE(Integrated Development Environment,集成开发环境)工具,Android开发官网也很明确地指出原先Eclipse with ADT的开发模式已经被Android Studio取代,建议开发者尽早改用Android Studio以获得最好的支持。
用过Eclipse with
ADT的开发人员都知道一旦导入Android项目,常常会发生不明原因的编译失败,而且往往需要重启Eclipse或用一些很奇怪的方式才能解决。这些问题在Android Studio上完全不见了,而且UI设计工具比以前更聪明,XML源代码与UI画面可以同时显示;许多贴心的提示功能可以让开发人员避免发生一些不必要的错误。使用Android Studio可以让初学者更加容易就进入到Android App的开发世界。本书所有范例已经更新成Android Studio版本。
CardView与RecyclerView是Android 5.0时推出的两个新的UI组件,使得用户界面的显示更加多样化。因为属于support函数库,所以旧版的Android设备也可以显示出这两个UI组件设计出来的用户界面,本书“5-5 CardView与RecyclerView”会详细说明如何使用它们。
除了前面所说的更新部分外,借此机会将本书的范例内容调整为更符合各章主题,让读者可以通过主题与范例紧密结合的介绍和说明,更清楚地了解Android App的开发技巧。除此之外,书中还增加了一些新的主题使内容更加充实,例如说明当设备处于休眠状态时仍然可以执行Service(服务)的技巧、如何在设备开机时就启动App、陀螺仪传感器的应用等。
本书范例的素材和代码下载地址为:。
如果下载有问题,,邮件主题为“求Android 5.X App开发实战代码”。
黄彬华
第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=”@ id/spFood”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:entries=”@array/food_array” />
SpinnerDemo > res > values >
strings.xml
spaghetti
dumpling
sushi
调用findViewById()找到layout文件上的Spinner,注册OnItemSelectedListener监听器,当用户改变选项时会调用onItemSelected()。除了步骤1使用文本文件来创建选项文字外,也可以通过程序代码创建字符串数组供ArrayAdapter使用。
SpinnerDemo > java > MainActivity.java
public class MainActivity extends
ActionBarActivity {
private TextView tvMessage;
@Override
public
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
findViews();
}
private void findViews() {
tvMessage = (TextView) findViewById(R.id.tvMessage);
Spinner spFood = (Spinner)
findViewById(R.id.spFood);
注册OnItemSelectedListener监听器之前先调用setSelection(),不仅可以设置开始的预选项目,还可避免Spinner开始就执行OnItemSelectedListener.onItemSelected()的问题
spFood.setSelection(0, true);
注册OnItemSelectedListener监听器,当用户改变选项时会调用onItemSelected()
spFood.setOnItemSelectedListener(listener);
Spinner spPlace = (Spinner)
findViewById(R.id.spPlace);
调用ArrayAdapter构造函数以创建选项的内容与样式。选项的内容来自于places字符串数组;样式则套用系统内置的android.R.layout.simple_spinner_item;
String[] places = {“Australia”,
“U.K.”, “Japan”, “Thailand”};
ArrayAdapter adapterPlace
= new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, places);
调用setDropDownViewResource()套用系统内置的下拉菜单样式——android.R.layout.
simple_spinner_dropdown_item
adapterPlace
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Spinner调用setAdapter()套用指定的Adapter以加载对应的选项内容与样式。ArrayAdapter是Adapter子类,如果选项只想显示文字,使用ArrayAdapter比较方便
spPlace.setAdapter(adapterPlace);
spPlace.setSelection(0, true);
spPlace的事件处理与前面的spFood相同,所以可以注册相同的监听器
spPlace.setOnItemSelectedListener(listener);
}
实现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.setText(parent.getItemAtPosition(pos).toString());
}
@Override
public void onNothingSelected(AdapterView> 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
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:id=”@ id/actvCountry”
android:hint=”@string/text_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 onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
final String[] countries = {
“CANADA”, “CHINA”,
“FRANCE”, “GERMANY”,
“ITALY”,
“JAPAN”, “KOREA”, “Greece”, “UK”,
“US”
};
AutoCompleteTextView actvCountry =
(AutoCompleteTextView)
findViewById(R.id.actvCountry);
R.layout.list_item是自行创建的layout文件,当作选项内容的样式
ArrayAdapter arrayAdapter
=
new
ArrayAdapter<>(this,
R.layout.list_item, countries);
actvCountry.setAdapter(arrayAdapter);
AutoCompleteTextView注册OnItemClickListener监听器,当用户选择提示列表上的文字时会调用onItemClick(),此时调用getItemAtPosition()获取用户选取的文字并以Toast消息框显示
actvCountry.setOnItemClickListener(new
AdapterView.OnItemClickListener() {
@Override
public void onItemClick(
AdapterView>
parent, View view, int position, long id) {
String item =
parent.getItemAtPosition(position).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
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
android:paddingLeft=”@dimen/activity_horizontal_margin”
android:paddingRight=”@dimen/activity_horizontal_margin”
android:paddingTop=”@dimen/activity_vertical_margin”
android:paddingBottom=”@dimen/activity_vertical_margin”
tools:context=”.MainActivity”>
android:id=”@ id/tvTitle”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_horizontal”
android:textSize=”20sp”
android:text=”@string/tvTitle” />
android:id=”@ id/lvMember”
android:layout_width=”match_parent”
android:layout_height=”wrap_content” />
创建ListView各选项所需的layout文件。因为每一个选项的版面设置都是一样的,所以只要创建一个layout文件即可重复套用。在此例中,载入的layout文件—listview_item.xml,其父组件为LinearLayout,所以其实加载的是LinearLayout;而三个子组件:一个ImageView用来显示会员照片;两个TextView分别显示会员ID与会员姓名。
ListViewDemo > res > layout >
listview_item.xml
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”horizontal”>
android:id=”@ id/ivImage”
android:layout_width=”48dp”
android:layout_height=”48dp”
android:layout_marginLeft=”10dp”
android:padding=”6dp” />
android:id=”@ id/tvId”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:padding=”6dp” />
android:id=”@ id/tvName”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:padding=”6dp” />
创建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 List memberList;
public MemberAdapter(Context context) {
获取LayoutInflater对象以便之后动态加载layout文件供选项使用
layoutInflater =
LayoutInflater.from(context);
memberList是此ListView的数据来源,而Member类定义着会员资料如会员ID、照片、姓名
memberList = new ArrayList<>();
memberList.add(new Member(23,
R.drawable.p01, “John”));
memberList.add(new Member(75, R.drawable.p02, “Jack”));
memberList.add(new Member(65, R.drawable.p03, “Mark”));
memberList.add(new Member(12, R.drawable.p04, “Ben”));
memberList.add(new Member(92, R.drawable.p05, “James”));
memberList.add(new Member(103, R.drawable.p06, “David”));
memberList.add(new Member(45, R.drawable.p07, “Ken”));
memberList.add(new Member(78, R.drawable.p08, “Ron”));
memberList.add(new Member(234, R.drawable.p09, “Jerry”));
memberList.add(new Member(35, R.drawable.p10, “Maggie”));
memberList.add(new Member(57, R.drawable.p11, “Sue”));
memberList.add(new Member(61, R.drawable.p12, “Cathy”));
}
提供选项的总数,系统会按照返回值来决定调用下面getView()的次数
@Override
public int getCount() {
return memberList.size();
}
根据position位置提供该选项对应的对象,在此返回代表会员的Member对象
@Override
public Object getItem(int position) {
return memberList.get(position);
}
根据position位置提供该选项对应的ID,在此返回会员ID
@Override
public long getItemId(int position) {
return
memberList.get(position).getId();
}
getView()是根据position位置提供该选项对应的View。
开始画面尚未显示时,converView为null,调用inflate()载入R.layout.listview_item文件其实就是载入LinearLayout这个View。
画面显示时,用户可以看到ListView画面,当用户向下滑动一行,原本第一行会被滑出画面,被滑出选项的View会自动传递给convertView,所以不会为null,可以重复利用该View,只要将值替换成滑入选项的值即可。
@Override
public View getView(int position, View
convertView, ViewGroup parent) {
if (convertView == null) {
convertView =
layoutInflater.inflate(R.layout.listview_item, parent, false);
}
按照position获取memberList内的member对象
Member member =
memberList.get(position);
找到convertView子组件imageView,并指定要显示的图片
ImageView ivImage =
(ImageView) convertView
.findViewById(R.id.ivImage);
ivImage.setImageResource(member.getImage());
找到convertView子组件textView,并显示会员ID与姓名
TextView tvId = (TextView) convertView
.findViewById(R.id.tvId);
tvId.setText(String.valueOf(member.getId()));
TextView tvName = (TextView) convertView
.findViewById(R.id.tvName);
tvName.setText(member.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 onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
ListView调用setAdapter()并套用创建好的MemberAdapter
ListView lvMember = (ListView)
findViewById(R.id.lvMember);
lvMember.setAdapter(new
MemberAdapter(this));
ListView注册OnItemClickListener监听器,当用户单击选项时会调用onItemClick()。
Parent——被单击的ListView
View——被单击的选项所加载的layout内容,在此为listview_item.xml内的LinearLayout组件
Position——被单击的索引位置
Id——实现BaseAdapter.getItemId()所返回的ID
lvMember.setOnItemClickListener(new
AdapterView.OnItemClickListener() {
@Override
public void
onItemClick(AdapterView> parent, View view,
int
position, long id) {
调用getItemAtPosition()会获取BaseAdapter.getItem()所返回的对象,在此为会员对象,之后以Toast方式显示此会员的相关信息
Member member = (Member)
parent.getItemAtPosition(position);
String text = “ID =
” member.getId()
“, name = ”
member.getName();
Toast.makeText(MainActivity.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 onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
GridView gvMember = (GridView) findViewById(R.id.gvMember);
gvMember.setAdapter(new MemberAdapter(this));
gvMember.setOnItemClickListener(new
AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView>
parent, View view,
int
position, long id) {
Member member = (Member) parent.getItemAtPosition(position);
创建ImageView并放上会员照片;再创建Toast并调用setView()套用该ImageView即可显示照片
ImageView imageView = new
ImageView(MainActivity.this);
imageView.setImageResource(member.getImage());
Toast toast = new
Toast(MainActivity.this);
toast.setView(imageView);
toast.setDuration(Toast.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无法像ListView/GridView一样注册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
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
android:paddingLeft=”@dimen/activity_horizontal_margin”
android:paddingRight=”@dimen/activity_horizontal_margin”
android:paddingTop=”@dimen/activity_vertical_margin”
android:paddingBottom=”@dimen/activity_vertical_margin”
tools:context=”.MainActivity”>
android:id=”@ id/tvTitle”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_horizontal”
android:textSize=”20sp”
android:text=”@string/tvTitle” />
android:id=”@ id/recyclerView”
android:layout_width=”match_parent”
android:layout_height=”wrap_content” />
创建RecyclerView选项所需的layout文件。因为每一个选项样式都相同,所以只要创建一个layout文件即可重复套用。在此例中为了要搭配CardView,所以创建CardView组件;因为也不属于android.widget套件,所以也必须使用完整的名称android.support.v7.widget.CardView。因为CardView使用了不同的名称空间,所以必须加入”http://schemas.android.com/apk/res-auto”,并以card_view名称表示。CardView重要属性有:cardBackgroundColor用来设置背景色,cardCornerRadius设置圆角弧度,cardElevation设置阴影。
RecyclerCardViewDemo > res > layout >
recyclerview_cardview_item.xml
xmlns:card_view=”http://schemas.android.com/apk/res-auto”
android:id=”@ id/cardview”
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”>
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”>
android:id=”@ id/ivImage”
android:layout_width=”120dp”
android:layout_height=”160dp”
android:layout_marginLeft=”16dp” />
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:orientation=”horizontal”>
android:id=”@ id/tvId”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginLeft=”20dp”
android:layout_marginBottom=”12dp” />
android:id=”@ id/tvName”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginLeft=”24dp”
android:layout_marginBottom=”12dp” />
创建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.Adapter {
private Context context;
private LayoutInflater layoutInflater;
private List memberList;
public MemberAdapter(Context context) {
this.context = context;
layoutInflater = LayoutInflater.from(context);
memberList是数据来源,而Member类定义着会员资料如会员ID、照片、姓名
memberList = new ArrayList<>();
memberList.add(new Member(92,
R.drawable.p05, “James”));
memberList.add(new Member(103, R.drawable.p06, “David”));
memberList.add(new Member(234, R.drawable.p09, “Jerry”));
memberList.add(new Member(35,
R.drawable.p10, “Maggie”));
memberList.add(new Member(23, R.drawable.p01, “John”));
memberList.add(new Member(75, R.drawable.p02, “Jack”));
memberList.add(new Member(65, R.drawable.p03, “Mark”));
memberList.add(new Member(12, R.drawable.p04, “Ben”));
memberList.add(new Member(45, R.drawable.p07, “Ken”));
memberList.add(new Member(78, R.drawable.p08, “Ron”));
memberList.add(new Member(57, R.drawable.p11, “Sue”));
memberList.add(new Member(61, 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 ViewHolder(View itemView) {
super(itemView);
ivImage = (ImageView) itemView.findViewById(R.id.ivImage);
tvId = (TextView) itemView.findViewById(R.id.tvId);
tvName = (TextView) itemView.findViewById(R.id.tvName);
RecyclerView无法像ListView/GridView同样注册OnItemClickListener;如果仍然想要监听选项是否被单击了,可以为选项的View注册OnClickListener并通过getAdapterPosition()来获取被单击项目的位置,不过可能返回NO_POSITION,因此建议要检查。当开发者调用RecyclerView.Adapter.notifyDataSetChanged()刷新画面,而选项的View没有实时传入就会导致getAdapterPosition()返回NO_POSITION
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v)
{
if
(getAdapterPosition() == RecyclerView.NO_POSITION) {
Toast.makeText(
context,
R.string.msg_ClickAgain,
Toast.LENGTH_SHORT)
.show();
return;
}
单击选项会显示对应的会员照片
Member member =
memberList.get(getAdapterPosition());
ImageView imageView =
new ImageView(context);
imageView.setImageResource(member.getImage());
Toast toast = new
Toast(context);
toast.setView(imageView);
toast.setDuration(Toast.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
onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View itemView = layoutInflater.inflate(
R.layout.recyclerview_cardview_item, viewGroup, false);
return new ViewHolder(itemView);
}
要显示RecyclerView特定position的数据时会调用此方法,开发者应该按照position提供Member对象,并将数据显示在ViewHolder指定的子View上
@Override
public void onBindViewHolder(ViewHolder
viewHolder, int position) {
Member member =
memberList.get(position);
viewHolder.getIvImage().setImageResource(member.getImage());
viewHolder.getTvId().setText(String.valueOf(member.getId()));
viewHolder.getTvName().setText(member.getName());
}
}
RecyclerView调用setLayoutManager()套用RecyclerView.LayoutManager提供的版面设置样式。RecyclerView调用setAdapter()套用RecyclerView.Adapter提供的选项设置。
RecyclerCardViewDemo > java >
MainActivity.java
public class MainActivity extends
ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
RecyclerView recyclerView = (RecyclerView)
findViewById(R.id.recyclerView);
StaggeredGridLayoutManager样子像GridView,但是可以设置成水平滑动
recyclerView.setLayoutManager(
new StaggeredGridLayoutManager(
2,
StaggeredGridLayoutManager.HORIZONTAL));
recyclerView.setAdapter(new
MemberAdapter(this));
}
…
}
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 GeometricView(Context context) {
super(context);
}
通过layout文件创建GeometricView组件会调用此构造函数,在layout文件使用到的属性会传递给attrs参数
public GeometricView(Context context,
AttributeSet attrs) {
super(context, attrs);
}
调用此方法并传递偏移量给offset参数,会在onDraw()绘图时用到
public void setOffset(int offset) {
this.offset = offset;
}
@Override
protected void onDraw(Canvas canvas) {
paint调用setColor()设置颜色、setStrokeWidth()设置线的粗细
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
paint调用drawLine()画线,需提供起点与终点的x, y坐标;
drawCircle()画圆,需提供圆点的x, y坐标与半径长度;
drawRect()画方形,需提供左、上、右、下四条边线的坐标
canvas.drawLine(10 offset, 10, 210
offset, 10, paint);
paint.setColor(Color.YELLOW);
canvas.drawCircle(110 offset, 140,
100, paint);
paint.setColor(Color.GREEN);
canvas.drawRect(10 offset, 260, 210
offset, 460, paint);
}
}
以layout文件创建自行定义的GeometricView组件会自动调用如前所述的GeometricView(Context, AttributeSet)构造函数。因为不属于android.widget套件,所以必须输入完整的名称idv.ron.draw2ddemo. GeometricView。
Draw2dDemo > res > layout >
main_activity.xml
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
android:paddingLeft=”@dimen/activity_horizontal_margin”
android:paddingRight=”@dimen/activity_horizontal_margin”
android:paddingTop=”@dimen/activity_vertical_margin”
android:paddingBottom=”@dimen/activity_vertical_margin”
tools:context=”.MainActivity”>
android:id=”@ id/btOffset”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_horizontal”
android:text=”@string/text_btOffset”
android:onClick=”onOffsetClick” />
android:id=”@ id/geometricView”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginTop=”24dp” />
调用View.invalidate()重绘组件:如果想要重新绘制UI组件,该组件调用invalidate(),系统会先废弃原来的画布,然后再次调用onDraw()并提供新的画布,以重新绘制此组件的内容。
Draw2dDemo > java > MainActivity.java
public class MainActivity extends
ActionBarActivity {
private GeometricView geometricView;
private int offset = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
geometricView = (GeometricView) findViewById(R.id.geometricView);
}
用户每按一次Move Right按钮时会将偏移量 10,也就是向右移动10像素,需要调用invalidate()废弃原来在GeometricView组件上的画布;系统会自动调用onDraw()并传送新的画布以便重新绘制
public void onOffsetClick(View view) {
if (geometricView != null) {
offset = 10;
geometricView.setOffset(offset);
geometricView.invalidate();
}
}
}
5-7 Frame Animation
Frame Animation动画(图框式动画)就是利用ImageView组件加载项目的res/drawable目录内的图片文件后,按照一定顺序与时间播放这一连串的图片,就像传统动画播放一样。这类动画需要通过调用AnimationDrawable类的相关方法完成播放设置后才能开始播放。
范例 FrameAnimationDemo
范例说明:
触击画面,ImageView组件会播放动画,再次触击则会停止播放,如图5-9所示。
图5-9 范例 FrameAnimationDemo演示动画的播放和停止
创建步骤:
使用layout文件创建ImageView用来装载动画的所有图片,并设置单击该ImageView后会调用onPictureClick()。
FrameAnimationDemo > res > layout >
main_activity.xml
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingLeft=”@dimen/activity_horizontal_margin”
android:paddingRight=”@dimen/activity_horizontal_margin”
android:paddingTop=”@dimen/activity_vertical_margin”
android:paddingBottom=”@dimen/activity_vertical_margin”
tools:context=”.MainActivity”>
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:id=”@ id/ivPicture”
android:layout_alignParentTop=”true”
android:layout_centerHorizontal=”true”
android:onClick=”onPictureClick” />
AnimationDrawable加载动画所需的图片,并做好播放设置。ImageView调用setBackground()将AnimationDrawable对象放入后,动画即可在该ImageView上显示。最后AnimationDrawable调用start()/stop()来播放/停止动画。
FrameAnimationDemo > java >
MainActivity.java
public class MainActivity extends
ActionBarActivity {
private AnimationDrawable animationDrawable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
获取Resource对象后调用getDrawable()并指定图片资源ID以获取对应的图片。图片数据类型必须为Drawable方可用于动画
Resources res = getResources();
Drawable p01 =
res.getDrawable(R.drawable.p01);
Drawable p02 = res.getDrawable(R.drawable.p02);
Drawable p03 = res.getDrawable(R.drawable.p03);
Drawable p04 = res.getDrawable(R.drawable.p04);
Drawable p05 =
res.getDrawable(R.drawable.p05);
Drawable p06 = res.getDrawable(R.drawable.p06);
Drawable p07 = res.getDrawable(R.drawable.p07);
Drawable p08 = res.getDrawable(R.drawable.p08);
Drawable p09 = res.getDrawable(R.drawable.p09);
Drawable p10 = res.getDrawable(R.drawable.p10);
Drawable p11 = res.getDrawable(R.drawable.p11);
Drawable p12 = res.getDrawable(R.drawable.p12);
AnimationDrawable对象调用setOneShot()设置动画是否仅播放一次,true代表仅播放一次,false代表连续播放。调用addFrame()可加入要播放的图片,并设置该图片持续显示的时间
animationDrawable = new
AnimationDrawable();
animationDrawable.setOneShot(false);
int duration = 200;
animationDrawable.addFrame(p01,
duration);
animationDrawable.addFrame(p02, duration);
animationDrawable.addFrame(p03, duration);
animationDrawable.addFrame(p04, duration);
animationDrawable.addFrame(p05, duration);
animationDrawable.addFrame(p06, duration);
animationDrawable.addFrame(p07, duration);
animationDrawable.addFrame(p08, duration);
animationDrawable.addFrame(p09, duration);
animationDrawable.addFrame(p10, duration);
animationDrawable.addFrame(p11, duration);
animationDrawable.addFrame(p12, duration);
获取ImageView组件当作播放图片的容器,调用setBackground()将AnimationDrawable对象放入后即可套用动画设置
ImageView ivPicture = (ImageView)
findViewById(R.id.ivPicture);
ivPicture.setBackground(animationDrawable);
}
当ImageView被单击后,先调用isRunning()判断是否在播放动画。如果没有播放动画,就调用start()播放;否则就调用stop()停止播放
public void onPictureClick(View view) {
if (!animationDrawable.isRunning()) {
animationDrawable.start();
} else {
animationDrawable.stop();
}
}
}
View组件也就是UI组件,Android API将View类定义成所有UI组件的根类。换句话说,View类定义的方法,所有UI组件都可以调用。
评论
还没有评论。