原文  http://blog.csdn.net/heimady/article/details/10363995

1. 前言

1.1. 什么是 3G 、 4G

Ÿ 第三代移动通信技术(3rd - Generation),速率一般在 几百 K bps ,较之前的 2G 和 2.5G 在数据传输速度上有很大提升。

Ÿ 第四代移动通信技术( 4th - Generation ),速度可达到 100Mbps 以上,几乎可以满足人们的所有传输数据的需求。

Ÿ 目前主流的 3G 技术标准有三种:

WCDMA :全球 80% 以上的 3G 网络都是采用此种制式。中国联通运营。 186

CDMA2000 :目前日韩及北美使用较多。中国电信运营。  189

TD-SCDMA :中国自主知识产权的 3G 通信技术。中国移动运营。  188 

Ÿ 目前主流的 4G 技术为 LTE ,但还没有被广泛应用:

GSM à GPRS à EDGE à WCDMA à HSDPA à HSDPA+ à LTE

1.2. 什么是 Android 

Ÿ Android 本义指 “ 机器人 ”,Google 于 2007 年 11 月 5 日宣布的基于 Linux 平台的开源手机操作系统的名称,官方中文名:安卓 。

Ÿ Android 系统早期由原名为 "Android" 的公司开发,谷歌在 2005 年收购 "Android.Inc" 后,继续对 Android 系统开发运营 。

Ÿ 底层 Linux 内核只提供基本功能,其他的应用软件则由各公司自行开发, 大部分程序以 Java 语言编写。

Ÿ 由于 Android 系统的开源特性,很多制造商都在生产 Android 系统的设备,如:摩托罗拉、 HTC 、三星、索爱、 LG 、小米、华为、魅族等。

Ÿ Android 系统除了运行在智能手机上之外,还可以用做平板电脑、电视、汽车等很多设备上。

Ÿ Android 系统架构图:

1.3. Android 的版本升级

Ÿ 1.5 Cupcake(纸杯蛋糕)

拍摄 / 播放影片,并支持上传到 Youtube

支持立体声 蓝牙 耳机,同时改善自动配对性能

最新的采用 WebKit 技术的浏览器,支持复制 / 贴上和页面中搜索

GPS 性能大大提高

提供屏幕虚拟键盘

主屏幕增加音乐播放器和相框 widgets

应用程序自动随着手机旋转

短信、 Gmail 、日暦,浏览器的用户接口大幅改进,如 Gmail 可以批量删除邮件

相机启动速度加快,拍摄图片可以直接上传到 Picasa

来电照片显示

Ÿ 1.6 Donut(甜甜圈)

重新设计的 Android Market 

手势支持 

支持 CDMA 网络 

文字转语音系统( Text-to-Speech ) 

快速搜索框 

全新的拍照接口 

查看应用程序耗电 

支持 虚拟私人网络 ( VPN ) 

支持更多的屏幕分辨率。 

支持 OpenCore2 媒体引擎 

新增面向视觉或听觉困难人群的易用性插件

Ÿ 2.1 Éclair(闪电泡芙)

优化硬件速度

"Car Home" 程序

支持更多的屏幕分辨率

改良的用户界面

新的浏览器的用户接口和支持 HTML5

新的联系人名单

更好的白色 / 黑色背景比率

改进 Google Maps  3.1.2

支持 Microsoft Exchange

支持内置相机闪光灯

支持数码变焦

改进的虚拟键盘

支持蓝牙 2.1

支持动态桌面的设计

Ÿ 2.2 Froyo ( 冻酸奶)

支持将软件安装至扩展内存

集成 Adobe Flash 10.1 支持

加强软件 即时编译 的速度

新增软件启动 " 快速 " 至电话和浏览器

USB 分享器和 WiFi 热点功能

支持在浏览器上传档案

更新 Market 中的批量和自动更新

增加对 Microsoft Exchange 的支持(安全政策 , auto-discovery, GAL look-up )

集成 Chrome 的 V8 JavaScript 引擎 到浏览器

加强快速搜索小工具

更多软件能透过 Market 更新,类似 2.0/2.1 中的 Map 更新

速度和性能优化

Ÿ  2.3 Gingerbread ( 姜饼 )

修补 UI

支持更大的屏幕尺寸和分辨率( WXGA 及更高)

系统级复制粘贴

重新设计的多点触摸屏幕键盘

本地支持多个镜头(用于视频通话等)和更多传感器(陀螺仪、气压计等)

电话簿集成 Internet Call 功能

支持 近场通信 ( NFC ) 

强化电源、应用程序管理功能

新增下载管理员

优化游戏开发支持

多媒体音效强化

从 YAFFS 转换到 ext4 文件系统

开放了屏幕截图功能

对黑色及白色的还原更加真实

Ÿ 3.x Honeycomb ( 蜂巢)

仅供平板电脑使用

Google eBooks 上提供数百万本书

支持平板电脑大萤幕、高分辨率

新版 Gmail

Google Talk 视讯功能

3D 加速处理

网页版 Market(Web store) 详细分类显示,依个人 Android 分别设定安装应用程序

新的短消息通知功能

专为平板电脑设计的用户界面(重新设计的通知列与系统列)

加强多任务处理的接口

重新设计适用大屏幕的键盘及复制粘贴功能

多个标签的浏览器以及私密浏览模式

快速切换各种功能的相机

增强的图库与快速滚动的联络人接口

更有效率的 Email 接口

支持多核心处理器

3.2 优化 7 吋平板显示

Ÿ 4.0 Ice Cream Sandwich ( 奶油三明治)

虚拟按键,增大屏幕面积同时控制手机整体大小

桌面插件 Widgets 列表呈现在标签页中,与程序列表类似并且共存

文件夹更容易创建和管理,与 iOS 类似

可定制的桌面系统

可视语音邮件  

日历支持缩放操作

Gmail 离线搜索,两行预览,以及底部新快捷栏

音量下键 + 电源键组合截图

改进虚拟键盘纠错

从锁屏界面直接访问应用程序

优化复制粘贴

新版浏览器

新的 Roboto 字体

流量控制系统

相机应用

人脸识别,刷脸解锁

内置照片编辑器

多任务列表

新的图库布局和组织方式

联系人应用整合社交网络信息

Android Beam

http://digi.tech.qq.com/a/20111019/001579.htm

1.4. 主流智能手机操作系统

2. 搭建开发环境

2.1. 所需资源

Ÿ  JDK , Java 开发环境。下载地址: http://www.oracle.com

Ÿ  Eclipse , IBM 公司开发的一款开源 IDE 。 http://www.eclipse.org

Ÿ  Android SDK , Android  开发工具,包含开发 Android 程序所需类库、源码、文档、案例等资源。 http://www.android.com

Ÿ  ADT 插件 ,ADT  是  Eclipse  平台下用来开发  Android  应用程序的插件 。 http://www.android.com

2.2. Eclipse 安装 ADT 插件

2.3. 配置 SDK 路径

2.4. 启动虚拟机

Ø 点击机器人图标

Ø 弹出虚拟机管理器

Ø 修改虚拟机默认目录

指定环境变量 android_sdk_home ,通常指定为 SDK 所在目录,可以随意指定。

重启 Eclipse 之后将会以这个目录作为存放虚拟机文件的位置。

Ø 配置虚拟机

Ÿ Name :虚拟机的名字,可随意定义

Ÿ Target :虚拟机版本

Ÿ SD Card - Size :虚拟机的 SDCard 大小,会在本地硬盘创建指定大小的文件用来存储数据,模拟真实手机的 SDCard

如果使用原有文件,可以选择 File 并点击 Browse 指定文件

Ÿ Snapshot : 保存快照,可以提高虚拟机启动速度

Ÿ Skin - Built-in :选择分辨率

HVGA : 480x320

QVGA : 320x240

WQVGA400 : 400x240

WQVGA432 : 432x240

WVGA800 : 800x480

WVGA854 : 854x480

也可以选择 Resolution 自行指定

Ø 成功进入 Android 操作系统界面

Ÿ 如果虚拟机显示无信号,需要对电脑的网络连接进行配置

无论电脑是否联网, IP 地址、子网掩码、默认网关、首选 DNS 服务器都需要配置

Ÿ 电脑如果未连接局域网或互联网,可以见默认网关和首选 DNS 服务器配置成本机IP ,例如:

Ÿ 电脑如果已连接局域网,需要将网关和首选 DNS 服务器进行设置,例如:

Ÿ 如果电脑已连接互联网,正常情况虚拟机不会显示无信号,重启虚拟机

2.5. 命令行操作

为了能在任意目录使用一下命令,需要将 SDK 目录下的 platform-tools文件夹路径和tools文件夹路径配置到 path 环境变量中

Ÿ 列出可以使用的 android 版本

android list targets

Ÿ 列出可以使用的虚拟机

android list avd

Ÿ 创建虚拟机

android create avd –n < 虚拟机名 > -t <Target 版本 ID> -c <SD 卡大小 > -s < 屏幕尺寸 >

Ÿ 启动虚拟机

emulator –avd < 虚拟机名 >

Ÿ 显示已连接的设备

adb devices

Ÿ 导入文件到手机

adb push <Windows 源文件路径 > < 手机目标路径 >

Ÿ 从手机导出文件

adb pull < 手机源文件路径 > <Windows 目录路径 >

Ÿ 安装程序

adb install <apk 文件路径 >

Ÿ 卸载程序

adb uninstall < 包名 >

Ÿ 重启 adb

adb kill-server

abd start-server

3. 开发一个 Android 程序

3.1. 创建 Android 程序

Ø 创建 Android Project

Ÿ Project name :项目名

Ÿ Build Target : Android 版本

Ÿ Application name :程序名,显示在程序列表中,以及程序启动后的标题栏

Ÿ Package name :包名,程序的唯一标识

Ÿ Create Activity :选择程序启动时是否创建一个窗体,设置主窗体名字

Ÿ Min SDK Version :设置运行该程序所需的最低版本

3.2. 安装、卸载程序

Ÿ Eclipse 安装

右键点击工程 – Run as – Android Application

Ÿ 虚拟机卸载

设置 – 应用程序 – 管理应用程序 – 选中要卸载的程序 – 卸载

3.3. 了解项目目录结构

Ÿ src :源代码

Ÿ gen :系统自动生成的文件

R.java 中记录了项目中各种资源 ID

Ÿ res :系统资源,所有文件都会在 R 文件生成资源 ID

drawable :图片

layout :界面布局

values :数据

anim :定义动画的 XML

raw :原生文件

Ÿ assets :资源路径,不会在 R 文件注册

Ÿ project.properties :供 Eclipse 使用,读取该项目使用 Android 版本号。早期版本名为: default.properties

Ÿ AndroidManifest.xml :清单文件,在软件安装的时候被读取

Android 中的四大组件( Activity 、 ContentProvider 、 BroadcastReceiver 、 Service)都需要在该文件中注册

程序所需的权限也需要在此文件中声明,例如:电话、短信、互联网、访问 SD 卡

Ÿ bin :二进制文件,包括 class 、资源文件、 dex 、 apk 等

Ÿ proguard.cfg:用来混淆代码的配置文件,防止别人反编译

3.4. 程序启动过程

Ÿ Eclipse 将 .java 源文件编译成 .class

Ÿ 使用 dx 工具将所有 .class 文件转换为 .dex 文件

Ÿ 再将 .dex 文件和所有资源打包成 .apk 文件

Ÿ 将 .apk 文件安装到虚拟机完成程序安装

Ÿ 启动程序 – 开启进程 – 开启主线程

Ÿ 创建 Activity 对象  – 执行 OnCreate() 方法

Ÿ 按照 main.xml 文件初始化界面

4. 演示案例

4.1. 电话拨号

Ÿ 搭建界面需要组件: TextView 、 EditText 、 Button

Ÿ 当点击 Button 时获取 EditText 中文本

Ÿ 使用 Intent 向系统内置的电话拨号器发送意图拨打电话

Ÿ 注册拨打电话权限

4.2. 查看程序错误信息

Ÿ Android 程序中如果出错,错误不会显示在 Console 中,而是显示在 LogCat 界面下。可以从 window  – show view 中打开

Ÿ 日志信息分为 5 个级别: verbose > debug > info > warn > error  高级的包含低级的

Ÿ 可以创建过滤器对日志进行过滤显示,点击绿色加号,可以按照 tag 、 pid 、 level进行筛选

4.3. 将程序安装到真实手机

Ÿ 在电脑上安装手机驱动

有些手机自带驱动,有些没有,可以从官网下载。或者可以使用“豌豆荚”软件自动安装。

Ÿ 在手机设置中打开 USB 调试,将手机用 USB 数据线连接到电脑

我的手机是:三星  i9100   

双核1228MHz、1GB RAM 、4GB ROM、480×800 像素 、Android 2.3、4.3 英寸

Ÿ 检查 Eclipse 的设备管理器中是否显示出新设备

如果未能显示出设备,检查驱动安装是否正常, USB 调试是否打开

Ÿ Eclipse 安装程序

Eclipse 上右键点击工程  – Run as – Android Application – 自动安装运行

Ÿ 手动打包安装

右键点击工程 – Export – Export Android Application – 选择或创建密钥对程序签名并打包生成 apk 文件

将 apk 文件放到手机的 SD 卡中,通过手机文件浏览器执行安装

4.4. 短信发送

Ÿ 搭建界面需要组件: TextView 、 EditText 、 Button

Ÿ 给 Button 添加监听器,当被点击的时候获取号码,获取内容

Ÿ 使用 SmsManager 发送短信

Ÿ 需要注册短信发送权限

4.5. 布局

Ø RelativeLayout (相对布局)

Ÿ android-sdk-windows/docs/guide/topics/ui/layout-objects.html#relativelayout

Ø TableLayout (表格布局)

android-sdk-windows/docs/guide/topics/ui/layout-objects.html#tablelayout

Ø FrameLayout (帧布局)

android-sdk-windows/docs/guide/topics/ui/layout-objects.html#framelayout

setRequestedOrientation(ActivityInfo. SCREEN_ORIENTATION_LANDSCAPE );

setRequestedOrientation(ActivityInfo. SCREEN_ORIENTATION_PORTRAIT );

4.6. Junit

Ø 项目中添加测试类

Ÿ 在 AndroidManifest.xml 清单文件中添加配置

< instrumentation  android:targetPackage = "cn.itcast.junit"  android:name = "android.test.InstrumentationTestRunner"  />

< uses-library  android:name = "android.test.runner"  />

Ÿ 定义一个类继承 AndroidTestCase ,定义测试方法

Ÿ 在 Outline 视图下右键点击测试方法  – Run as – Android Junit Test

Ø 创建测试项目

Ÿ 创建 Android Test Project

Ÿ 输入项目名,选择一个已存在的工程, Eclipse 可以自动配置 Junit 环境

4.7. 日志信息

Ÿ 在 LogCat 视图中我们可以看到程序的日志信息,也可以在程序中输出信息到 LogCat 中

Ÿ 程序中我们可以使用 Log 类来输出信息

Ÿ System.out 和 System.err 输出的信息也会显示在 LogCat 中,注意 System.out 输出信息是 Info 级别, System.err 是 Warn 级别

5. 文件操作( File 、 XML 、 SharedPreferences )

5.1. 读写文件

Ø 写入文件到 SD 卡

Ÿ 需要在清单文件中注册权限

< uses-permission  android:name = "android.permission.WRITE_EXTERNAL_STORAGE"  />

Ÿ 2.1 版本以下的 SDCard 位置和 2.2 之后版本不同

可以通过Environment.getExternalStorageDirectory()获取当前 SDCard 位置,兼容所有版本

Ÿ 获取 SDCard 状态

通过Environment.getExternalStorageState()方法获取 SDCard 当前状态

常量 Environment.MEDIA_MOUNTED 为已安装

Ø 写入文件

Ÿ 通过 Context. openFileOutput(String name,  int  mode)可以获取一个文件输入流

name 为文件名, mode 为文件模式,有 4 种模式

输出流指向路径为: /data/data/ 包名 /files/ 

Ÿ 文件模式在 Context 中有定义常量

MODE_PRIVATE 私有

MODE_WORLD_READABLE 其他程序可读(不可写)

MODE_WORLD_WRITEABLE 其他程序可写(不可读)

模式可以组合使用,例如:MODE_WORLD_READABLE + MODE_WORLD_WRITEABLE

MODE_APPEND 追加

Ø 读取文件

Ÿ 通过 Context. openFileInput(String name)可以获取一个文件输入流

该输入流可以读取  /data/data/ 包名 /files/  路径下的文件

Ÿ 获取当前程序 Files 文件路径

ContextWrapper.getFilesDir()

5.2. XML

Ø Pull 简介

Ÿ 常见的 XML 解析方式有三种, DOM 、 SAX 、 Pull , Android 系统中推荐使用Pull

Ÿ Pull 解析器是一个开源的 Java 项目, Android 系统内部解析 XML 文件均为此种方式,也可用于 JavaEE 项目

Ÿ Android SDK 中已经集成了 Pull 解析器,无需添加任何 jar 文件

Ÿ Pull 解析器运行方式与 SAX 类似,提供各种事件的判断

Ÿ 官方网站: http://xmlpull.org/

Ø 使用 Pull 解析器解析 XML 文件

Ÿ Xml.newPullParser() 获得解析器

Ÿ parser.setInput(in,  "UTF-8" ) 设置输入流以及编码

Ÿ parser.next() 获取下一个解析事件,得到一个事件代码

Ÿ XmlPullParser中定义了常量来标识各种解析事件

START_DOCUMENT 、 END_DOCUMENT  、 START_TAG  、END_TAG  、 TEXT 

Ø 使用XmlSerializer写出 XML

Ÿ 使用以下方法生成 XML ,和 XML 文档顺序类似

startDocument

startTag

attribute

text

endTag

endDocument

5.3. 偏好设定( SharedPreferences ) 

Ÿ 在程序中保存一些配置参数的时候我们经常使用 SharedPreferences

Context.getSharedPreferences(String name, int  mode)

该方法可以在 /data/data/<package>/shared_pref/ 目录下创建一个以 name 命名的 xml文件, mode 文件为模式

Ÿ 存储偏好

调用edit()方法可以获取一个 Editor 对象,对数据进行存储,存储之后需要调用 commit()保存到文件

Ÿ 读取偏好

获得SharedPreferences之后调用 getString() 、 getInt() 等方法获取其中设置的值

Ÿ 在 Activity 中获取 SharedPreferences

在 Activity 中可以调用 getPreferences( int  mode)方法获得一个SharedPreferences,文件名和 Activity 名一致

6. 数据库( SQLite )

6.1. SQLite 特点

Ÿ Android 平台中嵌入了一个关系型数据库 SQLite ,和其他数据库不同的是 SQLite存储数据时不区分类型

例如一个字段声明为 Integer 类型,我们也可以将一个字符串存入,一个字段声明为布尔型,我们也可以存入浮点数。

除非是主键被定义为 Integer ,这时只能存储 64 位整数

Ÿ 创建数据库的表时可以不指定数据类型,例如:

CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(20))

CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT, name)

Ÿ SQLite 支持大部分标准 SQL 语句,增删改查语句都是通用的,分页查询语句和 MySQL 相同

SELECT * FROM person LIMIT 20 OFFSET 10

SELECT * FROM person LIMIT 10,20

6.2. 创建数据库

Ÿ 定义类继承SQLiteOpenHelper

Ÿ 声明构造函数, 4 个参数

Ÿ 重写 onCreate ()方法

Ÿ 重写 upGrade() 方法

Ÿ 注意: SQLite 数据库中列一旦创建不能修改,如果一定要修改,需要重新创建表,拷贝数据

6.3. CRUD操作

Ÿ 和 JDBC 访问数据库不同,操作 SQLite 数据库无需加载驱动,不用获取连接,直接可以使用

获取 SQLiteDatabase 对象之后通过该对象直接可以执行 SQL 语句

SQLiteDatabase.execSQL()

SQLiteDatabase.rawQuery()

Ÿ getReadableDatabase()和getWritableDatabase()的区别

查看源代码后我们发现getReadableDatabase()在通常情况下返回的就是getWritableDatabase() 拿到的数据库

只有在抛出异常的时候才会以只读方式打开

Ÿ 数据库对象缓存

getWritableDatabase() 方法最后会使用一个成员变量记住这个数据库对象,下次打开时判断是否重用

Ÿ SQLiteDatabase 封装了 insert() 、 delete ()、 update ()、 query ()四个方法也可以对数据库进行操作

这些方法封装了部分 SQL 语句,通过参数进行拼接

6.4. 事务管理

Ÿ 在使用 SQLite 数据库时可以用 SQLiteDatabase类中定义的相关方法控制事务

beginTransaction()  开启事务

setTransactionSuccessful()  设置事务成功标记

endTransaction()  结束事务

Ÿ endTransaction() 需要放在 finally 中执行,否则事务只有到超时的时候才自动结束,会降低数据库并发效率

7. 内容提供者( ContentProvider )

7.1. 什么是内容提供者

Ÿ 内容提供者是 Android 中的四大组件之一,可以将应用中的数据对外进行共享

Ÿ 内容提供者将数据的访问方式统一,不必针对不同数据类型采取不同的访问策略

Ÿ 内容提供者将数据封装,只暴露出我们希望提供给其他程序的数据

Ÿ 内容提供者中数据更改可被监听

7.2. 创建内容提供者

Ÿ 定义类继承 ContentProvider ,根据需要重写内部方法

Ÿ 在清单文件的 <application> 节点下进行配置, <provider> 标签中需要指定 name 和authorities 属性

name 为类名,包名从程序 Package 开始,以“ . ”开始

authorities :是访问 Provider 时的路径,要唯一

Ÿ URI 代表要操作的数据,由 scheme 、 authorites 、 path 三部分组成

content:// cn.itcast. sqlite . provider / person

scheme :固定为 content ,代表访问内容提供者

authorites : <provider> 节点中的 authorites 属性

path :程序定义的路径,可根据业务逻辑定义

7.3. 完成 CRUD 方法

Ÿ 当程序调用 CRUD 方法时会传入 Uri

Ÿ 我们通过 Uri 判断调用者要操作的数据

可以使用工具类 UriMatcher 来判断 Uri

addURI 方法可以添加 Uri

match 方法可以匹配一个 Uri 判断其类型

Ÿ 根据业务逻辑操作数据

7.4. 访问内容提供者

Ÿ 通过 Context 获得 ContentResolver 对象

Ÿ 调用 ContentResolver 对象的方法即可访问内容提供者

7.5. 完成 getType 方法

Ÿ 如果返回数据是单条数据:vnd.android.cursor.item 

Ÿ 如果返回数据是多条数据:vnd.android.cursor.dir

7.6. 监听内容提供者数据变化

Ÿ 在内容提供者中可以通知其他程序数据发生变化

通过 Context 的 getContentResolver() 方法获取 ContentResolver

调用其notifyChange() 方法发送数据修改通知

Ÿ 在其他程序中可以通过ContentObserver监听数据变化

通过 Context 的 getContentResolver() 方法获取 ContentResolver

调用其registerContentObserver() 方法指定对某个 Uri 注册 ContentObserver

自定义ContentObserver,重写 onChange() 方法获取数据

7.7. GIT 获取源代码

Ø 资源地址

Ÿ Git

http://code.google.com/p/msysgit/

Ÿ 源码

https://github.com/android

注意:

GIT1.7.7 安装后不能卸载,可以用其他版本覆盖后再卸载。

使用 GIT 时不要使用中文目录,否则 GIT GUI 会报错无法启动。删除 C 盘中 .gitconfig文件可以解决。

7.8. 监听短信

Ÿ Android 系统提供了 Provider 对短信进行查询,当发出短信时也会发送更改通知

Ÿ 短信数据库在  com.android.providers.telephony

Ÿ 定义一个 Observer 监听 "content://sms"

Ÿ 在 onChange() 方法中查询 "content://sms"

Ÿ 需要权限android.permission.READ_SMS

7.9. 操作联系人

Ø 获取所有联系人

Ÿ Android 系统中的联系人也是通过 ContentProvider 来对外提供数据的

Ÿ 数据库路径为: /data/data/com.android.providers.contacts/database/contacts2.db

Ÿ 我们需要关注的有 3 张表

raw_contacts :其中保存了联系人 id

data :和 raw_contacts 是多对一的关系,保存了联系人的各项数据

mimetypes :为数据类型

Ÿ Provider 的 authorites 为 com.android.contacts

Ÿ 查询 raw_contacts 表的路径为: contacts

Ÿ 查询 data 表的路径为: contacts/#/data

这个路径为连接查询,直接查询“ mimetype ”字段就可以根据“ mimetype_id ”查询到 mimetypes 表中的数据

Ÿ 先查询 raw_contacts 得到每个联系人的 id ,在使用 id 从 data 表中查询对应数据,根据 mimetype 分类数据

Ø 通过电话号码获取联系人

Ÿ 系统内部提供了根据电话号码获取 data 表数据的功能,路径为: data/phones/filter/*

Ÿ 用电话号码替换“ * ”部分就可以查到所需数据,获取“ display_name ”可以获取到联系人显示名

Ø 添加联系人

Ÿ 先向 raw_contacts 表插入 id ,路径为: raw_contacts

Ÿ 得到 id 之后再向 data 表插入数据,路径为: data

Ø 使用事务添加联系人

Ÿ 在添加联系人得时候是分多次访问 Provider ,如果在过程中出现异常,会出现数据不完整的情况,这些操作应该放在一次事务中

Ÿ 使用ContentResolver的applyBatch(String authority,ArrayList<ContentProviderOperation> operations) 方法可以将多个操作在一个事务中执行

Ÿ 文档位置 :

file:///F:/Itcast/lib/android-sdk-windows/docs/reference/android/provider/ContactsContract.RawContacts.html

8. 网络通信

8.1. 获取文本数据

Ÿ 通过 URL 对象封装地址,打开一个 HttpURLConnection

Ÿ 设置头信息之后获取响应码,如果成功返回 200 即可从 HttpURLConnection 中获取输入流读取数据

Ÿ 代码过长屏幕显示不全可以使用 <ScrollView> 进行显示

Ÿ 需要访问网络的权限

< uses-permission  android:name = "android.permission.INTERNET"  />

8.2. 获取网络图片

Ÿ 通过 BitmapFactory 的 decodeByteArray(byte[] data, int offset, int length)方法将数据转换为图片对象

8.3. 获取 XML

Ÿ 使用 URL 封装路径,打开一个 HttpURLConnection

Ÿ 设置头信息之后获取相应码,从输入流中获取数据

Ÿ 使用 XmlPullPaser 解析

8.4. 获取 JSON

Ÿ 使用 URL 封装路径,打开一个 HttpURLConnection

Ÿ 设置头信息之后获取相应码,从输入流中获取数据

Ÿ 将数据转为 String ,封装成 JSONArray 对象

Ÿ 遍历 JSONArray 对象,调用获取其中的 JSONObject

Ÿ 再从 JSONObject 中获取每个字段的信息

8.5. 发送 GET 请求

Ÿ 拼接路径和参数,通过 URL 进行封装,打开一个 HttpURLConnection ,发送请求

Ÿ 如果参数是中文会出现乱码

Ÿ URL 中包含的中文参数需要使用 URLEncoder 进行编码

Ÿ 服务器端如果是 TOMCAT ,其默认使用 ISO8859-1 编码,接收时需要处理编码问题

8.6. 发送 POST 请求

Ÿ 通过 URL 打开一个 HttpURLConnection

Ÿ 头信息中除了超时时间和请求方式之外还必须设置Content-Type和Content-Length

Ÿ 从 HttpURLConnection 获得输出流输出参数数据

Ÿ 服务端可以使用 request 对象的 setCharacterEncoding方法设置编码

8.7. 发送 XML ,访问 WebService

Ø 发送 XML

Ÿ 通过 URL 封装路径打开一个 HttpURLConnection

Ÿ 设置请求方式,Content-Type和Content-Length

XML 文件的 Content-Type为:text/xml; charset=UTF-8

Ÿ 使用 HttpURLConnection 获取输出流输出数据

Ø WebService

Ÿ WebService 是发布在网络上的 API ,可以通过发送 XML 调用, WebService 返回结果也是 XML 数据

Ÿ WebService 没有语言限制,只要可以发送 XML 数据和接收 XML 数据即可

Ÿ  http://www.webxml.com.cn  网站上提供了一些 WebService 服务,我们可以对其进行调用

Ÿ  http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?op=getMobileCodeInfo  中提供了电话归属地查询的使用说明 

8.8. HTTP 协议上传文件

Ÿ 搭建服务器,完成上传功能

Ÿ 使用浏览器上传,查看请求信息

Ø HttpURLConnection

Ÿ 通过 URL 封装路径打开一个 HttpURLConnection

Ÿ 设置请求方式以及头字段:Content-Type、Content-Length、Host

Ÿ 拼接数据发送

Ø Socket

Ÿ 使用 HttpURLConnection 发送时内部有缓存机制,如果上传较大文件会导致内存溢出

Ÿ 我们可以使用 Socket 发送 TCP 请求,将上传数据分段发送

Ø HttpClient

public   void  upload(String name, String password, String path)  throws  Exception {

//  创建 HttpClient 对象

HttpClient client =  new  HttpClient();

//  设置超时事件

client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);

//  创建一个 Post 请求 ,  指定路径

PostMethod postMethod =  new  PostMethod( "http://192.168.1.102:8080/14.Web/LoginServlet" );

//  封装每个表单项

Part[] parts = {  new  StringPart( "name" , name),  new  StringPart( "password" , password),  new  FilePart( "file" ,  new  File(path)) };

//  给 Post 请求设置实体

postMethod.setRequestEntity( new  MultipartRequestEntity(parts, postMethod.getParams()));

//  执行 Post 请求

client.executeMethod(postMethod);

// Post 请求是释放资源

postMethod.releaseConnection();

}

8.9. 多线程断点续传下载器

Ÿ 在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度

Ÿ 手机端下载数据时难免会出现无信号断线、电量不足等情况,所以需要断点续传功能

Ÿ 根据下载数据长度计算每个线程下载的数据位置,程序中开启多个线程并发下载

在请求头中设置 Range 字段就可以获取指定位置的数据,例如: Range: bytes=100-200

Ÿ 在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,下次启动时从记录位置继续下载

Ø 多线程下载

Ÿ 进度条使用 <Progress> 进行配置

默认为圆形进度条,水平进度条需要配置 style 属性, ?android:attr/progressBarStyleHorizontal

使用 android.R.attr. progressBarStyleHorizontal作为样式

Ÿ 当点击下载按钮时开启多线程下载,下载过程中修改进度条进度

设置最大刻度:setMax()

设置当前进度:setProgress()

Ø 断点续传

Ÿ 断点续传需要在下载过程中记录每条线程的下载进度

Ÿ 每次下载开始之前先读取数据库,查询是否有未完成的记录,有就继续下载,没有则创建新记录插入数据库

Ÿ 在每次向文件中写入数据之后,在数据库中更新下载进度

Ÿ 下载完成之后删除数据库中下载记录

Ø Handler 传输数据

Ÿ 主线程中创建的 View 只能在主线程中修改,其他线程只能通过和主线程通信,在主线程中改变 View 数据

Ÿ 我们使用 Handler 可以处理这种需求

主线程中创建 Handler ,重写 handleMessage() 方法

新线程中使用 Handler 发送消息,主线程即可收到消息,并且执行 handleMessage()方法

Ø 动态生成新 View

Ÿ 创建 XML 文件,将要生成的 View 配置好

Ÿ 获取系统服务 LayoutInflater ,用来生成新的 View

LayoutInflater inflater = (LayoutInflater) getSystemService( LAYOUT_INFLATER_SERVICE );

Ÿ 使用inflate( int  resource, ViewGroup root)方法生成新的 View

Ÿ 调用当前页面中某个容器的 addView ,将新创建的 View 添加进来

9. 活动( Activity )

9.1. 创建 Activity

Ø 定义 Activity

Ÿ 定义类继承 Activity

Ÿ 在 AndroidManifest.xml 的 <application> 节点中声明 <activity>

Ø 显式意图创建方式

Ÿ 构造函数,代码少

new  Intent( this , NewActivity. class );

Ÿ 类名形式,灵活,可扩展性强

intent.setClassName( this ,  "cn.itcast.activity.NewActivity" );

Ÿ 包名类名形式,可启动其他程序中的 Activity

intent.setClassName( "cn.itcast.downloader" ,  "cn.itcast.downloader.MainActivity");

Ø 创建 Activity 并传递数据

Ÿ 在意图对象中封装了一个 Bundle 对象,可以用来携带数据

Ÿ 在新 Activity 中可以获得意图对象以获取其中 Bundle 保存的数据

Ø 创建 Activity 获取返回数据

Ÿ 使用startActivityForResult(Intent intent,  int  requestCode) 方法打开 Activity

Ÿ 重写onActivityResult( int  requestCode,  int  resultCode, Intent data) 方法

Ÿ 新 Activity 中调用 setResult( int  resultCode, Intent data) 设置返回数据之后,关闭 Activity 就会调用 onActivityResult方法

Ø 隐式意图创建 Activity

Ÿ 显式意图是指在创建意图时指定了组件,而隐式意图则不指定组件,通过动作、类型、数据匹配对应的组件

Ÿ 在清单文件中定义 <activity> 时需要定义 <intent-filter> 才能被隐式意图启动

Ÿ <intent-filter> 中至少配置一个 <action> 和一个 <category> ,否则无法被启动

Ÿ Intent 对象中设置的 action 、 category 、 data 在 <intent-filter> 必须全部包含才能启动

Ÿ <intent-filter> 中的 <action> 、 <category> 、 <data> 都可以配置多个, Intent 对象中不用全部匹配,每样匹配一个即可启动

Ÿ 如果一个意图可以匹配多个 Activity , Android 系统会提示选择

9.2. 生命周期

Ÿ Acitivity 三种状态

运行: activity 在最前端运行

暂停: activity 可见,但前端还有其他 acti vity ,被覆盖一部分,或者前端 activity 透明

停止: activity 不可见,完全被覆盖

Ÿ 生命周期相关方法

onCreate :创建时调用,或者程序在暂停、停止状态下被杀死之后重新打开时也会调用

onStart : onCreate 之后或者从停止状态恢复时调用

onResume : onStart 之后或者从暂停状态恢复时调用,从停止状态恢复时由于调用onStart ,也会调用 onResume

onPause:进入暂停、停止状态,或者销毁时会调用

onStop:进入停止状态,或者销毁时会调用

onDestroy:销毁时调用

onRestart :从停止状态恢复时调用

Ÿ 保存信息相关方法

onSaveInstanceState:在 Activity 被动的摧毁或停止的时候调用,用于保存运行数据,可以将数据存在在 Bundle 中

onRestoreInstanceState:该方法在 Activity 被重新绘制的时候调用,例如改变屏幕方向, savedInstanceState为onSaveInstanceState保存的数据

9.3. 启动模式

Ÿ 在 AndroidManifest.xml 中的 <activity> 标签中可以配置 android:launchMode 属性,用来控制 Actvity 的启动模式

Ÿ 在 Android 系统中我们创建的 Acitivity 是以栈的形式呈现的

standard :每次调用 startActivity() 启动时都会创建一个新的 Activity 放在栈顶

singleTop :如果启动的 Activity 时,指定 Activity 不在栈顶就创建,如在栈顶,则不再创建

singleTask :如果启动的 Activity 不存在就创建,如果存在直接跳转到指定的 Activity 所在位置

singleInstance :如果启动的 Activity 不存在就创建,如果存在就将指定的 Activity移动到栈顶

9.4. 内存管理

Ÿ Android 系统在运行多个进程时,如果系统资源不足,会强制结束一些进程。优先选择哪个进程来结束是有优先级的。以下顺序靠上的优先结束

空:进程中所有 Activity 都已销毁

后台:进程中有一个停止状态的 Activity

可见:进程中有一个暂停状态的 Activity

前台:进程中正在运行一个 Activity

10. 广播接收者 (BroadcastReceiver)

10.1. 定义广播接收者

Ÿ 定义类继承 BroadcastReceiver ,重写 onReceive 方法

Ÿ 清单文件中声明<receiver>,需要在其中配置<intent-filter>指定接收广播的动作

Ÿ 当接收到匹配广播之后就会执行 onReceive 方法

Ÿ BroadcastReceiver 除了在清单文件中声明,也可以在代码中声明,使用 registerReceiver方法注册 Receiver

10.2. 发送广播

Ø 无序广播

Ÿ 使用sendBroadcast方法发送

Ÿ 被所有广播接收者接收,无序,不可中断

Ÿ 广播时可设置接收者权限,仅当接收者含有权限才能接收

Ÿ 接收者的<receiver>也可设置发送方权限,只接收含有权限应用的广播

Ø 有序广播

Ÿ 使用sendOrderedBroadcast方法发送

Ÿ 接收者可以在<intent-filter>中定义android:priority定义优先级,数字越大优先级越高

Ÿ 被各个广播接收者逐个接收,中途可以中断或者添加数据

abortBroadcast()  

getResultExtras( true ).putString( "data" ,  " 新增数据 " );

10.3. 监听短信接收

Ÿ Android 系统在收到短信的时候会发送一条有序广播,我们如果定义一个接收者接收这个广播,就可以得到短信内容,也可以拦截短信

Ÿ 定义广播接收者接收广播 android.provider.Telephony.SMS_RECEIVED

Ÿ 在 onReceive 方法内部调用 Intent 的 getExtras() 再调用 get(String) 获取其中 pdus字段,得到一个 Object[],其中每一个元素都是一个 byte[]

Ÿ 通过SmsMessage类的createFromPdu方法创建 SmsMessage 对象

Ÿ 从 SmsMessage 对象中即可获取发送者号码、短信内容、发送时间等信息

Ÿ 需要接收短信权限: < uses-permission  android:name ="android.permission.RECEIVE_SMS" />

Ÿ Android 系统中收到短信的通知是一个有序通知,我们如需拦截垃圾短信,可以配置较高的 priority,收到信息进行判断是否abortBroadcast()

10.4. 监听呼出电话

Ÿ 定义广播接收者接收  android.intent.action.NEW_OUTGOING_CALL

Ÿ 需要权限  < uses-permission  android:name = "android.permission.PROCESS_OUTGOING_CALLS" />

Ÿ 在 onReceive 方法中使用 getResultData() 和  setResultData()  方法获取和设置电话号码

10.5. 生命周期

Ÿ 广播接收者的生命周期是非常短暂的,在接收到广播的时候创建, onReceive() 方法结束之后销毁

Ÿ 广播接收者中不要做一些耗时的工作,否则会弹出 Application No Response 错误对话框

Ÿ 最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉

Ÿ 耗时的较长的工作最好放在服务中完成

11. 服务 (Service)

11.1. 基本概念

Ÿ Service 是一种在后台运行,没有界面的组件,由其他组件调用开始。

Ÿ 创建 Service ,定义类继承 Service , AndroidManifest.xml 中定义 <service>

Ÿ 开启 Service ,在其他组件中调用 startService方法

Ÿ 停止 Service ,调用 stopService方法

11.2. 电话录音

需要权限: android.permission.READ_PHONE_STATE

TelephonyManager manager = (TelephonyManager) getSystemService( TELEPHONY_SERVICE );

manager.listen( new  MyListener(), PhoneStateListener. LISTEN_CALL_STATE );

private   final   class  MyListener  extends  PhoneStateListener {

private  String  num ;

private  MediaRecorder  recorder ;

public   void  onCallStateChanged( int  state, String incomingNumber) {

switch  (state) {

case  TelephonyManager. CALL_STATE_RINGING :

num  = incomingNumber;

break ;

case  TelephonyManager. CALL_STATE_OFFHOOK :

try  {

File  file  =  new  File(Environment.getExternalStorageDirectory(),  num  +  "_"  + System.currentTimeMillis() +  ".3gp" );

recorder  =  new  MediaRecorder();

recorder .setAudioSource(AudioSource. MIC );

recorder .setOutputFormat(OutputFormat. THREE_GPP );

recorder .setAudioEncoder(AudioEncoder. AMR_NB );

recorder .setOutputFile( file .getAbsolutePath());

recorder .prepare();

recorder .start();

}  catch  (Exception e) {

e.printStackTrace();

}

break ;

case  TelephonyManager. CALL_STATE_IDLE :

if  ( recorder  !=  null ) {

recorder .stop();

recorder .release();

}

break ;

}

}

}

11.3. 绑定本地服务

Ÿ 使用bindService绑定服务,传入一个自定义的ServiceConnection用来接收 IBinder

Ÿ 定义一个业务接口,其中定义需要的使用的方法

Ÿ 服务中自定义一个 IBinder 继承 Binder 并实现业务接口,在 onBind方法中返回

Ÿ 调用端将 IBinder 转为接口类型,调用接口中的方法即可调用到服务中的方法

11.4. 绑定远程服务

Ÿ 远程绑定服务时无法通过同一个接口来调用方法,这时就需要使用 AIDL 技术

Ÿ 将接口扩展名改为“.aidl”

Ÿ 去掉权限修饰符

Ÿ gen 文件夹下会生成同名接口

Ÿ 将服务中自定义的 IBinder 类改为继承接口中的 S tub

Ÿ ServiceConnection中返回的 IBinder 是代理对象,不能使用强转,改用 S tub.asInterface()

11.5. AIDL 使用自定义类型

Ÿ AIDL 默认只能使用 Java 中基本数据类型和 String 、 List 、 Map , List 和 Map 中的元素类型也只能是这些类型。

Ÿ 如果需要使用其他类型数据,使用的类必须实现 Parcelable 接口以完成序列化和反序列化工作

重写 public void writeToParcel(Parcel dest, int flags)

定义 public static final Parcelable.Creator<Person>  CREATOR

Ÿ 定义该类对应的 AIDL

package  包名

parcelable  类名

Ÿ 在接口 AIDL 中导入该类,注意:即使是同一个包也需要导入

12. 多媒体

12.1. 音频播放器

12.2. 视频播放器

screenSV .getHolder().setType(SurfaceHolder. SURFACE_TYPE_PUSH_BUFFERS );  //  设置缓冲区数据

screenSV .getHolder().setKeepScreenOn( true );  //  设置屏幕保持

screenSV .getHolder().addCallback( new  MyCallback());  //  设置回调函数

player .reset();

player .setDisplay( screenSV .getHolder()); //  设置显式

player .setDataSource( "/mnt/sdcard/1.mp4" ); //  设置数据源

player .prepare(); //  准备

player .seekTo(position); //  跳转到指定位置

player .start();

12.3. 拍照

Ÿ 需要权限

< uses-permission  android:name = "android.permission.CAMERA"  />

Ÿ 打开摄像头

Camera.open()

SDK2.3 之后支持前置摄像头, open 方法可以接收一个 int 参数,用来指定哪个摄像头

Ÿ 设置预览显示位置

setPreviewDisplay(SurfaceHolder holder)

注意 SurfaceView 不在前端显示的时候会被销毁,恢复之后会重绘

Ÿ 开始预览 

startPreview()

将摄像头拍摄画面显示在 SurfaceView 中,在此之前可对摄像头进行参数配置

getParameters() 方法可以获取摄像头的相关参数Parameters,调用其内部方法即可进行配置

Ÿ 自动对焦

autoFocus(AutoFocusCallback cb)

自动对焦是一个异步操作,如果我们向等待自动对焦结束之后才开始拍照,需要传入一个回调对象,在其回调函数中调用拍照方法

Ÿ 拍照 

takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)

拍照也是异步操作,需要通过回调函数来得到拍照之后的数据

注意拍照之后摄像头不回自动回到预览状态,需要重写调用startPreview()方法

12.4. 录像

Ÿ 需要权限

< uses-permission  android:name = "android.permission.RECORD_AUDIO" />

< uses-permission  android:name = "android.permission.CAMERA" />

Ÿ 创建MediaRecorder

new  MediaRecorder()

Ÿ 设置音频输入源

setAudioSource( int  audio_source)

Ÿ 设置视频输入源

setVideoSource( int  video_source)

Ÿ 设置输出格式

setOutputFormat( int  output_format)

Ÿ 设置音频编码器

setAudioEncoder( int  audio_encoder)

Ÿ 设置视频编码器

setVideoEncoder( int  video_encoder)

Ÿ 设置预览显示位置

setPreviewDisplay(Surface sv)

Ÿ 设置输出文件

setOutputFile(String path)

Ÿ 准备录制

prepare()

Ÿ 开始录制

start()

开始录制之前需要结束摄像头的预览

Ÿ 结束录制释放资源

stop()

release()

13. 通知

13.1. 吐司通知

Ÿ 创建通知

Toast.makeText(Context context, CharSequence text,  int  duration)

Toast.makeText(Context context,  int  resId,  int  duration)

Ÿ 发送通知

show()

13.2. 状态栏通知

Ÿ 获取系统通知服务 

NotificationManager nm = (NotificationManager) getSystemService( NOTIFICATION_SERVICE )

Ÿ 创建通知

通过构造函数创建 :  Notification( int  icon, CharSequence tickerText,  long  when)

icon:  通知的图片资源 ID

tickerText:  状态栏中显示的消息内容

when:  时间

Ÿ 创建PendingIntent以供点击时发送

PendingIntent.getActivity(Context context,  int  requestCode, Intent intent,  int  flags)

context:  当前上下文

requestCode:  请求码

intent:  点击时要发送的意图

flags:  类型 ,  PendingIntent中提供了常量选择

Ÿ 设置通知点击事件

调用Notification 对象方法 :  setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent)

context:  当前上下文

contentTitle:  标题

contentText: 内容

contentIntent:  点击时触发的意图

Ÿ 设置通知点击后清除

设置Notification 对象属性 n. flags  = Notification. FLAG_AUTO_CANCEL ;

Ÿ 发送消息

调用Notification对象方法 :  notify( int  id, Notification notification)

13.3. 对话框通知

Ø 普通对话框

new  AlertDialog.Builder( this )  //

.setTitle( " 普通对话框 " )  //

.setMessage( " 普通内容 " )  //

.setCancelable( false )  //

.setPositiveButton( "YES" , listener)  //   listener 为 OnClickListener  监听器对象 ,  监听按钮被选中

.setNeutralButton( "CANCEL" , listener)  //

.setNegativeButton( "NO" , listener)  //

.show();

Ø 列表对话框

new  AlertDialog.Builder( this )  //

.setTitle( " 列表对话框 " )  //

.setCancelable( false )  //

.setItems( items , listener)  //   listener 为 OnClickListener  监听器对象 ,  监听列表项被选中

.show();

Ø 单选对话框

new  AlertDialog.Builder( this )  //

.setTitle( " 单选对话框 " )  //

.setCancelable( false )  //

.setSingleChoiceItems( items , 0, choiceLinstener)  //   0,  为默认选中索引 ,  choiceLinstener 为  OnClickListener  监听器对象 ,  监听单选按钮被选中

.setPositiveButton( " 确定 " , positiveLinstener)  //   positiveLinstener 为  OnClickListener  监听器对象 ,  监听确定按钮点击

.show();

Ø 多选对话框

new  AlertDialog.Builder( this )  //

.setTitle( " 多选对话框 " )  //

.setCancelable( false )  //

.setMultiChoiceItems( items , checkedArr, choiceListener)  //   checkedArr 为默认选中 ,  choiceListener 为  OnMultiChoiceClickListener  监听器对象 ,  监听多选按钮被选中

.setPositiveButton( " 确定 " , positiveLinstener)  //   positiveLinstener 为  OnClickListener  监听器对象 ,  监听确定按钮点击

.show();

Ø 进度对话框

ProgressDialog dialog =  new  ProgressDialog( this );

dialog.setProgressStyle(ProgressDialog. STYLE_HORIZONTAL ); // 设置进度条样式

dialog.setTitle( " 下载中 " );

dialog.setMessage( " 请稍候 ..." );

dialog.setCancelable( false );

dialog.setMax(100);

dialog.show();

dialog.setProgress(10); //  设置进度

dialog.dismiss(); //  对话框结束

关于通知的文档位置: android-sdk-windows/docs/guide/topics/ui/notifiers/index.html

14. 常用 UI

14.1. 列表视图 (ListView)

Ø XML 配置

Ÿ 在主界面中配置 <ListView> 标签

Ÿ 在 res/layout/ 文件夹下创建一个新的 xml 文件指定每个条目的布局

Ø Java 代码构建 ListView

Ÿ 获取 ListView 对象

Ÿ 设置一个 Adapter

BaseAdapter :实现内部抽象方法

SimpleAdapter:以 List<Map<String, ?>> 形式封装数据

SimpleCursorAdapter:以 Cursor 对象封装数据, Cursor 中需要有“ _id ”一列

Ÿ 添加 OnItemClickListener

调用 ListView 的 getItemAtPosition(int) 方法可以获取封装数据的容器

如果传入的是 BaseAdapter ,获取到的就是我们自定义方法中返回的内容

如果传入的是SimpleAdapter,获取到的就是一个 Map<String, ?>

如果传入的是SimpleCursorAdapter,获得到的就是一个 Cursor ,并且 Cursor 以指向选中的一条记录

14.2. 单选 (RadioGroup)

Ÿ 定义 <RadioGroup>

Ÿ 在 <RadioGroup> 中定义 <RadioButton> 和 <Button>

Ÿ 处理 Button 的点击事件

Ÿ 根据 ID 获取 RadioGroup 对象,调用其 getCheckedRadioButtonId()方法可以获取其中被选中的RadioGroup 的 ID

Ø 代码

< RadioGroup

     android:id = "@+id/lessonsRG"

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"

     android:orientation = "horizontal"   >

     < RadioButton

         android:id = "@+id/javaRB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = "Java"   />

     < RadioButton

         android:id = "@+id/netRB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = ".Net"   />

     < RadioButton

         android:id = "@+id/phpRB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = "PHP"   />

     < Button

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:onClick = " onR radioClick"

         android:text = " 确定 "   />

</ RadioGroup >

public   void  onRradioClick(View view) {

RadioGroup lessonRG = (RadioGroup) findViewById(R.id. lesson s RG );

int  id = lessonRG.getCheckedRadioButtonId();  //  获取选中的 id

String msg =  null ;

switch  (id) {

case  R.id. javaRB :

msg =  "Java" ;

break ;

case  R.id. netRB :

msg =  ".Net" ;

break ;

case  R.id. phpRB :

msg =  "PHP" ;

break ;

}

Toast.makeText( this , msg, 0).show();

}

14.3. 多选 (CheckBox)

Ÿ 定义若干 <CheckBox> 和一个 <Button>

Ÿ 处理 Button 的点击事件

Ÿ 根据 ID 获取每个 CheckBox ,调用其 isChecked()方法判断是否被选中

Ø 代码

< LinearLayout

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"   >

     < CheckBox

         android:id = "@+id/javaCB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = "Java"   />

     < CheckBox

         android:id = "@+id/netCB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = ".Net"   />

     < CheckBox

         android:id = "@+id/phpCB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = "PHP"   />

     < Button

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:onClick = "checkboxOnClick"

         android:text = " 确定 "   />

</ LinearLayout >

public   void  checkboxOnClick(View view) {

CheckBox javaCB = (CheckBox) findViewById(R.id. javaCB );

CheckBox netCB = (CheckBox) findViewById(R.id. netCB );

CheckBox phpCB = (CheckBox) findViewById(R.id. phpCB );

StringBuilder sb =  new  StringBuilder();

sb.append(javaCB.isChecked() ? javaCB.getText() +  " "  :  "" );

sb.append(netCB.isChecked() ? netCB.getText() +  " "  :  "" );

sb.append(phpCB.isChecked() ? phpCB.getText() +  " "  :  "" );

Toast.makeText( this , sb, 0).show();

}

14.4. 下拉列表 ( Spinner )

Ÿ 定义 <Spinner> 标签

Ÿ 创建一个适配器

Ÿ 获取 Spinner 标签,调用 setAdapter(SpinnerAdapter adapter)方法设置一个适配器

Ÿ 调用setOnItemSelectedListener(OnItemSelectedListener listener)方法设置监听器监听选中事件

Ø XML 配置

< Spinner

     android:id = "@+id/spinner"

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"   />

Ø 使用字符串构建适配器

private   void  setSpinnerByString() {

final  Spinner spinner = (Spinner) findViewById(R.id. spinner );

ArrayAdapter<String> adapter =  new  ArrayAdapter<String>( this , android.R.layout. simple_spinner_item );  //   设置样式

adapter.setDropDownViewResource(android.R.layout. simple_spinner_dropdown_item ); //  设置下拉后样式

adapter.add( "Java" );

adapter.add( ".Net" );

adapter.add( "PHP" );

spinner.setAdapter(adapter);

spinner.setOnItemSelectedListener( new  OnItemSelectedListener() {

public   void  onItemSelected(AdapterView<?> parent, View view,  int  position,  long  id) {

String selection = (String) spinner.getItemAtPosition(position);

Toast.makeText(getApplicationContext(), selection, 0).show();

}

public   void  onNothingSelected(AdapterView<?> parent) {

}

});

}

Ø 使用 JavaBean 构建适配器

private   void  setSpinnerByJavaBean() {

final  Spinner spinner = (Spinner) findViewById(R.id. spinner );

ArrayAdapter<User> adapter =  new  ArrayAdapter<User>( this , android.R.layout.simple_spinner_item );

adapter.setDropDownViewResource(android.R.layout. simple_spinner_dropdown_item );

adapter.add( new  User(1,  "lhm" ,  "lhm@itcast.cn" ));

adapter.add( new  User(2,  "yzk" ,  "yzk@itcast.cn" ));

adapter.add( new  User(3,  "hsp" ,  "hsp@itcast.cn" ));

spinner .setAdapter(adapter);

spinner .setOnItemSelectedListener( new  OnItemSelectedListener() {

public   void  onItemSelected(AdapterView<?> parent, View view,  int  position,  long  id) {

User selection = (User)  spinner .getItemAtPosition(position);

Toast.makeText(getApplicationContext(), selection.getName(), 0).show();

}

public   void  onNothingSelected(AdapterView<?> parent) {

}

});

}

Ø 使用资源文件构建适配器

< string-array   name = "items" >

     < item > Java </ item >

     < item > .Net </ item >

     < item > PHP </ item >

</ string-array >

private   void  setSpinnerByResource() {

final  Spinner spinner = (Spinner) findViewById(R.id. spinner );

ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( this, R.array. items , android.R.layout. simple_spinner_item );

adapter.setDropDownViewResource(android.R.layout. simple_spinner_dropdown_item );

spinner .setAdapter(adapter);

spinner .setOnItemSelectedListener( new  OnItemSelectedListener() {

public   void  onItemSelected(AdapterView<?> parent, View view,  int  position,  long  id) {

CharSequence selection = (CharSequence)  spinner .getItemAtPosition(position);

Toast.makeText(getApplicationContext(), selection, 0).show();

}

public   void  onNothingSelected(AdapterView<?> parent) {

}

});

}

Ø 自定义适配器样式

<? xml   version = "1.0"   encoding = "utf-8" ?>

< LinearLayout   xmlns:android = "http://schemas.android.com/apk/res/android"

     android:layout_width = "match_parent"

     android:layout_height = "match_parent"

     android:orientation = "horizontal"   >

     < ImageView

         android:layout_width = "50dp"

         android:layout_height = "50dp"

         android:src = "@android:drawable/ic_delete"   />

     < TextView

         android:id = "@+id/content"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:textSize = "50sp"   />

</ LinearLayout >

private   void  setSpinnerByCustom() {

final  Spinner spinner = (Spinner) findViewById(R.id. spinner );

ArrayAdapter<CharSequence> adapter =  new  ArrayAdapter<CharSequence>( this , R.layout. item , R.id. content );

adapter.add( "Java" );

adapter.add( ".Net" );

adapter.add( "PHP" );

spinner .setAdapter(adapter);

spinner .setOnItemSelectedListener( new  OnItemSelectedListener() {

public   void  onItemSelected(AdapterView<?> parent, View view,  int  position,  long  id) {

String selection = (String)  spinner .getItemAtPosition(position);

Toast.makeText(getApplicationContext(), selection, 0).show();

}

public   void  onNothingSelected(AdapterView<?> parent) {

}

});

}

14.5. 菜单 (Menu)

Ø 添加菜单项

Ÿ 重写 Actvity 的 onCreateOptionsMenu(Menu menu)方法

Ÿ 添加菜单项

调用方法中参数 menu 的 add(CharSequence title) 方法

Ÿ 添加子菜单

调用 menu 对象的 addSubMenu( final  CharSequence title)

该方法返回一个SubMenu对象

Ÿ 添加子菜单的菜单项

调用SubMenu对象的add(CharSequence title) 方法

Ø 处理菜单点击事件

Ÿ 重写 Activity 的 onOptionsItemSelected(MenuItem item) 方法

参数 item 即为被选中的菜单项

Ø 代码

public   boolean  onCreateOptionsMenu(Menu menu) {

menu.add( " 增加 " );

menu.add( " 修改 " );

menu.add( " 删除 " );

SubMenu subMenu = menu.addSubMenu( " 查询 " );

subMenu.add( " 按照序号查询 " );

subMenu.add( " 按照姓名查询 " );

subMenu.add( " 按照邮箱查询 " );

return   super .onCreateOptionsMenu(menu);

}

public   boolean  onOptionsItemSelected(MenuItem item) {

Toast.makeText( this , item.getTitle(), 0).show();

return   super .onOptionsItemSelected(item);

}

14.6. 内容提示文本框 ( AutoCompleteTextView)

Ø 单次提示

Ø 代码

< AutoCompleteTextView

     android:id = "@+id/actv"

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"

     android:completionThreshold = "1"   />

private   void  setAutoCompleteTextView() {

AutoCompleteTextView actv = (AutoCompleteTextView) findViewById(R.id. actv );

String[] items = {  "tom" ,  "tony" ,  "terry" ,  " 张孝祥 " ,  " 张海军 " ,  " 张泽华 "  };

ArrayAdapter<String> adapter =  new  ArrayAdapter<String>( this , android.R.layout. simple_dropdown_item_1line , items);

actv.setAdapter(adapter);

}

Ø 多次提示

Ø 代码

< MultiAutoCompleteTextView

     android:id = "@+id/mactv"

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"

android:completionThreshold = "1"   />

private   void  setMultiAutoCompleteTextView() {

MultiAutoCompleteTextView mactv = (MultiAutoCompleteTextView) findViewById(R.id. mactv );

String[] items = {  "tom" ,  "tony" ,  "terry" ,  " 张孝祥 " ,  " 张海军 " ,  " 张泽华 "  };

ArrayAdapter<String> adapter =  new  ArrayAdapter<String>( this , android.R.layout. simple_dropdown_item_1line , items);

mactv.setAdapter(adapter);

mactv.setTokenizer( new  MultiAutoCompleteTextView.CommaTokenizer());

}

14.7. 手势识别 ( GestureOverlayView)

Ø 创建手势库

Ÿ 导入 SDK 中的工程

android-sdk-windows\samples\android-8\GestureBuilder

这个工程不能直接导入,需要添加三个配置文件:.classpath、.project、default.properties

Ÿ 将工程部署到手机中,创建手势库

手势库会存储在手机 SD 卡的根目录,文件名为: gestures

Ø 代码

将gestures放入 res/raw 文件夹下

< android.gesture.GestureOverlayView

     android:id = "@+id/gov"

     android:layout_width = "fill_parent"

     android:layout_height = "fill_parent"

android:gestureStrokeType = "multiple"   />

GestureOverlayView gov = (GestureOverlayView) findViewById(R.id. gov );

final  GestureLibrary library = GestureLibraries.fromRawResource( this , R.raw. gestures );

library.load();

gov.addOnGesturePerformedListener( new  OnGesturePerformedListener() {

public   void  onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {

ArrayList<Prediction> list = library.recognize(gesture);

for  (Prediction p : list)

System. out .println(p. name  +  ": "  + p. score );

}

});

14.8. 网页视图 (WebView)

Ø 代码

< WebView

     android:id = "@+id/webView"

     android:layout_width = "fill_parent"

     android:layout_height = "fill_parent"   />

WebView webView = (WebView) findViewById(R.id. webView );

webView.getSettings().setBuiltInZoomControls( true ); //  放大缩小按钮

webView.getSettings().setJavaScriptEnabled( true ); // JS 允许 

webView.setWebChromeClient( new  WebChromeClient()); // Chrome 内核

webView.loadUrl( "http://192.168.1.10 0 :8080" );

15. 样式与主题

15.1. 样式

Ø 定义样式

Ÿ 设置样式,在 values 文件夹下的任意文件中的 <resources>中配置 <style> 标签

< style  name = " style 1" >

< item  name = "android:layout_width" > fill_parent </ item >

< item  name = "android:layout_height" > wrap_content </ item >

</ style >

Ÿ 继承样式,在 <style> 标签中配置属性 parent

< style  name = " style2 "  parent = "@style/ style 1" >

< item  name = "android:textColor" > #FF0000 </ item >

</ style >

Ÿ 继承样式,在 name 中引用其他样式

< style  name = " style 2. style 3" >

< item  name = "android:textSize" > 30sp </ item >

</ style >

Ø 使用样式

Ÿ 在 layout 文件的标签中配置 style 属性

< Button  

style = "@style/ style2.style3 "

     android:text = " 这是 一个按钮 "

/>

15.2. 主题

Ÿ 定义过的样式也可以应用在 <activity> 和 <application> 标签中,使用 theme属性尽心配置

< style  name = "theme" >

< item  name = "android:windowNoTitle" > true </ item >

< item  name = "android:windowFullscreen" > ?android:windowNoTitle </ item >

</ style >

< activity  android:name = ".MainActivity"

                   android:label = "@string/app_name"

                   android:theme = "@style/theme"

>

Ÿ ?  表示引用其他属性的值

Ÿ @  表示访问资源文件

Ÿ 如果使用 android 内置的样式, IDE 自动提示的“ _ ”要替换成“ . ”

16. 国际化与屏幕适配

16.1. 国际化

Ÿ 在 values 和 drawable 文件夹后加上语言以及地区名,程序中需要国际化的部分使用资源 ID

values-en-rUK

values-en-rUS

values-zh-rCN

values-zh-rTW

Ÿ 匹配规则

在匹配资源时先会找语言、地区完全匹配的

如果没有地区匹配的,则查找语言匹配的

如果没有语言匹配的则找默认 values

16.2. 屏幕适配

Ÿ 在 layout 文件夹后加上分辨率,系统会根据屏幕尺寸自动选择

注意分辨率中的乘号是“ x ”不是“ * ”

Ÿ 如果没有匹配的分辨率会找默认 layout 文件夹

17. 动画特效

17.1. Frame

Ÿ 通过多个画面连续播放实现动画效果

Ÿ 详见文档 android-sdk-windows/docs/guide/topics/resources/animation-resource.html

17.2. Tween

Ÿ 将某个组件以渐变的方式实现透明、缩放、移动、旋转等动画效果

Ÿ 详见文档 android-sdk-windows/docs/guide/topics/resources/animation-resource.html

17.3. 使用动画切换 Activity

Ÿ 在 startActivity() 方法调用之后调用 overridePendingTransition( int  enterAnim,  int exitAnim)方法

enterAnim 进入的动画资源 id

exitAnim 退出的动画 资源 id

17.4. 使用动画翻页

Ÿ XML 配置

< ViewFlipper

android:id = "@+id/viewFlipper"

android:layout_width = "fill_parent"

android:layout_height = "fill_parent"

>

    < ImageView

     android:layout_width = "wrap_content"

     android:layout_height = "wrap_content"

    android:src = "@drawable/bb2"

    />

    < ImageView

     android:layout_width = "wrap_content"

     android:layout_height = "wrap_content"

    android:src = "@drawable/bb3"

    />

</ ViewFlipper >

Ÿ Java 代码

public   boolean  onTouchEvent(MotionEvent event) {

ViewFlipper viewFlipper = (ViewFlipper) findViewById(R.id. viewFlipper );

switch  (event.getAction()) {

case  MotionEvent. ACTION_DOWN :

start  = event.getX();

break ;

case  MotionEvent. ACTION_UP :

float  end = event.getX();

if  (end >  start ) {

viewFlipper .setInAnimation( this , R.anim. previous_enter );

viewFlipper .setOutAnimation( this , R.anim. previous_exit );

viewFlipper .showPrevious();

}  else   if  (end <  start ) {

viewFlipper .setInAnimation( this , R.anim. next_enter );

viewFlipper .setOutAnimation( this , R.anim. next_exit );

viewFlipper .showNext();

}

break ;

}

return   super .onTouchEvent(event);

}

18. 其他

18.1. 传感器

Ø 传感器参数

Ÿ 传感器类型

方向 Sensor. TYPE_ORIENTATION

加速 Sensor. TYPE_ACCELEROMETER

光线 Sensor. TYPE_LIGHT

磁场 Sensor. TYPE_MAGNETIC_FIELD

距离 Sensor. TYPE_PROXIMITY

温度 Sensor. TYPE_TEMPERATURE

Ÿ 传感器反应速度

SensorManager. SENSOR_DELAY_FASTEST

SensorManager. SENSOR_DELAY_GAME

SensorManager. SENSOR_DELAY_UI

SensorManager. SENSOR_DELAY_NORMAL

Ø 使用方向传感器

Ÿ 获得传感器管理器

SensorManager manager = (SensorManager) getSystemService( SENSOR_SERVICE );

Ÿ 获得方向传感器

Sensor sensor = manager.getDefaultSensor(Sensor. TYPE_ORIENTATION );

Ÿ 注册监听器

manager .registerListener( listener ,  sensor , SensorManager. SENSOR_DELAY_NORMAL );

Ÿ 监听器

private   final   class  MySensorEventListener  implements  SensorEventListener {

public   void  onSensorChanged(SensorEvent event) {

System. out .println(event. values [0]);

}

public   void  onAccuracyChanged(Sensor sensor,  int  accuracy) {

}

}

Ÿ 取消监听器

manager .unregisterListener( listener ,  sensor );

18.2. 触摸事件

Ø 拖拽

Ÿ XML 配置

< ImageView

     android:id = "@+id/image"

     android:layout_width = "wrap_content"

     android:layout_height = "wrap_content"

     android:scaleType = "matrix"

     android:src = "@drawable/image"   />

Ÿ Java代码

ImageView imageView = (ImageView) findViewById(R.id. image );

imageView.setOnTouchListener( new  MyOnTouchListener());

private   class  MyOnTouchListener  implements  OnTouchListener {

private   float   x ;

private   float   y ;

private  Matrix  currentMatrix  =  new  Matrix(); //  用来操作图片的矩阵

private  Matrix  oldMatrix  =  new  Matrix();

public   boolean  onTouch(View v, MotionEvent event) {

switch  (event.getAction()) {

case  MotionEvent. ACTION_DOWN :  //  按下时

x  = event.getX();  //  获取 x 轴坐标

y  = event.getY(); //  获取 y 轴坐标

oldMatrix .set( imageView .getImageMatrix()); //  记住位置

break ;

case  MotionEvent. ACTION_MOVE :  //  移动时

currentMatrix .set( oldMatrix );  //  设置成按下时记住的位置

currentMatrix .postTranslate(event.getX() -  x , event.getY() -  y ); //  改变位置

break ;

}

imageView .setImageMatrix( currentMatrix ); //  移动图片

return   true ;

}

}

Ø 多点触摸

private   class  MyOnTouchListener  implements  OnTouchListener {

private   float   x ; //  图片移动前的 x 轴坐标

private   float   y ; //  图片移动前的 y 轴坐标

private  Matrix  currentMatrix  =  new  Matrix();  //  用来移动图片的矩阵

private  Matrix  oldMatrix  =  new  Matrix(); //  图片移动前的矩阵

private   int   type ; //  操作类型 ,  一根手指触摸还是两根手指触摸

private   float   start ; //  第二根手指按下时的距离

private   float   end ; //  两根手指移动后的距离

private  PointF  point ; //  放大时的中心点

public   boolean  onTouch(View v, MotionEvent event) {

switch  (event.getAction() & MotionEvent. ACTION_MASK ) {

case  MotionEvent. ACTION_DOWN :

type  = 1;

x  = event.getX();

y  = event.getY();

oldMatrix .set( imageView .getImageMatrix());

break ;

case  MotionEvent. ACTION_MOVE :

currentMatrix .set( oldMatrix );

if  ( type  == 1) {  // 1 根手指触摸

currentMatrix .postTranslate(event.getX() -  x , event.getY() -  y );

}  else  {  // 2 跟手指触摸

end  = countDistance(event);  //  计算结束时距离

float  scale =  end  /  start ;  //  计算缩放比例

currentMatrix .postScale(scale, scale,  point . x ,  point . y );  //  设置缩放

}

break ;

case  MotionEvent. ACTION_POINTER_DOWN :

type  = 2;

start  = countDistance(event);  //  计算开始时距离

point  = countPoint(event);  //  计算中心点

oldMatrix .set( imageView .getImageMatrix());

break ;

}

imageView .setImageMatrix( currentMatrix );  //  改变图片

return   true ;

}

}

public   float  countDistance(MotionEvent event) {

float  a = event.getX(1) - event.getX(0);  // x 轴距离

float  b = event.getY(1) - event.getY(0);  // y 轴距离

return  ( float ) Math.sqrt(a * a + b * b);  //  勾股定理

}

public  PointF countPoint(MotionEvent event) {

float  x = (event.getX(0) + event.getX(1)) / 2;  // x 轴中间点

float  y = (event.getY(0) + event.getY(1)) / 2;  // y 轴中间点

return   new  PointF(x, y);

}

18.3. 读取 SIM 卡

Ø 电话号码、运营商信息

Ÿ 需要权限

< uses-permission  android:name = "android.permission.READ_PHONE_STATE"  />

< uses-permission  android:name = "android.permission.ACCESS_COARSE_LOCATION"  />

Ÿ Java 代码

TelephonyManager manager = (TelephonyManager) getContext().getSystemService(Context. TELEPHONY_SERVICE );

System. out .println( " 电话号码 : "  + manager.getLine1Number());

System. out .println( " 运营商编号 : "  + manager.getNetworkOperator());

System. out .println( " 运营商名字 : "  + manager.getNetworkOperatorName());

Ø 联系人

Ÿ 需要权限

< uses-permission  android:name = "android.permission.READ_ CONTACTS "  />

< uses-permission  android:name = "android.permission. WRITE _ CONTACTS "  />

Ÿ Java 代码

Uri uri = Uri.parse( "content://icc/adn" );

Cursor c = getContentResolver().query(uri,  null ,  null ,  null ,  null );

while  (c.moveToNext())

System. out .println(c.getString(c.getColumnIndex( "name" )) +  ": "  + c.getString(c.getColumnIndex( "number" )));

Ø 通话记录

Ÿ 需要权限

< uses-permission  android:name = "android.permission.READ_ CONTACTS "  />

< uses-permission  android:name = "android.permission. WRITE _ CONTACTS "  />

Ÿ Java 代码

Uri uri = CallLog.Calls. CONTENT_URI ;

Cursor c = getContentResolver().query(uri,  null ,  null ,  null ,  null );

while  (c.moveToNext())

System. out .println(c.getString(c.getColumnIndex( "number" )) +  ": "  + c.getString(c.getColumnIndex( "type" )));

Ÿ 源代码

ContactsProvider\src\com\android\providers\contacts\ CallLogProvider.java

18.4. 安装程序

Ÿ 需要权限

< uses-permission  android:name = "android.permission. INSTALL_PACKAGES "  />

Ÿ Java 代码

File file =  new  File(Environment.getExternalStorageDirectory(),  " test .apk" );

Intent intent =  new  Intent();

intent.setAction(Intent. ACTION_VIEW );

intent.setDataAndType(Uri.fromFile(file),  "application/vnd.android.package-archive" );

startActivity(intent);

18.5. 关闭程序

Ÿ 杀死当前进程

Process.killProcess(Process.myPid());

Ÿ 退出虚拟机

System.exit(0);

Ÿ 根据包名关闭后台进程

ActivityManager manager = (ActivityManager) getSystemService( ACTIVITY_SERVICE );

manager.restartPackage( "cn.itcast.test" );

< uses-permission  android:name = "android.permission.RESTART_PACKAGES"  />

18.6. 使用 HTML 构建界面

Ÿ HTML

<! DOCTYPE   html   PUBLIC   "-//W3C//DTD HTML 4.01 Transitional//EN"   "http://www.w3.org/TR/html4/loose.dtd" >

< html >

< head >

< meta   http-equiv = "Content-Type"   content = "text/html; charset=UTF-8" >

< title > Insert title here </ title >

< script   type = "text/javascript" >

function  show(jsondata) {

var  jsonobjs = eval(jsondata);

var  table = document.getElementById( "personTable" );

for  (  var  y = 0; y  <  jsonobjs.length; y++) {

var  tr = table.insertRow(table.rows.length);

var  td1 = tr.insertCell(0);

var  td2 = tr.insertCell(1);

td2.align =  "center" ;

var  td3 = tr.insertCell(2);

td3.align =  "center" ;

td1.innerHTML = jsonobjs[y].name;

td2.innerHTML = jsonobjs[y].amount;

td3.innerHTML =  "<a href='javascript:contact.call(\""  + jsonobjs[y].phone +  "\")'>" + jsonobjs[y].phone +  "</a>" ;

}

}

</ script >

</ head >

< body   onload = "javascript:contact.show C ontacts()" >

< table   border = "0"   width = "100%"   id = "personTable"   cellspacing = "0" >

< tr >

< td   width = "30%" > 姓名 </ td >

< td   width = "30%"   align = "center" > 存款 </ td >

< td   align = "center" > 电话 </ td >

</ tr >

</ table >

</ body >

</ html >

Ÿ XML 代码

< WebView

         android:id = "@+id/webView"

         android:layout_width = "fill_parent"

         android:layout_height = "fill_parent"   />

Ÿ Java 代码

public   class  MainActivity  extends  Activity {

private  WebView  webView ;

public   void  onCreate(Bundle savedInstanceState) {

super .onCreate(savedInstanceState);

setContentView(R.layout. main );

webView  = (WebView) findViewById(R.id. webView );

webView .getSettings().setJavaScriptEnabled( true );

webView .loadUrl( "file:///android_asset/index.html" );

webView .addJavascriptInterface( new  Contact(),  "contact" );

}

private   final   class  Contact {

public   void  showContacts() {

String json =  "[{\"name\":\"zxx\", \"amount\":\"99999\", \"phone\":\"18600012345\"}]";

webView .loadUrl( "javascript:show('"  + json +  "')" );

}

public   void  call(String phone) {

startActivity( new  Intent(Intent. ACTION_CALL , Uri.parse( "tel:"  + phone)));

}

}

}

18.7. apk 文件反编译

Ÿ 使用解压缩工具打开 apk 文件,找到其中 dex 文件

Ÿ 创建 Java 工程,导入 dex2jar中的所有 jar 文件

Ÿ 创建运行环境运行其中pxb.android.dex2jar.v3.Main 类, 指定 dex 文件地址,会在同目录下生成 jar 文件

Ÿ 使用jd-gui打开 jar 文件获取源码

1. 前言

1.1. 什么是 3G 、 4G

Ÿ 第三代移动通信技术(3rd - Generation),速率一般在 几百 K bps ,较之前的 2G 和 2.5G 在数据传输速度上有很大提升。

Ÿ 第四代移动通信技术( 4th - Generation ),速度可达到 100Mbps 以上,几乎可以满足人们的所有传输数据的需求。

Ÿ 目前主流的 3G 技术标准有三种:

WCDMA :全球 80% 以上的 3G 网络都是采用此种制式。中国联通运营。 186

CDMA2000 :目前日韩及北美使用较多。中国电信运营。  189

TD-SCDMA :中国自主知识产权的 3G 通信技术。中国移动运营。  188 

Ÿ 目前主流的 4G 技术为 LTE ,但还没有被广泛应用:

GSM à GPRS à EDGE à WCDMA à HSDPA à HSDPA+ à LTE

1.2. 什么是 Android 

Ÿ Android 本义指 “ 机器人 ”,Google 于 2007 年 11 月 5 日宣布的基于 Linux 平台的开源手机操作系统的名称,官方中文名:安卓 。

Ÿ Android 系统早期由原名为 "Android" 的公司开发,谷歌在 2005 年收购 "Android.Inc" 后,继续对 Android 系统开发运营 。

Ÿ 底层 Linux 内核只提供基本功能,其他的应用软件则由各公司自行开发, 大部分程序以 Java 语言编写。

Ÿ 由于 Android 系统的开源特性,很多制造商都在生产 Android 系统的设备,如:摩托罗拉、 HTC 、三星、索爱、 LG 、小米、华为、魅族等。

Ÿ Android 系统除了运行在智能手机上之外,还可以用做平板电脑、电视、汽车等很多设备上。

Ÿ Android 系统架构图:

1.3. Android 的版本升级

Ÿ 1.5 Cupcake(纸杯蛋糕)

拍摄 / 播放影片,并支持上传到 Youtube

支持立体声 蓝牙 耳机,同时改善自动配对性能

最新的采用 WebKit 技术的浏览器,支持复制 / 贴上和页面中搜索

GPS 性能大大提高

提供屏幕虚拟键盘

主屏幕增加音乐播放器和相框 widgets

应用程序自动随着手机旋转

短信、 Gmail 、日暦,浏览器的用户接口大幅改进,如 Gmail 可以批量删除邮件

相机启动速度加快,拍摄图片可以直接上传到 Picasa

来电照片显示

Ÿ 1.6 Donut(甜甜圈)

重新设计的 Android Market 

手势支持 

支持 CDMA 网络 

文字转语音系统( Text-to-Speech ) 

快速搜索框 

全新的拍照接口 

查看应用程序耗电 

支持 虚拟私人网络 ( VPN ) 

支持更多的屏幕分辨率。 

支持 OpenCore2 媒体引擎 

新增面向视觉或听觉困难人群的易用性插件

Ÿ 2.1 Éclair(闪电泡芙)

优化硬件速度

"Car Home" 程序

支持更多的屏幕分辨率

改良的用户界面

新的浏览器的用户接口和支持 HTML5

新的联系人名单

更好的白色 / 黑色背景比率

改进 Google Maps  3.1.2

支持 Microsoft Exchange

支持内置相机闪光灯

支持数码变焦

改进的虚拟键盘

支持蓝牙 2.1

支持动态桌面的设计

Ÿ 2.2 Froyo ( 冻酸奶)

支持将软件安装至扩展内存

集成 Adobe Flash 10.1 支持

加强软件 即时编译 的速度

新增软件启动 " 快速 " 至电话和浏览器

USB 分享器和 WiFi 热点功能

支持在浏览器上传档案

更新 Market 中的批量和自动更新

增加对 Microsoft Exchange 的支持(安全政策 , auto-discovery, GAL look-up )

集成 Chrome 的 V8 JavaScript 引擎 到浏览器

加强快速搜索小工具

更多软件能透过 Market 更新,类似 2.0/2.1 中的 Map 更新

速度和性能优化

Ÿ  2.3 Gingerbread ( 姜饼 )

修补 UI

支持更大的屏幕尺寸和分辨率( WXGA 及更高)

系统级复制粘贴

重新设计的多点触摸屏幕键盘

本地支持多个镜头(用于视频通话等)和更多传感器(陀螺仪、气压计等)

电话簿集成 Internet Call 功能

支持 近场通信 ( NFC ) 

强化电源、应用程序管理功能

新增下载管理员

优化游戏开发支持

多媒体音效强化

从 YAFFS 转换到 ext4 文件系统

开放了屏幕截图功能

对黑色及白色的还原更加真实

Ÿ 3.x Honeycomb ( 蜂巢)

仅供平板电脑使用

Google eBooks 上提供数百万本书

支持平板电脑大萤幕、高分辨率

新版 Gmail

Google Talk 视讯功能

3D 加速处理

网页版 Market(Web store) 详细分类显示,依个人 Android 分别设定安装应用程序

新的短消息通知功能

专为平板电脑设计的用户界面(重新设计的通知列与系统列)

加强多任务处理的接口

重新设计适用大屏幕的键盘及复制粘贴功能

多个标签的浏览器以及私密浏览模式

快速切换各种功能的相机

增强的图库与快速滚动的联络人接口

更有效率的 Email 接口

支持多核心处理器

3.2 优化 7 吋平板显示

Ÿ 4.0 Ice Cream Sandwich ( 奶油三明治)

虚拟按键,增大屏幕面积同时控制手机整体大小

桌面插件 Widgets 列表呈现在标签页中,与程序列表类似并且共存

文件夹更容易创建和管理,与 iOS 类似

可定制的桌面系统

可视语音邮件  

日历支持缩放操作

Gmail 离线搜索,两行预览,以及底部新快捷栏

音量下键 + 电源键组合截图

改进虚拟键盘纠错

从锁屏界面直接访问应用程序

优化复制粘贴

新版浏览器

新的 Roboto 字体

流量控制系统

相机应用

人脸识别,刷脸解锁

内置照片编辑器

多任务列表

新的图库布局和组织方式

联系人应用整合社交网络信息

Android Beam

http://digi.tech.qq.com/a/20111019/001579.htm

1.4. 主流智能手机操作系统

2. 搭建开发环境

2.1. 所需资源

Ÿ  JDK , Java 开发环境。下载地址: http://www.oracle.com

Ÿ  Eclipse , IBM 公司开发的一款开源 IDE 。 http://www.eclipse.org

Ÿ  Android SDK , Android  开发工具,包含开发 Android 程序所需类库、源码、文档、案例等资源。 http://www.android.com

Ÿ  ADT 插件 ,ADT  是  Eclipse  平台下用来开发  Android  应用程序的插件 。 http://www.android.com

2.2. Eclipse 安装 ADT 插件

2.3. 配置 SDK 路径

2.4. 启动虚拟机

Ø 点击机器人图标

Ø 弹出虚拟机管理器

Ø 修改虚拟机默认目录

指定环境变量 android_sdk_home ,通常指定为 SDK 所在目录,可以随意指定。

重启 Eclipse 之后将会以这个目录作为存放虚拟机文件的位置。

Ø 配置虚拟机

Ÿ Name :虚拟机的名字,可随意定义

Ÿ Target :虚拟机版本

Ÿ SD Card - Size :虚拟机的 SDCard 大小,会在本地硬盘创建指定大小的文件用来存储数据,模拟真实手机的 SDCard

如果使用原有文件,可以选择 File 并点击 Browse 指定文件

Ÿ Snapshot : 保存快照,可以提高虚拟机启动速度

Ÿ Skin - Built-in :选择分辨率

HVGA : 480x320

QVGA : 320x240

WQVGA400 : 400x240

WQVGA432 : 432x240

WVGA800 : 800x480

WVGA854 : 854x480

也可以选择 Resolution 自行指定

Ø 成功进入 Android 操作系统界面

Ÿ 如果虚拟机显示无信号,需要对电脑的网络连接进行配置

无论电脑是否联网, IP 地址、子网掩码、默认网关、首选 DNS 服务器都需要配置

Ÿ 电脑如果未连接局域网或互联网,可以见默认网关和首选 DNS 服务器配置成本机IP ,例如:

Ÿ 电脑如果已连接局域网,需要将网关和首选 DNS 服务器进行设置,例如:

Ÿ 如果电脑已连接互联网,正常情况虚拟机不会显示无信号,重启虚拟机

2.5. 命令行操作

为了能在任意目录使用一下命令,需要将 SDK 目录下的 platform-tools文件夹路径和tools文件夹路径配置到 path 环境变量中

Ÿ 列出可以使用的 android 版本

android list targets

Ÿ 列出可以使用的虚拟机

android list avd

Ÿ 创建虚拟机

android create avd –n < 虚拟机名 > -t <Target 版本 ID> -c <SD 卡大小 > -s < 屏幕尺寸 >

Ÿ 启动虚拟机

emulator –avd < 虚拟机名 >

Ÿ 显示已连接的设备

adb devices

Ÿ 导入文件到手机

adb push <Windows 源文件路径 > < 手机目标路径 >

Ÿ 从手机导出文件

adb pull < 手机源文件路径 > <Windows 目录路径 >

Ÿ 安装程序

adb install <apk 文件路径 >

Ÿ 卸载程序

adb uninstall < 包名 >

Ÿ 重启 adb

adb kill-server

abd start-server

3. 开发一个 Android 程序

3.1. 创建 Android 程序

Ø 创建 Android Project

Ÿ Project name :项目名

Ÿ Build Target : Android 版本

Ÿ Application name :程序名,显示在程序列表中,以及程序启动后的标题栏

Ÿ Package name :包名,程序的唯一标识

Ÿ Create Activity :选择程序启动时是否创建一个窗体,设置主窗体名字

Ÿ Min SDK Version :设置运行该程序所需的最低版本

3.2. 安装、卸载程序

Ÿ Eclipse 安装

右键点击工程 – Run as – Android Application

Ÿ 虚拟机卸载

设置 – 应用程序 – 管理应用程序 – 选中要卸载的程序 – 卸载

3.3. 了解项目目录结构

Ÿ src :源代码

Ÿ gen :系统自动生成的文件

R.java 中记录了项目中各种资源 ID

Ÿ res :系统资源,所有文件都会在 R 文件生成资源 ID

drawable :图片

layout :界面布局

values :数据

anim :定义动画的 XML

raw :原生文件

Ÿ assets :资源路径,不会在 R 文件注册

Ÿ project.properties :供 Eclipse 使用,读取该项目使用 Android 版本号。早期版本名为: default.properties

Ÿ AndroidManifest.xml :清单文件,在软件安装的时候被读取

Android 中的四大组件( Activity 、 ContentProvider 、 BroadcastReceiver 、 Service)都需要在该文件中注册

程序所需的权限也需要在此文件中声明,例如:电话、短信、互联网、访问 SD 卡

Ÿ bin :二进制文件,包括 class 、资源文件、 dex 、 apk 等

Ÿ proguard.cfg:用来混淆代码的配置文件,防止别人反编译

3.4. 程序启动过程

Ÿ Eclipse 将 .java 源文件编译成 .class

Ÿ 使用 dx 工具将所有 .class 文件转换为 .dex 文件

Ÿ 再将 .dex 文件和所有资源打包成 .apk 文件

Ÿ 将 .apk 文件安装到虚拟机完成程序安装

Ÿ 启动程序 – 开启进程 – 开启主线程

Ÿ 创建 Activity 对象  – 执行 OnCreate() 方法

Ÿ 按照 main.xml 文件初始化界面

4. 演示案例

4.1. 电话拨号

Ÿ 搭建界面需要组件: TextView 、 EditText 、 Button

Ÿ 当点击 Button 时获取 EditText 中文本

Ÿ 使用 Intent 向系统内置的电话拨号器发送意图拨打电话

Ÿ 注册拨打电话权限

4.2. 查看程序错误信息

Ÿ Android 程序中如果出错,错误不会显示在 Console 中,而是显示在 LogCat 界面下。可以从 window  – show view 中打开

Ÿ 日志信息分为 5 个级别: verbose > debug > info > warn > error  高级的包含低级的

Ÿ 可以创建过滤器对日志进行过滤显示,点击绿色加号,可以按照 tag 、 pid 、 level进行筛选

4.3. 将程序安装到真实手机

Ÿ 在电脑上安装手机驱动

有些手机自带驱动,有些没有,可以从官网下载。或者可以使用“豌豆荚”软件自动安装。

Ÿ 在手机设置中打开 USB 调试,将手机用 USB 数据线连接到电脑

我的手机是:三星  i9100   

双核1228MHz、1GB RAM 、4GB ROM、480×800 像素 、Android 2.3、4.3 英寸

Ÿ 检查 Eclipse 的设备管理器中是否显示出新设备

如果未能显示出设备,检查驱动安装是否正常, USB 调试是否打开

Ÿ Eclipse 安装程序

Eclipse 上右键点击工程  – Run as – Android Application – 自动安装运行

Ÿ 手动打包安装

右键点击工程 – Export – Export Android Application – 选择或创建密钥对程序签名并打包生成 apk 文件

将 apk 文件放到手机的 SD 卡中,通过手机文件浏览器执行安装

4.4. 短信发送

Ÿ 搭建界面需要组件: TextView 、 EditText 、 Button

Ÿ 给 Button 添加监听器,当被点击的时候获取号码,获取内容

Ÿ 使用 SmsManager 发送短信

Ÿ 需要注册短信发送权限

4.5. 布局

Ø RelativeLayout (相对布局)

Ÿ android-sdk-windows/docs/guide/topics/ui/layout-objects.html#relativelayout

Ø TableLayout (表格布局)

android-sdk-windows/docs/guide/topics/ui/layout-objects.html#tablelayout

Ø FrameLayout (帧布局)

android-sdk-windows/docs/guide/topics/ui/layout-objects.html#framelayout

setRequestedOrientation(ActivityInfo. SCREEN_ORIENTATION_LANDSCAPE );

setRequestedOrientation(ActivityInfo. SCREEN_ORIENTATION_PORTRAIT );

4.6. Junit

Ø 项目中添加测试类

Ÿ 在 AndroidManifest.xml 清单文件中添加配置

< instrumentation  android:targetPackage = "cn.itcast.junit"  android:name = "android.test.InstrumentationTestRunner"  />

< uses-library  android:name = "android.test.runner"  />

Ÿ 定义一个类继承 AndroidTestCase ,定义测试方法

Ÿ 在 Outline 视图下右键点击测试方法  – Run as – Android Junit Test

Ø 创建测试项目

Ÿ 创建 Android Test Project

Ÿ 输入项目名,选择一个已存在的工程, Eclipse 可以自动配置 Junit 环境

4.7. 日志信息

Ÿ 在 LogCat 视图中我们可以看到程序的日志信息,也可以在程序中输出信息到 LogCat 中

Ÿ 程序中我们可以使用 Log 类来输出信息

Ÿ System.out 和 System.err 输出的信息也会显示在 LogCat 中,注意 System.out 输出信息是 Info 级别, System.err 是 Warn 级别

5. 文件操作( File 、 XML 、 SharedPreferences )

5.1. 读写文件

Ø 写入文件到 SD 卡

Ÿ 需要在清单文件中注册权限

< uses-permission  android:name = "android.permission.WRITE_EXTERNAL_STORAGE"  />

Ÿ 2.1 版本以下的 SDCard 位置和 2.2 之后版本不同

可以通过Environment.getExternalStorageDirectory()获取当前 SDCard 位置,兼容所有版本

Ÿ 获取 SDCard 状态

通过Environment.getExternalStorageState()方法获取 SDCard 当前状态

常量 Environment.MEDIA_MOUNTED 为已安装

Ø 写入文件

Ÿ 通过 Context. openFileOutput(String name,  int  mode)可以获取一个文件输入流

name 为文件名, mode 为文件模式,有 4 种模式

输出流指向路径为: /data/data/ 包名 /files/ 

Ÿ 文件模式在 Context 中有定义常量

MODE_PRIVATE 私有

MODE_WORLD_READABLE 其他程序可读(不可写)

MODE_WORLD_WRITEABLE 其他程序可写(不可读)

模式可以组合使用,例如:MODE_WORLD_READABLE + MODE_WORLD_WRITEABLE

MODE_APPEND 追加

Ø 读取文件

Ÿ 通过 Context. openFileInput(String name)可以获取一个文件输入流

该输入流可以读取  /data/data/ 包名 /files/  路径下的文件

Ÿ 获取当前程序 Files 文件路径

ContextWrapper.getFilesDir()

5.2. XML

Ø Pull 简介

Ÿ 常见的 XML 解析方式有三种, DOM 、 SAX 、 Pull , Android 系统中推荐使用Pull

Ÿ Pull 解析器是一个开源的 Java 项目, Android 系统内部解析 XML 文件均为此种方式,也可用于 JavaEE 项目

Ÿ Android SDK 中已经集成了 Pull 解析器,无需添加任何 jar 文件

Ÿ Pull 解析器运行方式与 SAX 类似,提供各种事件的判断

Ÿ 官方网站: http://xmlpull.org/

Ø 使用 Pull 解析器解析 XML 文件

Ÿ Xml.newPullParser() 获得解析器

Ÿ parser.setInput(in,  "UTF-8" ) 设置输入流以及编码

Ÿ parser.next() 获取下一个解析事件,得到一个事件代码

Ÿ XmlPullParser中定义了常量来标识各种解析事件

START_DOCUMENT 、 END_DOCUMENT  、 START_TAG  、END_TAG  、 TEXT 

Ø 使用XmlSerializer写出 XML

Ÿ 使用以下方法生成 XML ,和 XML 文档顺序类似

startDocument

startTag

attribute

text

endTag

endDocument

5.3. 偏好设定( SharedPreferences ) 

Ÿ 在程序中保存一些配置参数的时候我们经常使用 SharedPreferences

Context.getSharedPreferences(String name, int  mode)

该方法可以在 /data/data/<package>/shared_pref/ 目录下创建一个以 name 命名的 xml文件, mode 文件为模式

Ÿ 存储偏好

调用edit()方法可以获取一个 Editor 对象,对数据进行存储,存储之后需要调用 commit()保存到文件

Ÿ 读取偏好

获得SharedPreferences之后调用 getString() 、 getInt() 等方法获取其中设置的值

Ÿ 在 Activity 中获取 SharedPreferences

在 Activity 中可以调用 getPreferences( int  mode)方法获得一个SharedPreferences,文件名和 Activity 名一致

6. 数据库( SQLite )

6.1. SQLite 特点

Ÿ Android 平台中嵌入了一个关系型数据库 SQLite ,和其他数据库不同的是 SQLite存储数据时不区分类型

例如一个字段声明为 Integer 类型,我们也可以将一个字符串存入,一个字段声明为布尔型,我们也可以存入浮点数。

除非是主键被定义为 Integer ,这时只能存储 64 位整数

Ÿ 创建数据库的表时可以不指定数据类型,例如:

CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(20))

CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT, name)

Ÿ SQLite 支持大部分标准 SQL 语句,增删改查语句都是通用的,分页查询语句和 MySQL 相同

SELECT * FROM person LIMIT 20 OFFSET 10

SELECT * FROM person LIMIT 10,20

6.2. 创建数据库

Ÿ 定义类继承SQLiteOpenHelper

Ÿ 声明构造函数, 4 个参数

Ÿ 重写 onCreate ()方法

Ÿ 重写 upGrade() 方法

Ÿ 注意: SQLite 数据库中列一旦创建不能修改,如果一定要修改,需要重新创建表,拷贝数据

6.3. CRUD操作

Ÿ 和 JDBC 访问数据库不同,操作 SQLite 数据库无需加载驱动,不用获取连接,直接可以使用

获取 SQLiteDatabase 对象之后通过该对象直接可以执行 SQL 语句

SQLiteDatabase.execSQL()

SQLiteDatabase.rawQuery()

Ÿ getReadableDatabase()和getWritableDatabase()的区别

查看源代码后我们发现getReadableDatabase()在通常情况下返回的就是getWritableDatabase() 拿到的数据库

只有在抛出异常的时候才会以只读方式打开

Ÿ 数据库对象缓存

getWritableDatabase() 方法最后会使用一个成员变量记住这个数据库对象,下次打开时判断是否重用

Ÿ SQLiteDatabase 封装了 insert() 、 delete ()、 update ()、 query ()四个方法也可以对数据库进行操作

这些方法封装了部分 SQL 语句,通过参数进行拼接

6.4. 事务管理

Ÿ 在使用 SQLite 数据库时可以用 SQLiteDatabase类中定义的相关方法控制事务

beginTransaction()  开启事务

setTransactionSuccessful()  设置事务成功标记

endTransaction()  结束事务

Ÿ endTransaction() 需要放在 finally 中执行,否则事务只有到超时的时候才自动结束,会降低数据库并发效率

7. 内容提供者( ContentProvider )

7.1. 什么是内容提供者

Ÿ 内容提供者是 Android 中的四大组件之一,可以将应用中的数据对外进行共享

Ÿ 内容提供者将数据的访问方式统一,不必针对不同数据类型采取不同的访问策略

Ÿ 内容提供者将数据封装,只暴露出我们希望提供给其他程序的数据

Ÿ 内容提供者中数据更改可被监听

7.2. 创建内容提供者

Ÿ 定义类继承 ContentProvider ,根据需要重写内部方法

Ÿ 在清单文件的 <application> 节点下进行配置, <provider> 标签中需要指定 name 和authorities 属性

name 为类名,包名从程序 Package 开始,以“ . ”开始

authorities :是访问 Provider 时的路径,要唯一

Ÿ URI 代表要操作的数据,由 scheme 、 authorites 、 path 三部分组成

content:// cn.itcast. sqlite . provider / person

scheme :固定为 content ,代表访问内容提供者

authorites : <provider> 节点中的 authorites 属性

path :程序定义的路径,可根据业务逻辑定义

7.3. 完成 CRUD 方法

Ÿ 当程序调用 CRUD 方法时会传入 Uri

Ÿ 我们通过 Uri 判断调用者要操作的数据

可以使用工具类 UriMatcher 来判断 Uri

addURI 方法可以添加 Uri

match 方法可以匹配一个 Uri 判断其类型

Ÿ 根据业务逻辑操作数据

7.4. 访问内容提供者

Ÿ 通过 Context 获得 ContentResolver 对象

Ÿ 调用 ContentResolver 对象的方法即可访问内容提供者

7.5. 完成 getType 方法

Ÿ 如果返回数据是单条数据:vnd.android.cursor.item 

Ÿ 如果返回数据是多条数据:vnd.android.cursor.dir

7.6. 监听内容提供者数据变化

Ÿ 在内容提供者中可以通知其他程序数据发生变化

通过 Context 的 getContentResolver() 方法获取 ContentResolver

调用其notifyChange() 方法发送数据修改通知

Ÿ 在其他程序中可以通过ContentObserver监听数据变化

通过 Context 的 getContentResolver() 方法获取 ContentResolver

调用其registerContentObserver() 方法指定对某个 Uri 注册 ContentObserver

自定义ContentObserver,重写 onChange() 方法获取数据

7.7. GIT 获取源代码

Ø 资源地址

Ÿ Git

http://code.google.com/p/msysgit/

Ÿ 源码

https://github.com/android

注意:

GIT1.7.7 安装后不能卸载,可以用其他版本覆盖后再卸载。

使用 GIT 时不要使用中文目录,否则 GIT GUI 会报错无法启动。删除 C 盘中 .gitconfig文件可以解决。

7.8. 监听短信

Ÿ Android 系统提供了 Provider 对短信进行查询,当发出短信时也会发送更改通知

Ÿ 短信数据库在  com.android.providers.telephony

Ÿ 定义一个 Observer 监听 "content://sms"

Ÿ 在 onChange() 方法中查询 "content://sms"

Ÿ 需要权限android.permission.READ_SMS

7.9. 操作联系人

Ø 获取所有联系人

Ÿ Android 系统中的联系人也是通过 ContentProvider 来对外提供数据的

Ÿ 数据库路径为: /data/data/com.android.providers.contacts/database/contacts2.db

Ÿ 我们需要关注的有 3 张表

raw_contacts :其中保存了联系人 id

data :和 raw_contacts 是多对一的关系,保存了联系人的各项数据

mimetypes :为数据类型

Ÿ Provider 的 authorites 为 com.android.contacts

Ÿ 查询 raw_contacts 表的路径为: contacts

Ÿ 查询 data 表的路径为: contacts/#/data

这个路径为连接查询,直接查询“ mimetype ”字段就可以根据“ mimetype_id ”查询到 mimetypes 表中的数据

Ÿ 先查询 raw_contacts 得到每个联系人的 id ,在使用 id 从 data 表中查询对应数据,根据 mimetype 分类数据

Ø 通过电话号码获取联系人

Ÿ 系统内部提供了根据电话号码获取 data 表数据的功能,路径为: data/phones/filter/*

Ÿ 用电话号码替换“ * ”部分就可以查到所需数据,获取“ display_name ”可以获取到联系人显示名

Ø 添加联系人

Ÿ 先向 raw_contacts 表插入 id ,路径为: raw_contacts

Ÿ 得到 id 之后再向 data 表插入数据,路径为: data

Ø 使用事务添加联系人

Ÿ 在添加联系人得时候是分多次访问 Provider ,如果在过程中出现异常,会出现数据不完整的情况,这些操作应该放在一次事务中

Ÿ 使用ContentResolver的applyBatch(String authority,ArrayList<ContentProviderOperation> operations) 方法可以将多个操作在一个事务中执行

Ÿ 文档位置 :

file:///F:/Itcast/lib/android-sdk-windows/docs/reference/android/provider/ContactsContract.RawContacts.html

8. 网络通信

8.1. 获取文本数据

Ÿ 通过 URL 对象封装地址,打开一个 HttpURLConnection

Ÿ 设置头信息之后获取响应码,如果成功返回 200 即可从 HttpURLConnection 中获取输入流读取数据

Ÿ 代码过长屏幕显示不全可以使用 <ScrollView> 进行显示

Ÿ 需要访问网络的权限

< uses-permission  android:name = "android.permission.INTERNET"  />

8.2. 获取网络图片

Ÿ 通过 BitmapFactory 的 decodeByteArray(byte[] data, int offset, int length)方法将数据转换为图片对象

8.3. 获取 XML

Ÿ 使用 URL 封装路径,打开一个 HttpURLConnection

Ÿ 设置头信息之后获取相应码,从输入流中获取数据

Ÿ 使用 XmlPullPaser 解析

8.4. 获取 JSON

Ÿ 使用 URL 封装路径,打开一个 HttpURLConnection

Ÿ 设置头信息之后获取相应码,从输入流中获取数据

Ÿ 将数据转为 String ,封装成 JSONArray 对象

Ÿ 遍历 JSONArray 对象,调用获取其中的 JSONObject

Ÿ 再从 JSONObject 中获取每个字段的信息

8.5. 发送 GET 请求

Ÿ 拼接路径和参数,通过 URL 进行封装,打开一个 HttpURLConnection ,发送请求

Ÿ 如果参数是中文会出现乱码

Ÿ URL 中包含的中文参数需要使用 URLEncoder 进行编码

Ÿ 服务器端如果是 TOMCAT ,其默认使用 ISO8859-1 编码,接收时需要处理编码问题

8.6. 发送 POST 请求

Ÿ 通过 URL 打开一个 HttpURLConnection

Ÿ 头信息中除了超时时间和请求方式之外还必须设置Content-Type和Content-Length

Ÿ 从 HttpURLConnection 获得输出流输出参数数据

Ÿ 服务端可以使用 request 对象的 setCharacterEncoding方法设置编码

8.7. 发送 XML ,访问 WebService

Ø 发送 XML

Ÿ 通过 URL 封装路径打开一个 HttpURLConnection

Ÿ 设置请求方式,Content-Type和Content-Length

XML 文件的 Content-Type为:text/xml; charset=UTF-8

Ÿ 使用 HttpURLConnection 获取输出流输出数据

Ø WebService

Ÿ WebService 是发布在网络上的 API ,可以通过发送 XML 调用, WebService 返回结果也是 XML 数据

Ÿ WebService 没有语言限制,只要可以发送 XML 数据和接收 XML 数据即可

Ÿ  http://www.webxml.com.cn  网站上提供了一些 WebService 服务,我们可以对其进行调用

Ÿ  http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?op=getMobileCodeInfo  中提供了电话归属地查询的使用说明 

8.8. HTTP 协议上传文件

Ÿ 搭建服务器,完成上传功能

Ÿ 使用浏览器上传,查看请求信息

Ø HttpURLConnection

Ÿ 通过 URL 封装路径打开一个 HttpURLConnection

Ÿ 设置请求方式以及头字段:Content-Type、Content-Length、Host

Ÿ 拼接数据发送

Ø Socket

Ÿ 使用 HttpURLConnection 发送时内部有缓存机制,如果上传较大文件会导致内存溢出

Ÿ 我们可以使用 Socket 发送 TCP 请求,将上传数据分段发送

Ø HttpClient

public   void  upload(String name, String password, String path)  throws  Exception {

//  创建 HttpClient 对象

HttpClient client =  new  HttpClient();

//  设置超时事件

client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);

//  创建一个 Post 请求 ,  指定路径

PostMethod postMethod =  new  PostMethod( "http://192.168.1.102:8080/14.Web/LoginServlet" );

//  封装每个表单项

Part[] parts = {  new  StringPart( "name" , name),  new  StringPart( "password" , password),  new  FilePart( "file" ,  new  File(path)) };

//  给 Post 请求设置实体

postMethod.setRequestEntity( new  MultipartRequestEntity(parts, postMethod.getParams()));

//  执行 Post 请求

client.executeMethod(postMethod);

// Post 请求是释放资源

postMethod.releaseConnection();

}

8.9. 多线程断点续传下载器

Ÿ 在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度

Ÿ 手机端下载数据时难免会出现无信号断线、电量不足等情况,所以需要断点续传功能

Ÿ 根据下载数据长度计算每个线程下载的数据位置,程序中开启多个线程并发下载

在请求头中设置 Range 字段就可以获取指定位置的数据,例如: Range: bytes=100-200

Ÿ 在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,下次启动时从记录位置继续下载

Ø 多线程下载

Ÿ 进度条使用 <Progress> 进行配置

默认为圆形进度条,水平进度条需要配置 style 属性, ?android:attr/progressBarStyleHorizontal

使用 android.R.attr. progressBarStyleHorizontal作为样式

Ÿ 当点击下载按钮时开启多线程下载,下载过程中修改进度条进度

设置最大刻度:setMax()

设置当前进度:setProgress()

Ø 断点续传

Ÿ 断点续传需要在下载过程中记录每条线程的下载进度

Ÿ 每次下载开始之前先读取数据库,查询是否有未完成的记录,有就继续下载,没有则创建新记录插入数据库

Ÿ 在每次向文件中写入数据之后,在数据库中更新下载进度

Ÿ 下载完成之后删除数据库中下载记录

Ø Handler 传输数据

Ÿ 主线程中创建的 View 只能在主线程中修改,其他线程只能通过和主线程通信,在主线程中改变 View 数据

Ÿ 我们使用 Handler 可以处理这种需求

主线程中创建 Handler ,重写 handleMessage() 方法

新线程中使用 Handler 发送消息,主线程即可收到消息,并且执行 handleMessage()方法

Ø 动态生成新 View

Ÿ 创建 XML 文件,将要生成的 View 配置好

Ÿ 获取系统服务 LayoutInflater ,用来生成新的 View

LayoutInflater inflater = (LayoutInflater) getSystemService( LAYOUT_INFLATER_SERVICE );

Ÿ 使用inflate( int  resource, ViewGroup root)方法生成新的 View

Ÿ 调用当前页面中某个容器的 addView ,将新创建的 View 添加进来

9. 活动( Activity )

9.1. 创建 Activity

Ø 定义 Activity

Ÿ 定义类继承 Activity

Ÿ 在 AndroidManifest.xml 的 <application> 节点中声明 <activity>

Ø 显式意图创建方式

Ÿ 构造函数,代码少

new  Intent( this , NewActivity. class );

Ÿ 类名形式,灵活,可扩展性强

intent.setClassName( this ,  "cn.itcast.activity.NewActivity" );

Ÿ 包名类名形式,可启动其他程序中的 Activity

intent.setClassName( "cn.itcast.downloader" ,  "cn.itcast.downloader.MainActivity");

Ø 创建 Activity 并传递数据

Ÿ 在意图对象中封装了一个 Bundle 对象,可以用来携带数据

Ÿ 在新 Activity 中可以获得意图对象以获取其中 Bundle 保存的数据

Ø 创建 Activity 获取返回数据

Ÿ 使用startActivityForResult(Intent intent,  int  requestCode) 方法打开 Activity

Ÿ 重写onActivityResult( int  requestCode,  int  resultCode, Intent data) 方法

Ÿ 新 Activity 中调用 setResult( int  resultCode, Intent data) 设置返回数据之后,关闭 Activity 就会调用 onActivityResult方法

Ø 隐式意图创建 Activity

Ÿ 显式意图是指在创建意图时指定了组件,而隐式意图则不指定组件,通过动作、类型、数据匹配对应的组件

Ÿ 在清单文件中定义 <activity> 时需要定义 <intent-filter> 才能被隐式意图启动

Ÿ <intent-filter> 中至少配置一个 <action> 和一个 <category> ,否则无法被启动

Ÿ Intent 对象中设置的 action 、 category 、 data 在 <intent-filter> 必须全部包含才能启动

Ÿ <intent-filter> 中的 <action> 、 <category> 、 <data> 都可以配置多个, Intent 对象中不用全部匹配,每样匹配一个即可启动

Ÿ 如果一个意图可以匹配多个 Activity , Android 系统会提示选择

9.2. 生命周期

Ÿ Acitivity 三种状态

运行: activity 在最前端运行

暂停: activity 可见,但前端还有其他 acti vity ,被覆盖一部分,或者前端 activity 透明

停止: activity 不可见,完全被覆盖

Ÿ 生命周期相关方法

onCreate :创建时调用,或者程序在暂停、停止状态下被杀死之后重新打开时也会调用

onStart : onCreate 之后或者从停止状态恢复时调用

onResume : onStart 之后或者从暂停状态恢复时调用,从停止状态恢复时由于调用onStart ,也会调用 onResume

onPause:进入暂停、停止状态,或者销毁时会调用

onStop:进入停止状态,或者销毁时会调用

onDestroy:销毁时调用

onRestart :从停止状态恢复时调用

Ÿ 保存信息相关方法

onSaveInstanceState:在 Activity 被动的摧毁或停止的时候调用,用于保存运行数据,可以将数据存在在 Bundle 中

onRestoreInstanceState:该方法在 Activity 被重新绘制的时候调用,例如改变屏幕方向, savedInstanceState为onSaveInstanceState保存的数据

9.3. 启动模式

Ÿ 在 AndroidManifest.xml 中的 <activity> 标签中可以配置 android:launchMode 属性,用来控制 Actvity 的启动模式

Ÿ 在 Android 系统中我们创建的 Acitivity 是以栈的形式呈现的

standard :每次调用 startActivity() 启动时都会创建一个新的 Activity 放在栈顶

singleTop :如果启动的 Activity 时,指定 Activity 不在栈顶就创建,如在栈顶,则不再创建

singleTask :如果启动的 Activity 不存在就创建,如果存在直接跳转到指定的 Activity 所在位置

singleInstance :如果启动的 Activity 不存在就创建,如果存在就将指定的 Activity移动到栈顶

9.4. 内存管理

Ÿ Android 系统在运行多个进程时,如果系统资源不足,会强制结束一些进程。优先选择哪个进程来结束是有优先级的。以下顺序靠上的优先结束

空:进程中所有 Activity 都已销毁

后台:进程中有一个停止状态的 Activity

可见:进程中有一个暂停状态的 Activity

前台:进程中正在运行一个 Activity

10. 广播接收者 (BroadcastReceiver)

10.1. 定义广播接收者

Ÿ 定义类继承 BroadcastReceiver ,重写 onReceive 方法

Ÿ 清单文件中声明<receiver>,需要在其中配置<intent-filter>指定接收广播的动作

Ÿ 当接收到匹配广播之后就会执行 onReceive 方法

Ÿ BroadcastReceiver 除了在清单文件中声明,也可以在代码中声明,使用 registerReceiver方法注册 Receiver

10.2. 发送广播

Ø 无序广播

Ÿ 使用sendBroadcast方法发送

Ÿ 被所有广播接收者接收,无序,不可中断

Ÿ 广播时可设置接收者权限,仅当接收者含有权限才能接收

Ÿ 接收者的<receiver>也可设置发送方权限,只接收含有权限应用的广播

Ø 有序广播

Ÿ 使用sendOrderedBroadcast方法发送

Ÿ 接收者可以在<intent-filter>中定义android:priority定义优先级,数字越大优先级越高

Ÿ 被各个广播接收者逐个接收,中途可以中断或者添加数据

abortBroadcast()  

getResultExtras( true ).putString( "data" ,  " 新增数据 " );

10.3. 监听短信接收

Ÿ Android 系统在收到短信的时候会发送一条有序广播,我们如果定义一个接收者接收这个广播,就可以得到短信内容,也可以拦截短信

Ÿ 定义广播接收者接收广播 android.provider.Telephony.SMS_RECEIVED

Ÿ 在 onReceive 方法内部调用 Intent 的 getExtras() 再调用 get(String) 获取其中 pdus字段,得到一个 Object[],其中每一个元素都是一个 byte[]

Ÿ 通过SmsMessage类的createFromPdu方法创建 SmsMessage 对象

Ÿ 从 SmsMessage 对象中即可获取发送者号码、短信内容、发送时间等信息

Ÿ 需要接收短信权限: < uses-permission  android:name ="android.permission.RECEIVE_SMS" />

Ÿ Android 系统中收到短信的通知是一个有序通知,我们如需拦截垃圾短信,可以配置较高的 priority,收到信息进行判断是否abortBroadcast()

10.4. 监听呼出电话

Ÿ 定义广播接收者接收  android.intent.action.NEW_OUTGOING_CALL

Ÿ 需要权限  < uses-permission  android:name = "android.permission.PROCESS_OUTGOING_CALLS" />

Ÿ 在 onReceive 方法中使用 getResultData() 和  setResultData()  方法获取和设置电话号码

10.5. 生命周期

Ÿ 广播接收者的生命周期是非常短暂的,在接收到广播的时候创建, onReceive() 方法结束之后销毁

Ÿ 广播接收者中不要做一些耗时的工作,否则会弹出 Application No Response 错误对话框

Ÿ 最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉

Ÿ 耗时的较长的工作最好放在服务中完成

11. 服务 (Service)

11.1. 基本概念

Ÿ Service 是一种在后台运行,没有界面的组件,由其他组件调用开始。

Ÿ 创建 Service ,定义类继承 Service , AndroidManifest.xml 中定义 <service>

Ÿ 开启 Service ,在其他组件中调用 startService方法

Ÿ 停止 Service ,调用 stopService方法

11.2. 电话录音

需要权限: android.permission.READ_PHONE_STATE

TelephonyManager manager = (TelephonyManager) getSystemService( TELEPHONY_SERVICE );

manager.listen( new  MyListener(), PhoneStateListener. LISTEN_CALL_STATE );

private   final   class  MyListener  extends  PhoneStateListener {

private  String  num ;

private  MediaRecorder  recorder ;

public   void  onCallStateChanged( int  state, String incomingNumber) {

switch  (state) {

case  TelephonyManager. CALL_STATE_RINGING :

num  = incomingNumber;

break ;

case  TelephonyManager. CALL_STATE_OFFHOOK :

try  {

File  file  =  new  File(Environment.getExternalStorageDirectory(),  num  +  "_"  + System.currentTimeMillis() +  ".3gp" );

recorder  =  new  MediaRecorder();

recorder .setAudioSource(AudioSource. MIC );

recorder .setOutputFormat(OutputFormat. THREE_GPP );

recorder .setAudioEncoder(AudioEncoder. AMR_NB );

recorder .setOutputFile( file .getAbsolutePath());

recorder .prepare();

recorder .start();

}  catch  (Exception e) {

e.printStackTrace();

}

break ;

case  TelephonyManager. CALL_STATE_IDLE :

if  ( recorder  !=  null ) {

recorder .stop();

recorder .release();

}

break ;

}

}

}

11.3. 绑定本地服务

Ÿ 使用bindService绑定服务,传入一个自定义的ServiceConnection用来接收 IBinder

Ÿ 定义一个业务接口,其中定义需要的使用的方法

Ÿ 服务中自定义一个 IBinder 继承 Binder 并实现业务接口,在 onBind方法中返回

Ÿ 调用端将 IBinder 转为接口类型,调用接口中的方法即可调用到服务中的方法

11.4. 绑定远程服务

Ÿ 远程绑定服务时无法通过同一个接口来调用方法,这时就需要使用 AIDL 技术

Ÿ 将接口扩展名改为“.aidl”

Ÿ 去掉权限修饰符

Ÿ gen 文件夹下会生成同名接口

Ÿ 将服务中自定义的 IBinder 类改为继承接口中的 S tub

Ÿ ServiceConnection中返回的 IBinder 是代理对象,不能使用强转,改用 S tub.asInterface()

11.5. AIDL 使用自定义类型

Ÿ AIDL 默认只能使用 Java 中基本数据类型和 String 、 List 、 Map , List 和 Map 中的元素类型也只能是这些类型。

Ÿ 如果需要使用其他类型数据,使用的类必须实现 Parcelable 接口以完成序列化和反序列化工作

重写 public void writeToParcel(Parcel dest, int flags)

定义 public static final Parcelable.Creator<Person>  CREATOR

Ÿ 定义该类对应的 AIDL

package  包名

parcelable  类名

Ÿ 在接口 AIDL 中导入该类,注意:即使是同一个包也需要导入

12. 多媒体

12.1. 音频播放器

12.2. 视频播放器

screenSV .getHolder().setType(SurfaceHolder. SURFACE_TYPE_PUSH_BUFFERS );  //  设置缓冲区数据

screenSV .getHolder().setKeepScreenOn( true );  //  设置屏幕保持

screenSV .getHolder().addCallback( new  MyCallback());  //  设置回调函数

player .reset();

player .setDisplay( screenSV .getHolder()); //  设置显式

player .setDataSource( "/mnt/sdcard/1.mp4" ); //  设置数据源

player .prepare(); //  准备

player .seekTo(position); //  跳转到指定位置

player .start();

12.3. 拍照

Ÿ 需要权限

< uses-permission  android:name = "android.permission.CAMERA"  />

Ÿ 打开摄像头

Camera.open()

SDK2.3 之后支持前置摄像头, open 方法可以接收一个 int 参数,用来指定哪个摄像头

Ÿ 设置预览显示位置

setPreviewDisplay(SurfaceHolder holder)

注意 SurfaceView 不在前端显示的时候会被销毁,恢复之后会重绘

Ÿ 开始预览 

startPreview()

将摄像头拍摄画面显示在 SurfaceView 中,在此之前可对摄像头进行参数配置

getParameters() 方法可以获取摄像头的相关参数Parameters,调用其内部方法即可进行配置

Ÿ 自动对焦

autoFocus(AutoFocusCallback cb)

自动对焦是一个异步操作,如果我们向等待自动对焦结束之后才开始拍照,需要传入一个回调对象,在其回调函数中调用拍照方法

Ÿ 拍照 

takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg)

拍照也是异步操作,需要通过回调函数来得到拍照之后的数据

注意拍照之后摄像头不回自动回到预览状态,需要重写调用startPreview()方法

12.4. 录像

Ÿ 需要权限

< uses-permission  android:name = "android.permission.RECORD_AUDIO" />

< uses-permission  android:name = "android.permission.CAMERA" />

Ÿ 创建MediaRecorder

new  MediaRecorder()

Ÿ 设置音频输入源

setAudioSource( int  audio_source)

Ÿ 设置视频输入源

setVideoSource( int  video_source)

Ÿ 设置输出格式

setOutputFormat( int  output_format)

Ÿ 设置音频编码器

setAudioEncoder( int  audio_encoder)

Ÿ 设置视频编码器

setVideoEncoder( int  video_encoder)

Ÿ 设置预览显示位置

setPreviewDisplay(Surface sv)

Ÿ 设置输出文件

setOutputFile(String path)

Ÿ 准备录制

prepare()

Ÿ 开始录制

start()

开始录制之前需要结束摄像头的预览

Ÿ 结束录制释放资源

stop()

release()

13. 通知

13.1. 吐司通知

Ÿ 创建通知

Toast.makeText(Context context, CharSequence text,  int  duration)

Toast.makeText(Context context,  int  resId,  int  duration)

Ÿ 发送通知

show()

13.2. 状态栏通知

Ÿ 获取系统通知服务 

NotificationManager nm = (NotificationManager) getSystemService( NOTIFICATION_SERVICE )

Ÿ 创建通知

通过构造函数创建 :  Notification( int  icon, CharSequence tickerText,  long  when)

icon:  通知的图片资源 ID

tickerText:  状态栏中显示的消息内容

when:  时间

Ÿ 创建PendingIntent以供点击时发送

PendingIntent.getActivity(Context context,  int  requestCode, Intent intent,  int  flags)

context:  当前上下文

requestCode:  请求码

intent:  点击时要发送的意图

flags:  类型 ,  PendingIntent中提供了常量选择

Ÿ 设置通知点击事件

调用Notification 对象方法 :  setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent)

context:  当前上下文

contentTitle:  标题

contentText: 内容

contentIntent:  点击时触发的意图

Ÿ 设置通知点击后清除

设置Notification 对象属性 n. flags  = Notification. FLAG_AUTO_CANCEL ;

Ÿ 发送消息

调用Notification对象方法 :  notify( int  id, Notification notification)

13.3. 对话框通知

Ø 普通对话框

new  AlertDialog.Builder( this )  //

.setTitle( " 普通对话框 " )  //

.setMessage( " 普通内容 " )  //

.setCancelable( false )  //

.setPositiveButton( "YES" , listener)  //   listener 为 OnClickListener  监听器对象 ,  监听按钮被选中

.setNeutralButton( "CANCEL" , listener)  //

.setNegativeButton( "NO" , listener)  //

.show();

Ø 列表对话框

new  AlertDialog.Builder( this )  //

.setTitle( " 列表对话框 " )  //

.setCancelable( false )  //

.setItems( items , listener)  //   listener 为 OnClickListener  监听器对象 ,  监听列表项被选中

.show();

Ø 单选对话框

new  AlertDialog.Builder( this )  //

.setTitle( " 单选对话框 " )  //

.setCancelable( false )  //

.setSingleChoiceItems( items , 0, choiceLinstener)  //   0,  为默认选中索引 ,  choiceLinstener 为  OnClickListener  监听器对象 ,  监听单选按钮被选中

.setPositiveButton( " 确定 " , positiveLinstener)  //   positiveLinstener 为  OnClickListener  监听器对象 ,  监听确定按钮点击

.show();

Ø 多选对话框

new  AlertDialog.Builder( this )  //

.setTitle( " 多选对话框 " )  //

.setCancelable( false )  //

.setMultiChoiceItems( items , checkedArr, choiceListener)  //   checkedArr 为默认选中 ,  choiceListener 为  OnMultiChoiceClickListener  监听器对象 ,  监听多选按钮被选中

.setPositiveButton( " 确定 " , positiveLinstener)  //   positiveLinstener 为  OnClickListener  监听器对象 ,  监听确定按钮点击

.show();

Ø 进度对话框

ProgressDialog dialog =  new  ProgressDialog( this );

dialog.setProgressStyle(ProgressDialog. STYLE_HORIZONTAL ); // 设置进度条样式

dialog.setTitle( " 下载中 " );

dialog.setMessage( " 请稍候 ..." );

dialog.setCancelable( false );

dialog.setMax(100);

dialog.show();

dialog.setProgress(10); //  设置进度

dialog.dismiss(); //  对话框结束

关于通知的文档位置: android-sdk-windows/docs/guide/topics/ui/notifiers/index.html

14. 常用 UI

14.1. 列表视图 (ListView)

Ø XML 配置

Ÿ 在主界面中配置 <ListView> 标签

Ÿ 在 res/layout/ 文件夹下创建一个新的 xml 文件指定每个条目的布局

Ø Java 代码构建 ListView

Ÿ 获取 ListView 对象

Ÿ 设置一个 Adapter

BaseAdapter :实现内部抽象方法

SimpleAdapter:以 List<Map<String, ?>> 形式封装数据

SimpleCursorAdapter:以 Cursor 对象封装数据, Cursor 中需要有“ _id ”一列

Ÿ 添加 OnItemClickListener

调用 ListView 的 getItemAtPosition(int) 方法可以获取封装数据的容器

如果传入的是 BaseAdapter ,获取到的就是我们自定义方法中返回的内容

如果传入的是SimpleAdapter,获取到的就是一个 Map<String, ?>

如果传入的是SimpleCursorAdapter,获得到的就是一个 Cursor ,并且 Cursor 以指向选中的一条记录

14.2. 单选 (RadioGroup)

Ÿ 定义 <RadioGroup>

Ÿ 在 <RadioGroup> 中定义 <RadioButton> 和 <Button>

Ÿ 处理 Button 的点击事件

Ÿ 根据 ID 获取 RadioGroup 对象,调用其 getCheckedRadioButtonId()方法可以获取其中被选中的RadioGroup 的 ID

Ø 代码

< RadioGroup

     android:id = "@+id/lessonsRG"

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"

     android:orientation = "horizontal"   >

     < RadioButton

         android:id = "@+id/javaRB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = "Java"   />

     < RadioButton

         android:id = "@+id/netRB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = ".Net"   />

     < RadioButton

         android:id = "@+id/phpRB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = "PHP"   />

     < Button

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:onClick = " onR radioClick"

         android:text = " 确定 "   />

</ RadioGroup >

public   void  onRradioClick(View view) {

RadioGroup lessonRG = (RadioGroup) findViewById(R.id. lesson s RG );

int  id = lessonRG.getCheckedRadioButtonId();  //  获取选中的 id

String msg =  null ;

switch  (id) {

case  R.id. javaRB :

msg =  "Java" ;

break ;

case  R.id. netRB :

msg =  ".Net" ;

break ;

case  R.id. phpRB :

msg =  "PHP" ;

break ;

}

Toast.makeText( this , msg, 0).show();

}

14.3. 多选 (CheckBox)

Ÿ 定义若干 <CheckBox> 和一个 <Button>

Ÿ 处理 Button 的点击事件

Ÿ 根据 ID 获取每个 CheckBox ,调用其 isChecked()方法判断是否被选中

Ø 代码

< LinearLayout

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"   >

     < CheckBox

         android:id = "@+id/javaCB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = "Java"   />

     < CheckBox

         android:id = "@+id/netCB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = ".Net"   />

     < CheckBox

         android:id = "@+id/phpCB"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:text = "PHP"   />

     < Button

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:layout_weight = "1"

         android:onClick = "checkboxOnClick"

         android:text = " 确定 "   />

</ LinearLayout >

public   void  checkboxOnClick(View view) {

CheckBox javaCB = (CheckBox) findViewById(R.id. javaCB );

CheckBox netCB = (CheckBox) findViewById(R.id. netCB );

CheckBox phpCB = (CheckBox) findViewById(R.id. phpCB );

StringBuilder sb =  new  StringBuilder();

sb.append(javaCB.isChecked() ? javaCB.getText() +  " "  :  "" );

sb.append(netCB.isChecked() ? netCB.getText() +  " "  :  "" );

sb.append(phpCB.isChecked() ? phpCB.getText() +  " "  :  "" );

Toast.makeText( this , sb, 0).show();

}

14.4. 下拉列表 ( Spinner )

Ÿ 定义 <Spinner> 标签

Ÿ 创建一个适配器

Ÿ 获取 Spinner 标签,调用 setAdapter(SpinnerAdapter adapter)方法设置一个适配器

Ÿ 调用setOnItemSelectedListener(OnItemSelectedListener listener)方法设置监听器监听选中事件

Ø XML 配置

< Spinner

     android:id = "@+id/spinner"

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"   />

Ø 使用字符串构建适配器

private   void  setSpinnerByString() {

final  Spinner spinner = (Spinner) findViewById(R.id. spinner );

ArrayAdapter<String> adapter =  new  ArrayAdapter<String>( this , android.R.layout. simple_spinner_item );  //   设置样式

adapter.setDropDownViewResource(android.R.layout. simple_spinner_dropdown_item ); //  设置下拉后样式

adapter.add( "Java" );

adapter.add( ".Net" );

adapter.add( "PHP" );

spinner.setAdapter(adapter);

spinner.setOnItemSelectedListener( new  OnItemSelectedListener() {

public   void  onItemSelected(AdapterView<?> parent, View view,  int  position,  long  id) {

String selection = (String) spinner.getItemAtPosition(position);

Toast.makeText(getApplicationContext(), selection, 0).show();

}

public   void  onNothingSelected(AdapterView<?> parent) {

}

});

}

Ø 使用 JavaBean 构建适配器

private   void  setSpinnerByJavaBean() {

final  Spinner spinner = (Spinner) findViewById(R.id. spinner );

ArrayAdapter<User> adapter =  new  ArrayAdapter<User>( this , android.R.layout.simple_spinner_item );

adapter.setDropDownViewResource(android.R.layout. simple_spinner_dropdown_item );

adapter.add( new  User(1,  "lhm" ,  "lhm@itcast.cn" ));

adapter.add( new  User(2,  "yzk" ,  "yzk@itcast.cn" ));

adapter.add( new  User(3,  "hsp" ,  "hsp@itcast.cn" ));

spinner .setAdapter(adapter);

spinner .setOnItemSelectedListener( new  OnItemSelectedListener() {

public   void  onItemSelected(AdapterView<?> parent, View view,  int  position,  long  id) {

User selection = (User)  spinner .getItemAtPosition(position);

Toast.makeText(getApplicationContext(), selection.getName(), 0).show();

}

public   void  onNothingSelected(AdapterView<?> parent) {

}

});

}

Ø 使用资源文件构建适配器

< string-array   name = "items" >

     < item > Java </ item >

     < item > .Net </ item >

     < item > PHP </ item >

</ string-array >

private   void  setSpinnerByResource() {

final  Spinner spinner = (Spinner) findViewById(R.id. spinner );

ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( this, R.array. items , android.R.layout. simple_spinner_item );

adapter.setDropDownViewResource(android.R.layout. simple_spinner_dropdown_item );

spinner .setAdapter(adapter);

spinner .setOnItemSelectedListener( new  OnItemSelectedListener() {

public   void  onItemSelected(AdapterView<?> parent, View view,  int  position,  long  id) {

CharSequence selection = (CharSequence)  spinner .getItemAtPosition(position);

Toast.makeText(getApplicationContext(), selection, 0).show();

}

public   void  onNothingSelected(AdapterView<?> parent) {

}

});

}

Ø 自定义适配器样式

<? xml   version = "1.0"   encoding = "utf-8" ?>

< LinearLayout   xmlns:android = "http://schemas.android.com/apk/res/android"

     android:layout_width = "match_parent"

     android:layout_height = "match_parent"

     android:orientation = "horizontal"   >

     < ImageView

         android:layout_width = "50dp"

         android:layout_height = "50dp"

         android:src = "@android:drawable/ic_delete"   />

     < TextView

         android:id = "@+id/content"

         android:layout_width = "fill_parent"

         android:layout_height = "wrap_content"

         android:textSize = "50sp"   />

</ LinearLayout >

private   void  setSpinnerByCustom() {

final  Spinner spinner = (Spinner) findViewById(R.id. spinner );

ArrayAdapter<CharSequence> adapter =  new  ArrayAdapter<CharSequence>( this , R.layout. item , R.id. content );

adapter.add( "Java" );

adapter.add( ".Net" );

adapter.add( "PHP" );

spinner .setAdapter(adapter);

spinner .setOnItemSelectedListener( new  OnItemSelectedListener() {

public   void  onItemSelected(AdapterView<?> parent, View view,  int  position,  long  id) {

String selection = (String)  spinner .getItemAtPosition(position);

Toast.makeText(getApplicationContext(), selection, 0).show();

}

public   void  onNothingSelected(AdapterView<?> parent) {

}

});

}

14.5. 菜单 (Menu)

Ø 添加菜单项

Ÿ 重写 Actvity 的 onCreateOptionsMenu(Menu menu)方法

Ÿ 添加菜单项

调用方法中参数 menu 的 add(CharSequence title) 方法

Ÿ 添加子菜单

调用 menu 对象的 addSubMenu( final  CharSequence title)

该方法返回一个SubMenu对象

Ÿ 添加子菜单的菜单项

调用SubMenu对象的add(CharSequence title) 方法

Ø 处理菜单点击事件

Ÿ 重写 Activity 的 onOptionsItemSelected(MenuItem item) 方法

参数 item 即为被选中的菜单项

Ø 代码

public   boolean  onCreateOptionsMenu(Menu menu) {

menu.add( " 增加 " );

menu.add( " 修改 " );

menu.add( " 删除 " );

SubMenu subMenu = menu.addSubMenu( " 查询 " );

subMenu.add( " 按照序号查询 " );

subMenu.add( " 按照姓名查询 " );

subMenu.add( " 按照邮箱查询 " );

return   super .onCreateOptionsMenu(menu);

}

public   boolean  onOptionsItemSelected(MenuItem item) {

Toast.makeText( this , item.getTitle(), 0).show();

return   super .onOptionsItemSelected(item);

}

14.6. 内容提示文本框 ( AutoCompleteTextView)

Ø 单次提示

Ø 代码

< AutoCompleteTextView

     android:id = "@+id/actv"

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"

     android:completionThreshold = "1"   />

private   void  setAutoCompleteTextView() {

AutoCompleteTextView actv = (AutoCompleteTextView) findViewById(R.id. actv );

String[] items = {  "tom" ,  "tony" ,  "terry" ,  " 张孝祥 " ,  " 张海军 " ,  " 张泽华 "  };

ArrayAdapter<String> adapter =  new  ArrayAdapter<String>( this , android.R.layout. simple_dropdown_item_1line , items);

actv.setAdapter(adapter);

}

Ø 多次提示

Ø 代码

< MultiAutoCompleteTextView

     android:id = "@+id/mactv"

     android:layout_width = "fill_parent"

     android:layout_height = "wrap_content"

android:completionThreshold = "1"   />

private   void  setMultiAutoCompleteTextView() {

MultiAutoCompleteTextView mactv = (MultiAutoCompleteTextView) findViewById(R.id. mactv );

String[] items = {  "tom" ,  "tony" ,  "terry" ,  " 张孝祥 " ,  " 张海军 " ,  " 张泽华 "  };

ArrayAdapter<String> adapter =  new  ArrayAdapter<String>( this , android.R.layout. simple_dropdown_item_1line , items);

mactv.setAdapter(adapter);

mactv.setTokenizer( new  MultiAutoCompleteTextView.CommaTokenizer());

}

14.7. 手势识别 ( GestureOverlayView)

Ø 创建手势库

Ÿ 导入 SDK 中的工程

android-sdk-windows\samples\android-8\GestureBuilder

这个工程不能直接导入,需要添加三个配置文件:.classpath、.project、default.properties

Ÿ 将工程部署到手机中,创建手势库

手势库会存储在手机 SD 卡的根目录,文件名为: gestures

Ø 代码

将gestures放入 res/raw 文件夹下

< android.gesture.GestureOverlayView

     android:id = "@+id/gov"

     android:layout_width = "fill_parent"

     android:layout_height = "fill_parent"

android:gestureStrokeType = "multiple"   />

GestureOverlayView gov = (GestureOverlayView) findViewById(R.id. gov );

final  GestureLibrary library = GestureLibraries.fromRawResource( this , R.raw. gestures );

library.load();

gov.addOnGesturePerformedListener( new  OnGesturePerformedListener() {

public   void  onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {

ArrayList<Prediction> list = library.recognize(gesture);

for  (Prediction p : list)

System. out .println(p. name  +  ": "  + p. score );

}

});

14.8. 网页视图 (WebView)

Ø 代码

< WebView

     android:id = "@+id/webView"

     android:layout_width = "fill_parent"

     android:layout_height = "fill_parent"   />

WebView webView = (WebView) findViewById(R.id. webView );

webView.getSettings().setBuiltInZoomControls( true ); //  放大缩小按钮

webView.getSettings().setJavaScriptEnabled( true ); // JS 允许 

webView.setWebChromeClient( new  WebChromeClient()); // Chrome 内核

webView.loadUrl( "http://192.168.1.10 0 :8080" );

15. 样式与主题

15.1. 样式

Ø 定义样式

Ÿ 设置样式,在 values 文件夹下的任意文件中的 <resources>中配置 <style> 标签

< style  name = " style 1" >

< item  name = "android:layout_width" > fill_parent </ item >

< item  name = "android:layout_height" > wrap_content </ item >

</ style >

Ÿ 继承样式,在 <style> 标签中配置属性 parent

< style  name = " style2 "  parent = "@style/ style 1" >

< item  name = "android:textColor" > #FF0000 </ item >

</ style >

Ÿ 继承样式,在 name 中引用其他样式

< style  name = " style 2. style 3" >

< item  name = "android:textSize" > 30sp </ item >

</ style >

Ø 使用样式

Ÿ 在 layout 文件的标签中配置 style 属性

< Button  

style = "@style/ style2.style3 "

     android:text = " 这是 一个按钮 "

/>

15.2. 主题

Ÿ 定义过的样式也可以应用在 <activity> 和 <application> 标签中,使用 theme属性尽心配置

< style  name = "theme" >

< item  name = "android:windowNoTitle" > true </ item >

< item  name = "android:windowFullscreen" > ?android:windowNoTitle </ item >

</ style >

< activity  android:name = ".MainActivity"

                   android:label = "@string/app_name"

                   android:theme = "@style/theme"

>

Ÿ ?  表示引用其他属性的值

Ÿ @  表示访问资源文件

Ÿ 如果使用 android 内置的样式, IDE 自动提示的“ _ ”要替换成“ . ”

16. 国际化与屏幕适配

16.1. 国际化

Ÿ 在 values 和 drawable 文件夹后加上语言以及地区名,程序中需要国际化的部分使用资源 ID

values-en-rUK

values-en-rUS

values-zh-rCN

values-zh-rTW

Ÿ 匹配规则

在匹配资源时先会找语言、地区完全匹配的

如果没有地区匹配的,则查找语言匹配的

如果没有语言匹配的则找默认 values

16.2. 屏幕适配

Ÿ 在 layout 文件夹后加上分辨率,系统会根据屏幕尺寸自动选择

注意分辨率中的乘号是“ x ”不是“ * ”

Ÿ 如果没有匹配的分辨率会找默认 layout 文件夹

17. 动画特效

17.1. Frame

Ÿ 通过多个画面连续播放实现动画效果

Ÿ 详见文档 android-sdk-windows/docs/guide/topics/resources/animation-resource.html

17.2. Tween

Ÿ 将某个组件以渐变的方式实现透明、缩放、移动、旋转等动画效果

Ÿ 详见文档 android-sdk-windows/docs/guide/topics/resources/animation-resource.html

17.3. 使用动画切换 Activity

Ÿ 在 startActivity() 方法调用之后调用 overridePendingTransition( int  enterAnim,  int exitAnim)方法

enterAnim 进入的动画资源 id

exitAnim 退出的动画 资源 id

17.4. 使用动画翻页

Ÿ XML 配置

< ViewFlipper

android:id = "@+id/viewFlipper"

android:layout_width = "fill_parent"

android:layout_height = "fill_parent"

>

    < ImageView

     android:layout_width = "wrap_content"

     android:layout_height = "wrap_content"

    android:src = "@drawable/bb2"

    />

    < ImageView

     android:layout_width = "wrap_content"

     android:layout_height = "wrap_content"

    android:src = "@drawable/bb3"

    />

</ ViewFlipper >

Ÿ Java 代码

public   boolean  onTouchEvent(MotionEvent event) {

ViewFlipper viewFlipper = (ViewFlipper) findViewById(R.id. viewFlipper );

switch  (event.getAction()) {

case  MotionEvent. ACTION_DOWN :

start  = event.getX();

break ;

case  MotionEvent. ACTION_UP :

float  end = event.getX();

if  (end >  start ) {

viewFlipper .setInAnimation( this , R.anim. previous_enter );

viewFlipper .setOutAnimation( this , R.anim. previous_exit );

viewFlipper .showPrevious();

}  else   if  (end <  start ) {

viewFlipper .setInAnimation( this , R.anim. next_enter );

viewFlipper .setOutAnimation( this , R.anim. next_exit );

viewFlipper .showNext();

}

break ;

}

return   super .onTouchEvent(event);

}

18. 其他

18.1. 传感器

Ø 传感器参数

Ÿ 传感器类型

方向 Sensor. TYPE_ORIENTATION

加速 Sensor. TYPE_ACCELEROMETER

光线 Sensor. TYPE_LIGHT

磁场 Sensor. TYPE_MAGNETIC_FIELD

距离 Sensor. TYPE_PROXIMITY

温度 Sensor. TYPE_TEMPERATURE

Ÿ 传感器反应速度

SensorManager. SENSOR_DELAY_FASTEST

SensorManager. SENSOR_DELAY_GAME

SensorManager. SENSOR_DELAY_UI

SensorManager. SENSOR_DELAY_NORMAL

Ø 使用方向传感器

Ÿ 获得传感器管理器

SensorManager manager = (SensorManager) getSystemService( SENSOR_SERVICE );

Ÿ 获得方向传感器

Sensor sensor = manager.getDefaultSensor(Sensor. TYPE_ORIENTATION );

Ÿ 注册监听器

manager .registerListener( listener ,  sensor , SensorManager. SENSOR_DELAY_NORMAL );

Ÿ 监听器

private   final   class  MySensorEventListener  implements  SensorEventListener {

public   void  onSensorChanged(SensorEvent event) {

System. out .println(event. values [0]);

}

public   void  onAccuracyChanged(Sensor sensor,  int  accuracy) {

}

}

Ÿ 取消监听器

manager .unregisterListener( listener ,  sensor );

18.2. 触摸事件

Ø 拖拽

Ÿ XML 配置

< ImageView

     android:id = "@+id/image"

     android:layout_width = "wrap_content"

     android:layout_height = "wrap_content"

     android:scaleType = "matrix"

     android:src = "@drawable/image"   />

Ÿ Java代码

ImageView imageView = (ImageView) findViewById(R.id. image );

imageView.setOnTouchListener( new  MyOnTouchListener());

private   class  MyOnTouchListener  implements  OnTouchListener {

private   float   x ;

private   float   y ;

private  Matrix  currentMatrix  =  new  Matrix(); //  用来操作图片的矩阵

private  Matrix  oldMatrix  =  new  Matrix();

public   boolean  onTouch(View v, MotionEvent event) {

switch  (event.getAction()) {

case  MotionEvent. ACTION_DOWN :  //  按下时

x  = event.getX();  //  获取 x 轴坐标

y  = event.getY(); //  获取 y 轴坐标

oldMatrix .set( imageView .getImageMatrix()); //  记住位置

break ;

case  MotionEvent. ACTION_MOVE :  //  移动时

currentMatrix .set( oldMatrix );  //  设置成按下时记住的位置

currentMatrix .postTranslate(event.getX() -  x , event.getY() -  y ); //  改变位置

break ;

}

imageView .setImageMatrix( currentMatrix ); //  移动图片

return   true ;

}

}

Ø 多点触摸

private   class  MyOnTouchListener  implements  OnTouchListener {

private   float   x ; //  图片移动前的 x 轴坐标

private   float   y ; //  图片移动前的 y 轴坐标

private  Matrix  currentMatrix  =  new  Matrix();  //  用来移动图片的矩阵

private  Matrix  oldMatrix  =  new  Matrix(); //  图片移动前的矩阵

private   int   type ; //  操作类型 ,  一根手指触摸还是两根手指触摸

private   float   start ; //  第二根手指按下时的距离

private   float   end ; //  两根手指移动后的距离

private  PointF  point ; //  放大时的中心点

public   boolean  onTouch(View v, MotionEvent event) {

switch  (event.getAction() & MotionEvent. ACTION_MASK ) {

case  MotionEvent. ACTION_DOWN :

type  = 1;

x  = event.getX();

y  = event.getY();

oldMatrix .set( imageView .getImageMatrix());

break ;

case  MotionEvent. ACTION_MOVE :

currentMatrix .set( oldMatrix );

if  ( type  == 1) {  // 1 根手指触摸

currentMatrix .postTranslate(event.getX() -  x , event.getY() -  y );

}  else  {  // 2 跟手指触摸

end  = countDistance(event);  //  计算结束时距离

float  scale =  end  /  start ;  //  计算缩放比例

currentMatrix .postScale(scale, scale,  point . x ,  point . y );  //  设置缩放

}

break ;

case  MotionEvent. ACTION_POINTER_DOWN :

type  = 2;

start  = countDistance(event);  //  计算开始时距离

point  = countPoint(event);  //  计算中心点

oldMatrix .set( imageView .getImageMatrix());

break ;

}

imageView .setImageMatrix( currentMatrix );  //  改变图片

return   true ;

}

}

public   float  countDistance(MotionEvent event) {

float  a = event.getX(1) - event.getX(0);  // x 轴距离

float  b = event.getY(1) - event.getY(0);  // y 轴距离

return  ( float ) Math.sqrt(a * a + b * b);  //  勾股定理

}

public  PointF countPoint(MotionEvent event) {

float  x = (event.getX(0) + event.getX(1)) / 2;  // x 轴中间点

float  y = (event.getY(0) + event.getY(1)) / 2;  // y 轴中间点

return   new  PointF(x, y);

}

18.3. 读取 SIM 卡

Ø 电话号码、运营商信息

Ÿ 需要权限

< uses-permission  android:name = "android.permission.READ_PHONE_STATE"  />

< uses-permission  android:name = "android.permission.ACCESS_COARSE_LOCATION"  />

Ÿ Java 代码

TelephonyManager manager = (TelephonyManager) getContext().getSystemService(Context. TELEPHONY_SERVICE );

System. out .println( " 电话号码 : "  + manager.getLine1Number());

System. out .println( " 运营商编号 : "  + manager.getNetworkOperator());

System. out .println( " 运营商名字 : "  + manager.getNetworkOperatorName());

Ø 联系人

Ÿ 需要权限

< uses-permission  android:name = "android.permission.READ_ CONTACTS "  />

< uses-permission  android:name = "android.permission. WRITE _ CONTACTS "  />

Ÿ Java 代码

Uri uri = Uri.parse( "content://icc/adn" );

Cursor c = getContentResolver().query(uri,  null ,  null ,  null ,  null );

while  (c.moveToNext())

System. out .println(c.getString(c.getColumnIndex( "name" )) +  ": "  + c.getString(c.getColumnIndex( "number" )));

Ø 通话记录

Ÿ 需要权限

< uses-permission  android:name = "android.permission.READ_ CONTACTS "  />

< uses-permission  android:name = "android.permission. WRITE _ CONTACTS "  />

Ÿ Java 代码

Uri uri = CallLog.Calls. CONTENT_URI ;

Cursor c = getContentResolver().query(uri,  null ,  null ,  null ,  null );

while  (c.moveToNext())

System. out .println(c.getString(c.getColumnIndex( "number" )) +  ": "  + c.getString(c.getColumnIndex( "type" )));

Ÿ 源代码

ContactsProvider\src\com\android\providers\contacts\ CallLogProvider.java

18.4. 安装程序

Ÿ 需要权限

< uses-permission  android:name = "android.permission. INSTALL_PACKAGES "  />

Ÿ Java 代码

File file =  new  File(Environment.getExternalStorageDirectory(),  " test .apk" );

Intent intent =  new  Intent();

intent.setAction(Intent. ACTION_VIEW );

intent.setDataAndType(Uri.fromFile(file),  "application/vnd.android.package-archive" );

startActivity(intent);

18.5. 关闭程序

Ÿ 杀死当前进程

Process.killProcess(Process.myPid());

Ÿ 退出虚拟机

System.exit(0);

Ÿ 根据包名关闭后台进程

ActivityManager manager = (ActivityManager) getSystemService( ACTIVITY_SERVICE );

manager.restartPackage( "cn.itcast.test" );

< uses-permission  android:name = "android.permission.RESTART_PACKAGES"  />

18.6. 使用 HTML 构建界面

Ÿ HTML

<! DOCTYPE   html   PUBLIC   "-//W3C//DTD HTML 4.01 Transitional//EN"   "http://www.w3.org/TR/html4/loose.dtd" >

< html >

< head >

< meta   http-equiv = "Content-Type"   content = "text/html; charset=UTF-8" >

< title > Insert title here </ title >

< script   type = "text/javascript" >

function  show(jsondata) {

var  jsonobjs = eval(jsondata);

var  table = document.getElementById( "personTable" );

for  (  var  y = 0; y  <  jsonobjs.length; y++) {

var  tr = table.insertRow(table.rows.length);

var  td1 = tr.insertCell(0);

var  td2 = tr.insertCell(1);

td2.align =  "center" ;

var  td3 = tr.insertCell(2);

td3.align =  "center" ;

td1.innerHTML = jsonobjs[y].name;

td2.innerHTML = jsonobjs[y].amount;

td3.innerHTML =  "<a href='javascript:contact.call(\""  + jsonobjs[y].phone +  "\")'>" + jsonobjs[y].phone +  "</a>" ;

}

}

</ script >

</ head >

< body   onload = "javascript:contact.show C ontacts()" >

< table   border = "0"   width = "100%"   id = "personTable"   cellspacing = "0" >

< tr >

< td   width = "30%" > 姓名 </ td >

< td   width = "30%"   align = "center" > 存款 </ td >

< td   align = "center" > 电话 </ td >

</ tr >

</ table >

</ body >

</ html >

Ÿ XML 代码

< WebView

         android:id = "@+id/webView"

         android:layout_width = "fill_parent"

         android:layout_height = "fill_parent"   />

Ÿ Java 代码

public   class  MainActivity  extends  Activity {

private  WebView  webView ;

public   void  onCreate(Bundle savedInstanceState) {

super .onCreate(savedInstanceState);

setContentView(R.layout. main );

webView  = (WebView) findViewById(R.id. webView );

webView .getSettings().setJavaScriptEnabled( true );

webView .loadUrl( "file:///android_asset/index.html" );

webView .addJavascriptInterface( new  Contact(),  "contact" );

}

private   final   class  Contact {

public   void  showContacts() {

String json =  "[{\"name\":\"zxx\", \"amount\":\"99999\", \"phone\":\"18600012345\"}]";

webView .loadUrl( "javascript:show('"  + json +  "')" );

}

public   void  call(String phone) {

startActivity( new  Intent(Intent. ACTION_CALL , Uri.parse( "tel:"  + phone)));

}

}

}

18.7. apk 文件反编译

Ÿ 使用解压缩工具打开 apk 文件,找到其中 dex 文件

Ÿ 创建 Java 工程,导入 dex2jar中的所有 jar 文件

Ÿ 创建运行环境运行其中pxb.android.dex2jar.v3.Main 类, 指定 dex 文件地址,会在同目录下生成 jar 文件

Ÿ 使用jd-gui打开 jar 文件获取源码