Android应用开发教程

Size: px
Start display at page:

Download "Android应用开发教程"

Transcription

1

2 人购买享受 6.5 QQ:76601 免费提供全书手 Android 把手教学视频 应用开发教程 教师指定本书为授 课教材优惠多多 钟元生高成珍编著 和三本以上包邮

3 内容简介 本书系统讲解了 Android 应用开发的基础知识, 既有基本语法与基本应用, 又有直 接运行案例的分析, 使读者能理论联系实践, 寓教于练 寓教于用 通过学习, 读者将全面了解 Android 应用程序的开发方法, 实现 Android 编程的快 速入门 全书分十三章, 包括 Android 简介与环境搭建 Android 界面编程基础 Android 高级界面设计 事件处理 Activity 与 Intent 图形与图像处理 Android 中的数据 存取 Service 分析 BroadcastReceiver 的使用 GPS 位置服务与地图编程 Android 客户端与服务器端交互 综合案例 校园通 以及基于竞赛的测试方案设计 本书内容充实 材料新颖 案例丰富 条理清晰, 适合于软件工程 计算机科学与 技术 信息管理与信息系统等专业本科 专科生或研究生作为教材使用, 也供 Android 软件设计爱好者参考 图书在版编目 (CIP) 数据 Android 应用开发教程 / 钟元生, 高成珍编著.- 南昌 : 江西高校出版社, ISBN Ⅰ.1A... II.1 钟...2 高...Ⅲ.1 移动终端 - 应用程序 - 程序设计 - 教材 IV.1TN 中国版本图书馆 CIP 数据核字 (2013) 第 号 出版发行社址邮政编码总编室电话销售电话网址印刷经销开本印张字数版次印数书号定价 江西高校出版社江西省南昌市洪都北大道 96 号 (0791) (0791) www. juacp. com 南昌市光华印刷有限责任公司各地新华书店 787 mm x 1092 mm 1/ 千字 2015 年 1 月第 1 版第 2 次印刷 3001~4500 册 ISBN 元. 赣版权登字 版权所有侵权必究

4 作者简介 钟元生, 江西财经大学软件与通信工程学院教授 学术委员会主 任, 电子商务专业博士生导师, 教育技术学研究生导师组组长, 浙江大学博士毕业, 美国加州大学尔湾分校访问学者, 江西省计 算机学会理事, 江西省政府学位委员会学科评议组成员, 江西省 中青年学科带头人 曾任江西财经大学评建创优专家组副组长 用友软件学院教学副院长, 科技部科技支撑计划项目评审专家 江西省教学成果奖评审专家, 多次担任 IEEE 电子商务国际学术会 议程序委员 主持或参与国家自然科学基金 全国教育科学规划教育部重点课题 江西省自然科 学基金 江西省工业支撑计划项目和江西省科技型中小企业技术创新基金项目等 10 多 项, 江西省教育厅科技项目等其他省级以上项目多项 作为第一完成人获江西省教学成 果奖一等奖两项, 参与完成江西省教学成果二等奖 三等项多项, 获全国计算机基础教 育优秀教材二等奖一项 出版专著 2 部, 主编教材多部 江西省大学生手机软件设计赛发起人 总策划和前三届竞赛承办专家委员会负责人, 正在联合全国近百所高校举办全国大学生手机软件邀请赛 创办, 基于软件工厂思想, 探索移动互联网领域的软件设计 服务创新 和人才培养等工作 培养了一大批软件工程 计算机科学与技术 电子商务 教育技术 MBA 等专业研究生. 高成珍, 江西科技学院信息工程学院教师, 江西财经大学软件与 通信工程学院教育技术学专业 ---- 移动学习与手机软件开发方向 硕士毕业, 作为骨干开发完成 Android 手机编程 网络课程, 曾 任江西省大学生手机软件设计赛 ----Android 编程指导教师培训班 主讲教师 竞赛命题专家和评审教师 参与创建的 Android 编程 网络学习社区 ----, 影响越来越大 主编 android 编 程经典案例解析 于 2015 年 1 月在清华大学出版社出版

5 Android 编程 优秀任课教师 ( 仅列选用本书的教师, 其他教师将来再印时补充 ) 朱文强陈虹曹重华程小扬刘清 QQ: QQ: QQ: QQ: QQ: 江西财经大学软件与通萍乡学院计算机系 校江西财经大学软件与通赣南师范学院数学与计算井冈山大学电子与信息信工程学院长办公室信工程学院机学院工程学院 万念斌魏启明万里勇孙洪伟陈海俊 厦门理工学院软件学院 QQ: QQ: 九江学院信息科学与技南昌工学院信息学院术学院 QQ: 南昌大学科技学院 QQ: 江西机电职业技术学院信息管理系 胡晓光邓磊古发辉王金强何英 QQ: QQ: QQ: QQ: QQ: 天津中德职业技术学院 江西应用技术职业学院江西应用技术职业学院江西工业工程学院计算机江西机电职业学院 ( 兼 计算机系 电子信息工程系 电子信息工程系 系 课 ), 江西财经大学软件 与通信工程学院硕士生

6 阅读指南及再印修订说明 本书假定您懂一些基本的 Java 语法知识, 具有一定的 Java 编程经验 如果没有 Java 基础, 也可阅读本书, 但在涉及 Java 知识时, 建议去补充学习一些相关的 Java 知识 书中示例较多, 源代码较长 本书注重示例的程序分析, 为了方便介绍知识重点 压缩篇幅, 仅列出一些关键代码, 读者可从相应网站下载完整源码并直接运行 强烈建议, 读者基于书上说明和关键代码自己补充完成程序, 而不主张一开始就看下载程序 粗看 调通并对比运行结果就万事大吉 仅在反复尝试失败时看下载的源码 为便于教学, 书中源码分别添加了行号, 并为一些关键语句添加了注释, 例如 : 1 public class MainActivity extends Activity { 2 public void oncreate(bundle savedinstancestate) { 3 super.oncreate(savedinstancestate); 调用父类的该方法 4 setcontentview(r.layout.activity_main); 设置 Activity 对应的界面布 5 } 局文件 6 public boolean oncreateoptionsmenu(menu menu) { 创建选项菜单 7 getmenuinflater().inflate(r.menu.activity_main, menu); 指定菜单资源 8 return true; 9 } 10 } 其中, 左边的 表示行号, 中间的 super.oncreate (savedinstancestate); 才是真实的程序代码内容 及后面的内容 调用父类的该方法 表示对中间代码的 注释 这些部分非真实编程时所需, 请读者注意 为了方便读者学习, 网站上提供了本书相关资源下载, 包括源码 课件 教学视频 试题等, 网址为 : 如果大家在学习或使用本书过程中有什么疑问或有什么好的建议, 欢迎通过 QQ 群 : (android 学习交流群 ) 或 QQ: 与我们联系 本书第一版出版后,Android 提供了三合一的免安装压缩包 (adt-bundle-windows-x zip), 除 JDK 和 Java 开发环境需要安装配置外, 仅 需对上述压缩包解压, 就可直接使用 Eclipse 和 Android 开发工具 ADT+SDK+AVD, 大大 方便初学者学习 因此, 本次重印时对第一章以 android4.4 版为准作了相应的修订, 以 方便读者 教学实践中, 我们发现很多同学上机调试经常遇到一些错误就措手无策 本书整理 了 Android 上机调试中部分常见错误与程序调试方法 ( 见附录 6), 希望对这些同学有用 再印时还增加了采用本书的 Android 部分优秀任课教师和少量优秀学生名单, 以让更多的人认识他们, 交流移动应用商机

7 前 言 近年来, 移动互联网影响越来越大,Android 终端越来越普及, 各种新的 APP 层出不 穷 谁更早地掌握了手机编程技术, 谁就占有发展先机 许多高校纷纷开设 Android 编 程课, 大家都希望有一本好的教材 为此, 在江西省大学生手机软件设计赛指导教师 Android 编程 培训班和多年 Android 教学经验的基础上, 完成本书 本书努力做到 : (1) 既介绍 Android 基本语法 基本知识和基本应用, 又介绍可直接运行的应用教 学案例 使教师容易教学, 学生能寓教于练 寓教于用 (2) 不仅讲解注重语法细节, 而是循序渐进地引导和启发学生建构自己的知识体系, 包括用图解法详细分析 Android 应用程序的结构 运行过程以及各部分间的调用关系, 演示 Android 应用的开发流程, 给出一些关键代码由学生自己去重组和实现相应功能 (3) 重点关注手机应用中常见案例, 将有关知识串起来 结合学生用 Android 手机 的体验, 逐步引导他们深入思考其内部实现 每章都有一些练习题, 帮助学生自测 (4) 基于竞赛的测试方案设计, 使学习更有趣味 为帮助老师组织测试和 Android 程序竞赛, 本书专门增设这一章, 介绍了我们的经验, 供大家参考 本书由钟元生担任主编, 负责全书的组织设计 质量控制和统稿定稿 各章分工如 下 : 钟元生负责第 1 第 2 第 3 和第 13 章, 高成珍负责第 4 第 5 第 7 第 10 第 11 和第 12 章, 涂云钊负责第 6 章, 徐军负责第 8 章, 朱文强负责第 9 章 研究生刘平 何 英 章雯 陈海俊 吴微微 高必梵 杨旭 邵婷婷等参与了初稿讨论 编辑加工以及 配套教学课件的制作工作 陈海俊做了大量的初稿电脑排版工作 许多领导与朋友为本书编写 大学生手机软件设计赛提供了无私支援 特别是, 江 西财经大学校长 博士生导师王乔教授, 在百忙之中过问竞赛并特批经费支持 ; 江西省 科技厅副厅长 ( 原江西财经大学副校长 ) 博士生导师卢福财教授对竞赛给予了大力支持 ; 江西省教育厅高等院校科技开发办公室主任陈东林编审 省教育工委党校校长杜侦研究 员参与策划竞赛 江西财经大学软件与通信工程学院院长关爱浩博士 分党委书记李新 海先生 副院长黄茂军博士 副院长白耀辉博士 副院长邓庆山博士, 现代经济管理学 院院长 博士生导师陆长平教授, 经济管理与创业模拟实验中心主任 博士生导师夏家 莉教授, 协同创新中心监测预警仿真部主任万本庭博士 ; 江西高校出版社徐明华主任及 许多朋友以不同的形式对我们的工作提供了无私帮助 对上述领导与朋友们的帮助, 我 们深表感谢 希望本书帮助 Android 任课教师更快地教好 Android 编程课, 也能帮助使用本书的 学生更快更扎实地掌握 Android 应用开发技能 编者于南昌江西财经大学麦庐园 2014 年 12 月

8 目录 第 1 章 Android 简介与环境搭建 初识 Android Android 的概述 Android 的体系结构 搭建 Android 开发环境 安装 JDK 和配置 Java 开发环境 Eclipse android SDK 和 ADT 三合一安装包的安装 管理模拟器 开发第一个 Android 应用 创建 Android 项目 运行 Android 应用 Android 应用结构分析 Android 应用程序的结构 Android 应用程序运行过程 Android 应用下载与安装 Android 四大基本组件介绍 Android 设计之 MVC 模式 本章小结 课后练习 第 2 章 Android 界面编程基础 基础 View 组件简介 文本显示框 TextView 文本编辑框 EditText 按钮 Button 布局管理器 线性布局 表格布局 相对布局 其他布局 布局的综合运用 I

9 II 2.3 开发自定义 View 本章小结 课后练习 第 3 章 Android 高级界面组件 图片组件 ImageView 图片视图 ImageButton 图片按钮 ImageSwitcher 图片切换器 Gallery 画廊视图 列表视图 AutoCompleteTextView 自动提示 Spinner 列表 ListView 列表 ExpandableListView 扩展下拉列表 对话框 对话框简介 创建对话框 自定义对话框 菜单 选项菜单 上下文菜单 本章小结 课后练习 第 4 章事件处理 Android 的事件处理机制 基于监听的事件处理 基于回调的事件处理 直接绑定到标签 Handler 消息传递机制 异步任务处理 本章小结 课后练习 第 5 章 Activity 与 Intent Activity 详解... 95

10 5.1.1 Activity 概述 创建和配置 Activity 启动和关闭 Activity Activity 的生命周期 Activity 间的数据传递 Intent 详解 Intent 概述 Intent 构成 Intent 解析 本章小结 课后练习 第 6 章图形与图像处理 简单图片和逐帧动画 简单图片 逐帧动画 示例讲解 自定义绘图 Canvas 和 Paint Shader Path 和 PathEffect 示例讲解 本章小结 课后练习 第 7 章 Android 中的数据存取 普通文件存储 手机内部存储空间文件的存取 读写 SD 卡上的文件 共享参数 (SharedPreferences) 文件存取 SharedPreferences 的存储位置和格式 读写其他应用 SharedPreferences SQLite 数据库 SQLite 数据库简单介绍 SQLite 数据库相关类 使用 ContentProvider 实现数据共享 III

11 IV ContentProvider 简单介绍 ContentProvider 操作常用类介绍 ContentProvider 应用实例 获取网络资源 本章小结 课后练习 第 8 章 Service 解析 Service 概述 Service 介绍 启动 Service 的两种方式 Service 中常用方法简介 绑定 Service 过程 Service 生命周期 跨进程调用 Service 什么是 AIDL 服务 建立 AIDL 文件 建立 AIDL 服务端 建立 AIDL 客户端 调用系统服务 本章小结 课后练习 第 9 章 BroadcastReceiver 的使用 BroadcastReceiver 介绍 发送广播的两种方式 音乐播放器 本章小结 课后练习 第 10 章 GPS 位置服务与地图编程 GPS 位置服务编程 支持位置服务的核心 API 简单位置服务应用 Google Map 服务编程 使用 Google 地图的准备工作 根据位置信息在地图上定位

12 10.3 本章小结 课后练习 第 11 章 Android 客户端与服务器端交互 HTTP 协议介绍 Apache HttpClient 介绍 客户端发送请求和接收响应 服务器端编程基础 Tomcat 服务器介绍 Servlet 简介 服务器端编程 第一次交互 高校信息查询平台 登录功能 注册功能 文件上传 资料下载 信息查询 本章小结 课后练习 第 12 章综合案例 校园通 校园通 概述 校园通 应用程序结构 校园通 应用程序功能模块 学校生活模块 出行指南模块 游玩南昌模块 号码百事通 注意事项 本章小结 课后习题 第 13 章基于竞赛的测试方案设计 Android 编程竞赛测试背景 竞赛目的 竞赛项目 V

13 VI 13.2 Android 手机应用编程赛考核知识点及要求 Java 程序设计 Android 程序设计 Android 手机应用编程赛测试方法 测试形式 测试分数权重 机试注意事项 Android 应用编程赛组织过程设计 手机应用编程赛机试流程 手机应用编程赛评审标准 手机应用编程赛评审安排 本章小结 附录 附录 1:Android 开发工具及书中所用代码 附录 2: 手机应用编程 (Android) 赛 本科基础题样卷 附录 3: 手机应用编程 (Android) 赛 本科编程题样卷及评分标准 附录 4: 手机应用编程 (Android) 赛 专科基础题样卷 附录 5: 手机应用编程 (Android) 赛 专科编程题样卷及评分标准 附录 6:Android 中部分常见错误与程序调试方法 参考文献

14 本章要点 初识 Android 第 1 章 Android 简介与环境搭建 搭建 Android 开发环境 开发第一个 Android 应用 Android 应用结构分析 Android 应用的下载与安装 本章知识结构图 Android 简介与环境搭建 初识 Android 搭建 Android 开发环境 开发第一个 Android 应用 Android 程序结构分析 Android 概述 Android 体系结构 Java JDK 的安装 Eclipse 的安装 ADT 的安装 Android SDK 安装与配置 模拟器的创建与启动 创建 Android 项目 Android 程序结构 本章示例 运行 Android 应用 程序运行过程 Android 应用下载与安装 基本组件介绍 MVC 模式 Android 环境搭建成功后, 创 建第一个 Android 项目, 启动 模拟器, 并运行 Android 程序, 效果如左图所示 1

15 本章是 Android 应用开发的准备章节, 主要介绍什么是 Android, 如何搭建 Android 开发环境, 然后通过一个简单的 HelloAndroid 程序讲解 Android 项目的创建 运行过程以及 Android 应用程序目录结构中各文件的作用等 本章是学好 Android 的基础, 是学习其他章节所必须掌握的内容 1.1 初识 Android 近年来在开放的手持设备中,Android 无疑是发展最快的操作系统之一, 覆盖高 中 低端手机系统, 在众多系统中,Android 为什么能脱颖而出? 它究竟有什么特点? 下面我们从不同的角度来认识 Android Android 的概述 1. 什么是 Android Android( 英文翻译为机器人, 前期版本主要标志是一个绿色机器人,Android 3.0 之后的标志改为蜂巢 ), 最早是由安迪 罗宾 (Andy Rubin) 创办, 随后在 2005 年的时候被 Google 公司收购 Android 是基于 Linux 平台的开源手机操作系统,Android 平台由操作系统 中间件 用户界面和应用软件组成, 号称是首个为移动终端打造的真正开放和完整的移动软件 2008 年 9 月 22 日, 美国运营商 T-Mobile USA 在纽约正式发布第一款 Google 手机 T-Mobile G1 该款手机由台湾宏达电代工制造, 是世界上第一部使用 Android 操作系统的手机 目前智能手机的应用已经越来越广泛, 市场上已经出现数万种运行于 Android 平台的手机应用软件, 涉及办公软件 影视娱乐软件 游戏软件等应用领域, 可以说已深入到移动应用的方方面面 应用软件开发人才的需求数量庞大, 据统计, 软件应用类 Android 开发人才的需求约占总需求的 72% 2.Android 的特点 : 开放性 平等性 无界性 方便性 硬件的丰富性 Android 的体系结构 2 Android 系统的底层建立在 Linux 系统之上 该平台由操作系统 中间件 用户界面和应用软件四层组成, 采用一种被称为软件叠层 (Software Stack) 的方式进行构建 这种软件叠层结构使得层与层之间相互分离, 以明确各层的分工 这种分工保证了层与层之间的低耦合, 当下层的层内或层下发生改变时, 上层应用程序无须任何改变 Android 体系结构主要由三部分组成 底层以 Linux 内核工作为基础, 主要由 C 语言开发, 提供基本功能 ; 中间层包括函数库 Library 和 Dalvik 虚拟机, 由 C++ 语言开发 最上层是各种应用框架和应用软件, 包括通话程序, 短信程序等 应用软件则由各公司自行开发, 主要是以 Java 语言编写 可以把 Android 看作是一个类似于 Windows 的操作系统 Android 的体系结构如图 1-1 所示

16 图 1-1 Android 系统的体系结构 1. 应用程序 (APPLICATIONS) Android 内有一系列的核心应用, 如短信程序 日历工具 地图浏览器 网页浏览器等工具, 以及基于 Android 平台的应用程序框架, 所有的应用都是 Java 语言编写的 2. 应用程序框架 (APPLICATION FRAMEWORK) 开发者可以完全使用与那些内核应用程序相同的框架, 这些框架用于简化和重用应用程序的组件 若某程序能够 暴露 其内容, 则其他程序就可以使用这些内容 例如 Android 的四大组件 :Activity Service ContentProvider BroadcastReceiver 3. 系统运行库层 (LIBRARIES) Android 定义了一套 C/C++ 开发库供 Android 平台的其他组件使用 这些功能通过 Android 应用程序框架提供给开发者, 开发者是不能直接使用这些库的 4.Linux 内核层 (LINUX KERNEL) Android 的核心系统服务依赖于 Linux2.6 内核, 如安全性 内存管理 进程管理 网络协议栈和驱动模型 Linux 内核也同时作为硬件和软件栈之间的抽象层 1.2 搭建 Android 开发环境 本书中所有示例运行环境为 :Java JDK1.6+Eclipse4.2+ADT Android SDK 4.4 下面讲述这些工具下载的地址及在 Android 开发中扮演的功能角色 ( 见表 1-1) 3

17 4 表 1-1 Android 开发所需软件的下载地址及其功能 软件名称下载地址功能角色 Java JDK Eclipse Android SDK ADT Android 是基于 Java 的, 需要安装 Java 运行环境 免费 开源的集成开发工具, 方便 快捷开发 Android 应用开发工具包, 包含 Android 程序运行所需要的各种资源 类库 将 Eclipse 和 Android SDK 连接起来的纽带, 方便开发 Android 程序 注意 :(1) 本章假定读者已将所有的工具安装包下载并存放在 D:\android 文件夹下 (2) 上述 D:\android 文件夹是指下载软件包所存放的文件夹, 而不是将来运行的开发环境 文件所存放的文件夹 本书不特别指定时, 后者假定开发环境存放在 F 盘 1 上述开发工具中,Java JDK 和 Android SDK 是必需的, 而 Eclipse 和 ADT 是可选的 Eclipse 是一个集成开发工具, 能帮助开发者完成很多繁琐事情, 而 ADT 是 Eclipse 中 开发 Android 应用所需要的插件, 使用它们可以提高开发者的开发速度和效率 实际上, 完全可以通过记事本和命令行来开发和运行 Android 应用程序 官方下载地址 : Java JDK: Eclipse: Android SDK: ADT: 2 Android4.2 之后官方提供了三合一的安装包, 当前最新版本 4.4 三合一安装包是指包括了 Eclipse android SDK 和 Anroid ADT 三部分, 只要直接解 包即可, 大大地简化了安装可能, 便利初学者 三合一安装包下载地址 : ( 官方网站 ) 选用三合一安装包来配置 android 开发环境, 安装流程与主要步骤如图 1-2 所示 Java JDK 安装 安装 Java JDK 安装 Java JRE 配置 path 环境变量 配置 classpath 环境变量 三合一安装包解压与自安装 Eclipse 开发工具包 Android SDK Android ADT 插件 图 1-2 Android 开发环境搭建的流程与主要步骤

18 1.2.1 安装 JDK 和配置 Java 开发环境 Android 程序是基于 Java 语言的, 若要开发和运行 Android 程序, 必须首先安装 Java JDK, 并对其进行简单配置 1.JDK1.6 程序的安装单击下载好的 Java JDK 安装包, 然后弹出提示框, 点击下一步, 直到选择安装目录如图 1-3 所示, 此处将 Java JDK 安装在 F:\Java\jdk1.6.0_10\ 目录下, 然后继续下一步 ( 安装目录可任意设置, 建议选择的安装目录最好不要包含中文和空格 ) 设定 Java 的安装目录 图 1-3 设定 JDK 安装目录图 JDK(Java 开发工具 ) 安装过程中, 系统会自动安装 JRE(Java 运行时环境 ), 更改 JRE 的安装目录, 将其与 JDK 放在同一目录下, 如图 1-4 所示 图 1-4 设定 JRE 安装目录 5

19 6 安装完成后, 出现如图 1-5 所示界面 图 1-5 Java 环境安装结束界面 2. 配置 Java 环境在 Java JDK1.5 之前,Java JDK 安装完成后, 并不能立即使用, 还需要配置相关环境变量,Java JDK1.5 之后系统会有默认的配置, 但建议手动进行配置 右键单击计算机 ( 或我的电脑 ) 属性弹出如图 1-6 所示对话框, 选择高级 环境变量 图 1-6 系统高级属性对话框 首先, 在 系统变量 中新建一个 JAVA_HOME 变量, 该变量的值为 JDK 的安装目录 在此为 F:\Java\jdk1.6.0_10\( 与前面安装时指定的目录一致 ), 如图 1-7 所示

20 Java JDK 安装的目录 图 1-7 JAVA_HOME 环境变量设置图 建议使用大写, 代表常量, 但 Windows 不区分大小写 建议 JAVA_HOME 变量名为大写, 表示常量 但 Windows 系统不区分大小写, 即大写 小写 大小写混合表示同一个变量名, 虽不会出错, 但不符合规范 注意 : 变量值后不需要加任何符号 然后在系统变量中查找 path 变量, 如果存在, 则将 JDK 安装目录下的 bin 文件夹添加其后, 多个目录以分号 ( ; ) 隔开, 如图 1-8 所示 如果不存在则新建一个, 然后将 bin 目录放进去即可 %JAVA_HOME%\bin 代表的路径就是 F:\Java\jdk1.6.0_10\bin 添加 JDK 安装目录下的 bin 文件夹 图 1-8 在 path 变量中添加 Java bin 目录 新建 classpath 环境变量, 该变量的值为 JDK 安装目录下 lib 文件夹, 在此为 :. ; %JAVA_HOME%\lib, 其中点 (. ) 表示当前目录, 分号表示多个路径之间的分隔符 如图 1-9 所示 Java JDK 安装的目录下的 lib 文件夹 图 1-9 设定 classpath 环境变量 配置完成后, 单击开始 运行, 输入 cmd, 如图 1-10 所示, 确定, 打开命令行窗口 在命令行窗口中输入 java version 命令, 若能显示安装的 Java 版本信息, 如图 1-11 所示, 则表明 java 开发环境搭建成功 7

21 8 图 1-10 打开命令行窗口的命令 查看 Java 版本的命令 图 1-11 Java 环境测试结果 Java 版本信息 Eclipse android SDK 和 ADT 三合一安装包的安装 已经安装了 Java JDK 1.7 并配置好 Java 环境后, 按以下直接解压三合一安装包, 就可 以直接在 Eclipse 目录下, 运行 Eclipse.exe, 即可直接开发 android 程序 解压官方提供的 Android 三合一安装包 ( 从安卓官网下载或根据附录 1, 见图 1-12), 解压后包含三个文件夹 ( 见图 1-13) 打开 eclipse 文件夹, 启动 Eclipse( 见图 1-14) 第 一次启动时会弹出图 1-15 所示对话框, 确定项目默认存放的位置, 这里将其放在 F 盘下 的 android 文件夹下, 如果文件夹不存在, 系统将会自动创建 并勾选不再询问复选框 图 1-12 三合一安装包 adt64.rar 图 1-13 解包后文件夹图 1-14 启动 eclipse

22 默认使用该目录, 并不再弹出对话框询问 图 1-15 设置项目存放的路径 Eclipse 第一次启动时弹出存放位置对话框 当出现新界面 Eclipse Java EE IDE for Web Developers 标题时, 说明 Eclipse 正常可用 Eclipse 默认是不能开发 Android 程序的, 需要安装相应的插件 :ADT(Android Develop Tools) 但三合一安装包解包后, 就已经自动加载了 ADT 插件, 其标志是在 Eclipse 的菜单栏中多了两个按钮 ( 见图 1-16) 图 1-16 Eclipse 菜单栏上的图标 查看 Android 的安装目录, 包含许多文件夹, 各个文件夹的作用如表 1-2 所示 表 1-2 Android SDK 完整开发包下各文件的作用 文件名称 文件夹及文件的作用 add-ons 该目录下存放额外的附件软件 docs 该文件夹下存放 Android SDK 开发文件和 API 文档等 extras 该目录下存放一些额外的插件 platforms 该目录下存放所包含的 Android 版本 platform-tools 该目录下存放 Android 平台相关工具 samples 该目录下存放 Android 平台的一些示例程序 sources 该目录下存放 Android 的源文件 system-images 该目录下存放系统所使用的图片 temp 该目录用于存放一些临时文件 tools 该目录下存放大量 Android 开发 调试的工具 AVD Manager.exe Android 模拟器管理器 SDK Manager.exe Android SDK 管理器 SDK Readme.txt SDK 使用说明 注意 : 为了能在命令行窗口使用 Android SDK 的各种工具, 建议将 Android SDK 目 录下的 tools 子目录 platform-tools 子目录添加到系统的 path 环境变量中 管理模拟器 Android 程序的运行需要相应设备的支持, 既可以是真实的 Android 手机, 也可以是 9

23 Android 为我们提供的模拟器, 在此介绍模拟器的使用 管理模拟器有两种方式 : 命令行中输入相应命令或用 Eclipse 的图形化界面管理 1. 命令行管理 AVD 在命令行下管理 AVD 需要借助于 Android 命令 ( 位于 Android SDK 安装目录的 tools 子目录下 ), 如果直接执行 android 命令将会启动 Android SDK 和 AVD 管理器 除此之外, 该命令还支持如下子命令 10 表 1-3 Android 支持的命令 命令功能 android list 列出机器上所有已经安装的 Android 版本和 AVD 设备 android list avd 列出机器上所有已经安装的 AVD 设备 android list target 列出机器上所有已经安装的 Android 版本 android create avd 创建一个 AVD 设备 android move avd 移动或重命名一个 AVD 设备 android delete avd 删除一个 AVD 设备 android update avd 更新 AVD 设备使之符合新的 SDK 环境创建和启动模拟器的命令 : (1) android create avd n<avd 名称 > -t <android 版本 > (2) emulator avd<avd 名称 > 启动指定模拟器例如需要创建一个名为 myavd2 的 AVD 设备, 则可输入如下命令 ( 见图 1-17): Android create avd n myavd2 t 1, 1 代表 Android4.1 所对应的序号, 如果仅有一个 Android 版本, 则为 1, 否则可通过 list target 查看 Android 版本所对应的序号, 这里采用 2 图 1-17 使用 create 创建 AVD 的命令 模拟器的版本 创建一个名称为 myavd 的设备 提示 Do you wish to create a custom hardware profile [no], 这里我们直接按回车键, 就可以创建 AVD 设备 创建的 AVD 设备信息如图 1-18 所示 模拟器的参数配置信息 图 1-18 已创建模拟设备的信息

24 输入 android list avd 查看已安装的 AVD 设备, 如图 1-19 所示 注意 : 新创建的模拟器 列出所有的模拟器设备 图 1-19 列出已经安装的 Android 版本和 AVD 设备 (1) 创建 删除和浏览 AVD 之前, 通常应该先为 Android SDK 设置一个环境变量 : ANDROID_SDK_HOME, 该环境变量的值为磁盘上一个已有的路径 ( 可任选 ) (2) 如果不设置该环境变量, 开发者创建的虚拟设备默认保存在 C:\Documents and Setting\<user_name>\.android 目录下, 不同系统路径有所差异 (3) 如果设置了 ANDROID_SDK_HOME 环境变量, 那么虚拟设备就会保存在 %ANDROID_SDK_HOME%/.android 路径下 注意与 JAVA_HOME 等环境变量的区别, 它们都是指向自身的安装目录 2. 图形化管理 AVD(Android 虚拟设备管理器 ) 单击 Eclipse 菜单栏中的图标, 弹出 AVD 管理界面或者在 Eclipse 中选中 Windows AVD Manager 弹出 AVD 管理界面, 如图 1-20 所示 图 1-20 创建 AVD 模拟器 单击 New 按钮创建模拟器, 输入相应的参数, 如图 1-21 所示 11

25 12 图 1-21 设置 Android 模拟器参数 单击 Create AVD 创建该模拟器 创建完 AVD 模拟器后, 返回到 AVD 管理器界面, 我们刚才创建的 AVD 模拟器 AVD4.4 已经在 AVD 设备界面列表中如图 1-22 所示 图 1-22 启动模拟器 选中模拟器, 单击 Start 按钮, 弹出 Launch Options 界面, 单击 Launch 按 钮, 运行我们创建的 AVD4.4 模拟器, 启动后的模拟器如图 1-23 所示

26 图 1-23 Android 模拟器界面 注意 : 安装过程中, 需要选择目录时, 所有的目录最好都不要包含中文和空格, 以避免带来一些不必要的麻烦 1.3 开发第一个 Android 应用 前面所有的准备工作都完成后, 我们现在通过一个简单的例子, 来测试 Android 开发环境是否搭建成功, 同时熟悉开发 Android 应用程序的一般步骤 创建 Android 项目 Android 文件夹 Android 应用程序 图 1-24 创建一个 Android 项目 13

27 (1) 启动 Eclipse, 选择 File New Other... 菜单项, 或者单击工具栏中的按钮, 弹出新建工程对话框, 如图 1-24 所示 (2) 选择 Android Application Project 创建一个 Android 项目 Eclipse 弹出如图 1-25 所示的窗口, 填好参数后, 单击 Next 14 图 1-25 创建 Android 项目图 (3) 进入 Configure Launcher Icon 配置应用程序图标, 如图 1-26 所示 (4) 单击 Next 按钮, 显示创建 Activity 面板, 选择 ( 默认选项 ), 单击 Next 按钮, 显示创建空 Activity 面板如图 1-27 所示, 最后单击 finish 按钮完成项目的创建 项目创建后会在左边生成一个 HelloAndroid 文件夹 选择应用图标 设置图标背景色 设置图标前景色 图 1-26 配置应用程序图标

28 1.3.2 运行 Android 应用 Activity 名称 Activity 的布局文件 Activity 的标题 图 1-27 主 Activity 的配置参数 右键点击 HelloAndroid 项目, 选择 Run As Android Application, 如果此时没有连接任何设备, 会启动一个模拟器, 如果没有创建模拟器, 会提示没有任何可运行的设备, 并提醒是否要创建一个 ( 如图 1-28 所示 ) 第一次启动时间会比较长, 需耐心等待 启动完成后,Android 会自动运行程序, 运行结果如图 1-29 所示 1.4 Android 应用结构分析 图 1-28 提示没有可运行的设备 前面我们只是根据向导创建了一个 Android 项目, 并未编写任何代码, 运行后却能显示 HelloWord! 字符串, 并且有标题和图标,Eclipse 究竟为我们做了些什么? Android 程序又是如何运行的? 为什么会得到这样的结果? 本节将详细介绍 Android 程序的执行过程 15

29 1.4.1 Android 应用程序的结构 16 图 1-29 Android 程序运行后的界面 细心的同学可能会发现, 创建一个 Android 项目后, 会在 Eclipse 的左边的 Package Explorer 视图下生成一个以 HelloAndroid 为根的文件夹结构 ( 见图 1-30), 其中 : (1) gen 目录中存放 ADT 自动生成的文件, 该目录中最主要的就是 R.java 文件 (2)Android 开发工具会根据 res 目录中的 xml 文件 图片等资源, 同步更新 R.java (3) R.java 在应用中起着字典的作用, 它包含各种资源的引用, 通过 R.java 系统可以很方便地找到对应资源 (4) 编译器会根据 R.java 文件, 检查资源是否被使用, 没有使用的资源将不会打包到安装文件中, 减少应用所占空间大小 res 文件夹下用于存放各种资源文件, 主要包含的类型如表 1-4 所示 表 1-4 res 文件夹下各目录的作用 目录结构 资源类型 备注 res/anim/ XML 动画文件 默认不存在 anim 文件夹, 需要手动添加 res/drawable/ 一些图形 图像文件 res/layout XML 布局文件 res/values/ 各种 XML 资源文件 可手动添加这些文件, 文件名没有特殊要求 arrays.xml:xml 数组文件 colors.xml:xml 颜色文件 dimen.xml:xml 尺寸文件 styles.xml:xml 样式文件 res/xml/ 任意的 XML 文件 需手动添加 xml 文件夹 res/raw/ 直接复制到设备中的原生文件 默认不包含 raw 文件夹, 需手动添加 res/menu/ XML 菜单资源文件

30 图 1-30 HelloAndroid 项目目录结构 Android 应用程序运行过程 主程序, 界面中启动 的第一个程序 定义屏幕中显示内容 的布局文件 我们知道 HelloAndroid 项目的各个文件的作用后, 那么这些文件又是如何协同工 作, 最后得到运行效果的呢? 下面我们来探究 Android 应用程序的运行过程 当我们运行程序时, 系统首先会读取 AndroidManifest.xml 清单文件, 内容如下 1 <manifest xmlns:android= 命名空间 2 package="iet.jxufe.cn.android" 应用程序包名 3 android:versioncode="1" 版本号 4 android:versionname="1.0" > 版本名 5 <uses-sdk 6 android:minsdkversion="8" 使用的 SDK 最低版本 7 android:targetsdkversion="15" /> 目标版本 8 <application 9 android:icon="@drawable/ic_launcher" 应用程序的图标 10 android:label="@string/app_name" 应用程序标签 11 android:theme="@style/apptheme" > 主题样式 12 <activity 17

31 13 android:name=".mainactivity" Activity 对应的类名 14 > Activity 的标签名 15 <intent-filter> 启动的过滤条件 16 <action android:name="android.intent.action.main" /> 主活动的 activity 17 <category android:name="android.intent.category.launcher" /> 18 </intent-filter> 19 </activity> 20 </application> 21 </manifest> 其中命名空间所对应的文件中, 定义了该 XML 文件中各种标签及属性, 必不可 少, 否则系统无法解析这些标签资源 应用程序的图标指的是安装该应用程序后, 显示在手机功能菜单上的启动该应用的 图标 ( 见图 1-31 中第二行的 MainActivity 及其上面的图标 ) 它的值由属性 android:icon 规定, 表示引用 R.java 文件中的 资源,drawable 是 R 类中的一个内部类,ic_launcher 是该内部类下的一个静态成员, 它所 对应的资源存放在 res/drawable 下 通过更改该值, 可以更改应用程序的图标 Application 标签下的 android:label 属性对应的值 "@string/app_name", 表示 R 类中 string 内部类中 app_name 成员变量所对应的资源的值 具体是指 strings.xml 文件中, name 属性值为 app_name 的标签所对应的内容, 我们查看 strings.xml 文件的内容如 下, 在此其值为 :HelloAndroid 1 <resources> 2 <string name="app_name">helloandroid</string> 3 <string name="hello_world">hello world!</string> 4 <string name="menu_settings">settings</string> 5 <string name="title_activity_main">mainactivity</string> 6 </resources> 那么这个标签有什么作用或显示在哪里呢? 打开系统菜单, 选择管理应用, 会显示 本机上所有已安装的应用程序, 在这里就会显示我们所设置的应用标签 ( 见图 1-32 应用 中的 HelloAndroid) <activity> 元素是应用程序的关键部分,Activity 为用户提供了一个执行操作的可视 化用户界面 需要指定 Activity 所对应的类名, 以及过滤条件 每个应用程序默认会有 一个主 Activity, 即过滤条件为如下代码所示的 Activity 详细的指定办法见第 5 章 1 <intent-filter> 2 <action android:name="android.intent.action.main" /> 3 <category android:name="android.intent.category.launcher" /> 4 </intent-filter> 系统找到主 Activity 后, 通过反射机制, 自动创建该 Activity 所对应的类的实例 在此 为 MainActivity 查看 MainActivity 的代码如下 18

32 标签 图标 图 1-31 应用图标显示的位置 图 1-32 应用标签显示的位置 1 public class MainActivity extends Activity { 2 public void oncreate(bundle savedinstancestate) { 3 super.oncreate(savedinstancestate); 调用父类的该方法 4 setcontentview(r.layout.activity_main); 设置 Activity 对应的界面 5 } 布局文件 6 public boolean oncreateoptionsmenu(menu menu) { 创建选项菜单 7 getmenuinflater().inflate(r.menu.activity_main, menu); 指定菜单资源 8 return true; 9 } 10 } 创建 MainActivity 对象后, 会自动回调该类的 OnCreate() 方法, 在 oncreate() 方法 中, 设置了界面布局文件为 R.layout.activity_main 所对应的文件即 activity_main.xml 文 件 查看该文件内容如下 1 <RelativeLayout 相对布局 2 xmlns:android=" xml 对应的命名空间 3 xmlns:tools=" 4 android:layout_width="match_parent" 宽度为整个屏幕 5 android:layout_height="match_parent" > 高度为整个屏幕 6 <TextView 文本显示框 7 android:layout_width="wrap_content" 宽度为内容包裹 8 android:layout_height="wrap_content" 高度为内容包裹 9 android:layout_centerhorizontal="true" 水平居中 10 android:layout_centervertical="true" 垂直居中 11 android:text="@string/hello_world" 文本框显示的文字 12 tools:context=".mainactivity" /> 所属上下文 13 </RelativeLayout> 整个界面中只有一个文本显示框, 该文本显示框垂直居中并水平居中, 该文本框的 查看 strings.xml 文件, 对应的值为 : Hello world! 因 19

33 此, 将按 layout 文件显示 Hello world! 至此, 我们终于得到了运行结果 综上所述,android 应用程序的运行过程大致如下 : 首先读取 AndroidManifest.xml 清单文件, 根据配置找到默认启动的类 MainActivity 并创建该类对象, 系统自动调用 MainActivity 的 oncreate() 方法, 该方法中设置用户界面为 activity_main.xml 布局文件, 该文件中有一个文本显示控件, 该控件居中显示在布局上, 其显示的信息是 string.xml 文件中定义的 hello_world 所对应的值, 即为 Hello World! Android 应用下载与安装 可运行的 android 程序的文件后缀名为.apk 可以是我们自己开发的, 也可以是网络下载的 在图 1-39 的 bin 文件夹下, 只要编译成功就会生成对应的可运行程序 Android 的模拟器与我们的 Android 手机功能类似, 可以从网上下载一些 Android 应用, 然后安装到我们的模拟器上 主要是通过 Android 为我们提供的 adb 命令来完成的 例如我们在 D:\android 目录下存放一个 Android 应用 abc.apk 打开命令行, 进入到该目录, 然后输入 adb install abc.apk, 如图 1-33 所示 在真实手机上运行自己开发程序的方法 : 在 Eclipse 中运行自己的 Android 应用时, Eclipse 会自动为我们生成对应的 apk 文件, 并存放在 bin 文件夹下 ( 见图 1-30) 只需要将 apk 拷贝到自己手机后直接安装, 就可以在自己的手机上运行自己开发的应用 20 (a) 若你没有启动模拟器也没有连接你的手机, 则会提示 device not found 错误, 否则开始安装应用 (b) 若你的模拟器上已有该应用, 则会提示 :INSTALL_FAILED_ALREADY_ EXISTS 失败信息, 可先卸载再安装 (c) 命令行中出现 Success 时, 表示该应用安装成功, 可以在功能菜单中找到相应的应用图标, 并启动它 图 1-33 在模拟器上安装 Android 应用 Android 四大基本组件介绍 Activity: 在 Android 应用中负责与用户进行交互的组件, 我们称之为 活动, 一个 Activity 就是一个屏幕 每一个 Activity 都被实现为一个独立的类, 并且从

34 活动基类中继承而来, 活动类将会显示由视图控件组成的用户接口, 并对事件作出响应 Android 应用需要多个用户界面, 将会包含多个 Activity, 多个 Activity 组成了 Activity 栈, 当前活动的 Activity 位于栈顶 Service: 它也代表一个单独的 Android 组件,Service 与 Activity 的区别在于 : Service 通常位于后台运行, 一般不需要与用户交互, 一些 Service 组件没有图形用户界面 同样,Service 组件需要继承 Service 基类 一个 Service 被运行起来之后, 它将拥有自己独立的生命周期,Service 组件通常用于为其他组件提供后台服务或监控其他组件的运行状态 BroadcastReceiver: 代表广播消息接收器, 非常类似于事件编程中的监听器, 所监听的事件源是 Android 应用中的其他组件 使用 BroadcastReceiver 组件接收广播消息, 需要实现 BroadcastReceiver 子类, 并重写 onreceive(context context,intent intent) 方法 ContentProvider: 提供一种跨应用的数据交换的标准 当应用程序继承 ContentProvider 类, 并重写该类用于提供数据和存储数据的方法, 就可以将自己的数据向其他应用程序共享 Android 设计之 MVC 模式 Android 程序开发采用了当前比较流行的 MVC 模式, 即 (Model-View-Controller):M 指模型层 V 指视图层 C 是控制层 MVC 模式实现了应用程序的模型层与视图层代码分离, 使得同一程序可以有不同的表现形式, 而控制层则用于确定模型层与视图层之间的关系, 使得数据一致 MVC 把应用程序的模型层与视图层完全分开, 最大的好处就是分工明确, 界面设计人员可以直接参与到界面开发, 程序员则可以把精力放在业务逻辑上 而不用像以前那样, 设计人员把所有的材料交给开发人员, 开发人员除业务逻辑外还要设计实现界面 在 Android 中 MVC 各部分对应的关系如下 (1) 视图层 (View): 在 Android 中, 所有的界面控件都继承于 View 类, 每个界面都是由很多个 View 对象组合而成 在 Android 中, 为每个 View 类定义了相应的 XML 标签, 并为 XML 标签定义了各种属性, 这些 XML 标签通常在 XML 布局文件中定义 因此, 可以用 xml 文件简单而快速地设计界面, 不懂代码的美工也可以采用一些界面设计工具快速设计界面, 而不用理会复杂的 java 代码, 较好地实现了分工 例如在 Eclipse 中布局文件的定义, 提供了源代码和图形化界面两种形式 ( 如图 1-34 所示 ) 左边是图形化界面设计窗口, 提供了各种图形化界面, 设计者只需将自己需要的控件拖到视图窗口中即可, 右边是对应的 xml 源代码文件, 二者是一一对应的关系, 任何一方的改变都会影响另一方 注意 : 两个视图之间的转换只要单击左下角的 Graphical Layout( 图形化视图标签 ) 或 activity_main.xml( 源代码文件标签 ), 就可以从一种模式转到另一个 (2) 控制层 (Controller):Android 中控制层的重任通常是由 Acitvity 和 Intent 来实现的, 一个 Activity 可以有多种界面, 通过 setcontentview() 方法指定以哪个视图模型显示数据 这也提醒我们不要在 Acitivity 中写过多的业务处理代码, 要通过 Activity 交给 Model 业务逻辑层处理, 这样做的另外一个原因是 Android 中的 Acitivity 的响应时间是 21

35 5s, 如果耗时的操作放在这里, 程序就很容易被回收掉 (3) 模型层 (Model): 主要处理数据库 网络以及对业务计算等操作, 模型层主要采用 Java 程序来实现 1.5 本章小结 22 图形化视图标签 图形化视图 图 1-34 同一个不界面两种不同的表现形式 界面对应的源代码 源代码文件标签 本章介绍了 Android 应用开发的基础知识, 包括什么是 Android Android 的体系结构 Android 环境搭建 Android 程序运行与下载安装 Android 程序结构分析以及 Android 程序设计之 MVC 模式 通过本章的学习, 读者应重点掌握 Android 的环境搭建, 包括 Java 环境变量的配置 Eclipse 上 ADT 插件的安装 Android SDK 的配置等, 并熟悉 Android 应用的创建 运行方式 ; 了解 Android 应用程序中各文件夹的作用以及 Android 程序的运行过程 能够独立的搭建 Android 开发环境并描述 Android 程序的运行过程 课后练习 1.Android 的四大基本组件是 2. 搭建 Android 开发环境必需的工具是 3.Android 系统的底层建立在什么操作系统之上 ( ) A)Java B)Unix C)Windows D)Linux 4.Android 系统中安装的应用软件是什么格式的 ( ) A)exe B)java C)apk D)jar 5.Android 中启动 Android SDK 和 AVD 管理器的命令是 ( ) A) adb B)aidl C)android D)emulator 6.Android 中启动模拟机 (Android Virtual Device) 的命令是 ( )

36 A)adb B)android C)avd D)emulator 7.Android 中完成模拟器文件与电脑文件的相互复制以及安装应用程序的命令是 ( ) A)adb B)android C)avd D)emulator 8.Android 项目工程下面的 assets 目录的作用是什么 ( ) A) 放置应用到的图片资源 B) 主要放置一些文件资源, 这些资源会被原封不动打包到 apk 里面 C) 放置字符串, 颜色, 数组等常量数据 D) 放置一些与 UI 相应的布局文件, 都是 xml 文件 9. 关于 res/raw 目录说法正确的是 ( ) A) 该目录下的文件将原封不动的存储到设备上, 不会转换为二进制的格式 B) 该目录下的文件将原封不动的存储到设备上, 会转换为二进制的格式 C) 该目录下的文件最终以二进制的格式存储到指定的包中 D) 该目录下的文件最终不会以二进制的格式存储到指定的包中 10. 当我们创建一个 Android 项目时, 该项目的图标是在哪个文件中设置的 ( ) A)AndroidManifest.xml B)string.xml C)main.xml D)project.properties 11. 在第一个 Android 项目的 AndroidManifest.xml 文件中,<Application> 标签内的 android:label 对应的属性值是什么? 该值会显示在模拟器的哪个位置? 12. 请简要描述 HelloAndroid 程序的执行过程 13.res 目录下各文件夹与 R.java 中的类与成员变量之间有什么关系? 14. 创建一个 Android 项目, 该项目的应用名称为 Name, 包名为 :com.text.book, 为它设置一个自定义的图标, 并实现下面的 Android 运行结果 ( 注意标题文字和中间显示文字的变化 ) 23

37 本章要点 24 第 2 章 Android 界面编程基础 View 与 ViewGroup 的理解 文本显示框的功能和用法 文本编辑框的常用属性 按钮的简单用法 线性布局的功能和用法 表格布局的功能和用法 相对布局的功能和用法 布局的嵌套使用 开发自定义 View 的方法和步骤本章知识结构图 本章示例 View 文本显示框 (TextView) ViewGroup 自定义组件 按钮 (Button) 文本编辑框 (EditText) 线性布局 表格布局 相对布局 其他布局 层布局 绝对布局

38 第一章中, 我们通过一个简单的程序熟悉了 Android 应用程序的运行过程 Android 程序开发主要分为三部分 : 界面设计 代码流程控制和资源建设 代码和资源主要是由开发者进行编写和维护的, 对于大部分用户来说是不关心的, 展现在用户面前最直观的就是界面设计 作为一个程序设计者, 必须首先考虑用户的体验, 只有用户满意了你开发的产品, 应用才能推广, 才有价值, 因此界面设计尤为重要 Android 系统中为我们提供了丰富的界面组件, 开发者熟悉这些组件的功能和用法后, 只需要直接调用就可以设计出优秀的图形用户界面 除此之外,Android 系统还允许用户开发自定义的组件, 在系统的功能组件基础之上设计出符合自己要求的个性化组件 本章将详细讲解 Android 中的一些最基本的组件以及简单的布局管理 通过本章的学习, 读者应该能开发出简单的图形用户界面 2.1 基础 View 组件简介 Android 中所有的组件都继承于 View 类,View 类代表的就是屏幕上的一块空白的矩形区域, 该空白区域可用于绘画和事件处理 不同的界面组件, 相当于是对这个矩形区域做了一些处理, 例如文本显示框 按钮等 View 类有一个重要的子类 :ViewGroup ViewGroup 类是所有布局类和容器组件的基类, 它是一个不可见的容器, 它里面还可以添加 View 组件或 ViewGroup 组件, 主要用于定义它所包含的组件的排列方式, 例如网格排列或线性排列等 通过 View 和 ViewGroup 的组合使用, 从而使得整个界面呈现一种层次结构 ViewGroup 内包含的组件如图 2-1 所示 图 2-1 ViewGroup 组件的层次结构 Android 中控制组件的显示有两种方式 : 一种是通过 XML 布局文件来设置组件的属 性进行控制 ; 另一种是通过 Java 代码调用相应的方法进行控制 这两种方式控制 Android 界面显示的效果是完全一样的 实际上,XML 文件的属性与 Java 代码中方法之间存在着一一对应的关系 从 Android API 文档中 View 类的介绍中, 可查看所有的属性与方法之间的对应关系, 在此只列出一些常用的属性供参考 25

39 26 表 2-1 View 类的常见 XML 属性 对应方法及说明 XML 属性 对应方法 说明 android:alpha setalpha(float) 设置组件的透明度 android:background setbackgroundresource(int) 设置组件的背景 android:clickable setclickable(boolean) 设置组件是否可以触发点击事件 android:focusable setfocusable(boolean) 设置组件是否可以得到焦点 android:id setid(int) 设置组件的唯一 ID android:minheight setminimumheight(int) 设置组件的最小高度 android:minwidth setminimumwidth(int) 设置组件的最小宽度 android:padding setpadding(int,int,int,int) 在组件四边设置边距 android:scalex setscalex(float) 设置组件在 X 轴方向的缩放 android:visibility setvisibility(int) 设置组件是否可见 几乎每一个界面组件都需要设置 android:layout_height android:layout_width 这两个 属性, 用于指定该组件的高度和宽度, 主要有以下三种取值 fill_parent: 表示组件的高或宽与其父容器的高或宽相同 wrap_content: 表示组件的高或宽恰好能包裹内容, 随着内容的变化而变化 match_parent: 该属性值与 fill_parent 完全相同,Android2.2 之后推荐使用 match_parent 代替 fill_parent 虽然两种方式都可以控制界面的显示, 但是它们又各有优缺点 ( 1) 完全使用 Java 代码来控制用户界面不仅繁琐而且界面和代码相混合, 不利于解耦 分工 ;(2) 完全使 用 XML 布局文件虽然方便 便捷, 但灵活性不好, 不能动态改变属性值 因此, 我们经常会混合使用这两种方式来控制界面, 一般来说, 习惯将一些变化小 的 比较固定的 初始化的属性放在 XML 文件中管理, 而对于那些需要动态变化的属 性则交给 Java 代码控制 例如可以在 XML 布局文件中设置文本显示框的高度和宽度以 及初始时的显示文字, 在代码中根据实际需要动态的改变显示的文字 文本显示框 TextView TextView 类直接继承于 View 类, 主要用于在界面上显示文本信息, 类似于一个文本显示器, 从这个方面来理解, 有点类似于 Java 编程中的 JLable 的用法, 但是比 JLable 的功能更加强大, 使用更加方便 TextView 可以设置显示文本的字体大小 颜色 风格等属性,TextView 的常见属性如表 2-2 所示 表 2-2 TextView 类的常见 XML 属性 对应方法及说明 XML 属性 对应方法 说明 android:gravity setgravity(int) 设置文本的对齐方式 android:height setheight(int) 设置文本框的高度 (pixel 为单位 ) android:text settext(charsequence) 设置文本的内容 android:textcolor settextcolor(int) 设置文本的颜色 android:textsize settextsize(int,float) 设置文本的大小 android:textstyle settypeface(typeface) 设置文本的风格 android:typeface settypeface(typeface) 设置文本的字体 android:width setwidth(int) 设置文本框的宽度 (pixel 为单位 )

40 这些是文本显示控件都具有的功能 除此之外,Android 中的 TextView 还具有自动识别文本中的各种链接 能够显示字符串中的 Html 标签的格式 识别自动链接的属性为 :android:autolink, 该属性的值有 none: 不匹配任何格式, 这是默认值 web: 只匹配网页, 如果文本中有网页, 网页会以超链接的形式显示 只匹配电子邮箱, 电子邮箱会以超链接的形式显示 phone: 只匹配电话号码 ; 电话号码会以超链接的形式显示 map: 只匹配地图地址 all: 匹配以上所有 当匹配时, 相应部分会以超链接显示, 单击超链接, 会自动的运行相关程序 例如电话号码超链接, 会调用拨号程序, 网页超链接会打开网页等 而显示 Html 标签格式, 则需要通过 java 代码来控制了 首先为该文本框添加一个 id 属性, 然后在 oncreate() 方法中, 通过 findviewbyid(r.id.***), 获取该文本框, 最后通过 settext() 方法来设置显示的内容 例如 : 1 TextView tv=((textview)findviewbyid(r.id.mytext); mytext 为 id 号 2 tv.settext(html.fromhtml ( 欢迎参加 <font color=blue> 手机软件设计赛 </font> )); 该代码的显示效果是 : 手机软件设计赛这几个字为蓝色, 其他字的颜色为布局文件中设置的颜色 文本编辑框 EditText TextView 的功能仅仅是用于显示信息而不能编辑, 好的应用程序往往需要与用户进行交互, 让用户进行输入信息 为此,Android 中提供了 EditText 组件,EditText 是 TextView 类的子类, 与 TextView 具有很多相似之处 它们最大的区别在于,EditText 允许用户编辑文本内容, 使用 EditText 时, 经常使用到的属性有 : android:hint: 设置当文本框内容为空时, 文本框内显示的提示信息, 一旦输入内容, 该提示信息立即消失, 当删除所有输入的内容时, 提示信息又会出现 android:password: 设置文本框是否为密码框, 值为 true 或者 false, 设置为 true 时, 输入的内容将会以点替代, 但已不推荐使用了 android:inputtype: 设置文本框接收值的类型, 例如只能是数字 电话号码等 按钮 Button Button 也是继承于 TextView, 功能非常单一, 就是在界面中生成一个按钮, 供用户 单击, 单击按钮后, 会触发一个单击事件, 开发人员针对该单击事件可以设计相应的事件处理 ; 从而实现与用户交互的功能 我们可以设置按钮的大小 显示文字以及背景等 当我们想把一张图片作为按钮时, 有两种方法 : 一种是将该图片作为 Button 的背景图片 ; 另一种是使用 ImageButton 按钮, 将该图片作为 ImageButton 的 android:src 属性值即可 需注意的是 :ImageButton 按钮不能指定 android:text 属性, 即使指定了, 也不会 27

41 显示任何文字 下面我们以一个简单的例子, 介绍这三种简单组件一些属性的用法 程序运行效果如图 2-2 所示 在此界面中包含两个 TextView 两个 EditText 两个 Button, 界面布局文件如下 28 图 2-2 程序运行效果图 程序清单 :codes\02\textviewtest\res\layout\activity_main.xml 1 <LinearLayout xmlns:android=" 2 xmlns:tools=" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:gravity="center_horizontal" 线性布局内组件的对齐方式 : 水平居中 6 android:orientation="vertical" > 线性布局方向为垂直 7 <TextView 8 android:id="@+id/html" 为 TextView 添加 id 属性 9 android:layout_width="wrap_content" 组件宽度为内容包裹 10 android:layout_height="wrap_content" 组件高度为内容包裹 11 android:textsize="20sp" /> 设置文本大小为 20 像素 12 <EditText 13 android:layout_width="match_parent" 组件宽度为填充父容器 14 android:layout_height="wrap_content" 组件高度为内容包裹 15 android:hint="@string/name"/> 设置文本编辑框的提示信息 16 <EditText 17 android:layout_width="match_parent" 18 android:layout_height="wrap_content" 19 android:inputtype="textpassword" 设置文本编辑框的输入类型为密码 20 android:hint="@string/psd"/> 设置文本编辑框的提示信息 21 <Button 22 android:layout_width="wrap_content" 23 android:layout_height="wrap_content" 24 android:text="@string/login"/> 设置按钮的显示文本

42 25 <Button 26 android:layout_width="wrap_content" 27 android:layout_height="wrap_content" 28 设置按钮的显示文本 29 <TextView 30 android:layout_width="wrap_content" 31 android:layout_height="wrap_content" 32 android:textsize="18sp" 设置文本字体大小为 18 像素 33 android:textcolor="#0000ff" 设置文本颜色为蓝色 34 android:autolink="all" 自动识别所有链接 35 设置显示的文本 36 </LinearLayout> 作为 android:text 的属性值, 表示引用 R.java 中 string 内部类的 *** 成员变量所代表的资源 这些常量值是在 strings.xml 文件中定义的 查看 strings.xml 文件的内容如下 程序清单 :codes\02\textviewtest\res\values\strings.xml 1 <resources> 2 <string name="app_name">textview</string> 3 <string name="hello_world">hello world!</string> 4 <string name="menu_settings">settings</string> 5 <string name="title_activity_main"> 竞赛登录 </string> <string name="test"> 如有疑问请联系我们 \n 联系电话 : \n 6 iet2011@163.com\n 网址 : 7 <string name="name"> 请输入用户名 </string> 8 <string name="psd"> 请输入密码 </string> 9 <string name="login"> 登录 </string> 10 <string name="register"> 注册 </string> 11 </resources> 其实在设置 android:text 属性时, 可以直接将这些字符串常量赋值给该属性, 但是建 议不要这么做 因为一些字符串常量可能会在多处被使用, 如果都在属性里写, 不仅占 用更多的内存, 而且修改起来也比较麻烦, 需要一个个进行修改 ; 另一方面, 统一放在 strings.xml 文件中, 还有利于以后软件语言的国际化 针对不同的语言, 写一个相应的 资源文件就可以了, 而不用去更改别的文件, 可扩展性比较好 由于本程序中, 还涉及 html 格式标签的使用, 因此需要在 Java 代码中进行简单设 置, 首先通过 findviewbyid() 方法获取文本组件, 然后进行设置显示文本 该过程调用 了 Html 类的静态方法 fromhtml(), 代码如下 1 public void oncreate(bundle savedinstancestate) { 2 super.oncreate(savedinstancestate); 3 setcontentview(r.layout.activity_main); 4 TextView html=(textview )findviewbyid(r.id.html); 根据 id 获取文本组件 5 html.settext(html.fromhtml(" 欢迎参加 <font color=red>" + 6 " 手机软件设计赛 </font>")); 设置文本组件的显示文本 7 } 29

43 2.2 布局管理器 前面一节, 我们学习了几种简单的界面组件, 并通过一个简单的示例, 演示了几种组件的常用属性的基本用法, 但是程序的运行界面并不是很美观, 组件排列杂乱 本节我们将学习 Android 中为我们提供的几种管理界面组件的布局管理器 Android 中布局管理器本身也是一个界面组件, 所有的布局管理器都是 ViewGroup 类的子类, 都可以当做容器类来使用 因此, 可以在一个布局管理器中嵌套其他布局管理器 Android 中布局管理器可以根据运行平台来调整组件的大小, 具有良好的平台无关性 Android 中用得最多的布局主要有 : 线性布局 表格布局 相对布局 线性布局 线性布局是最常用也是最基础的布局方式 前面的示例中, 我们就使用到了线性布局, 它用 LinearLayout 类表示 线性布局和 Java 编程中 AWT 编程里 FlowLayout 有些相似, 它们都会将容器里的所有组件一个挨着一个排列 它提供了水平和垂直两种排列方向, 通过 android:orientation 属性进行设置, 默认为垂直排列 当为水平方向时, 不管组件的宽度是多少, 整个布局只占一行, 当组件宽度超过容器宽度时, 超出的部分将不会显示 当为垂直方向时, 整个布局文件只有一列, 每个组件占一行, 不管该组件宽度有多小 线性布局与 AWT 编程中 FlowLayout 的最明显的区别 : 在 FlowLayout 中组件一个个地排列到边界就会自动从下一行重新开始 ; 在线性布局中如果一行的宽度或一列的高度超过了容器的宽度或高度, 那么超出的部分将无法显示, 如果希望超出的部分能够滚动显示, 则需在外边包裹一个滚动组件,ScrollView( 垂直滚动 ) 或 HorizontalScrollView ( 水平滚动 ) 在线性布局中, 除了设置高度和宽度外, 主要设置如下两个属性 : android:gravity: 设置布局管理器内组件的对齐方式, 可以同时指定多种对齐方式的组合, 多个属性之间用竖线隔开, 但竖线前后不能出现空格 例如 bottom center_horizontal 代表出现在屏幕底部, 而且水平居中 android:orientation: 设置布局管理器内组件的排列方向, 可以设置为 vertical( 垂直排列 ) 或 horizontal( 水平排列 ) 表格布局 30 表格布局是指以行和列的形式来管理界面组件, 由 TableLayout 类表示, 不必明确声明包含几行几列, 而通过添加 TableRow 来添加行, 在 TableRow 中添加组件来添加列 TableRow 就是一个表格行, 本身也是容器, 可以不断地添加其他组件, 每添加一个组件就是在该行中增加一列, 如果直接向 TableLayout 中添加组件, 而没有添加

44 TableRow, 那么该组件将会占用一行 在表格布局中, 每列的宽度都是一样的, 列的宽度由该列中最宽的那个单元决定, 整个表格布局的宽度则取决于父容器的宽度, 默认总是占满父容器本身 TableLayout 继承了 LinearLayout, 因此它完全支持 LinearLayout 所支持的全部 XML 属性, 另外,TableLayout 还增加了自己所特有的属性 android:collapsecolumns: 隐藏指定的列, 其值为列所在的序号, 从 0 开始, 如果需要隐藏多列, 可用逗号隔开这些序号 android:shrinkcolumns: 收缩指定的列以适合屏幕, 使整行能够完全显示不会超出屏幕, 用于当某一行的内容超过屏幕的宽度时, 会使该列自动换行, 其值为列所在的序号 如果没有该属性, 则超出屏幕的部分会自动截取, 不会显示 android:stretchcolumns: 尽量把指定的列填充空白部分 该属性用于某一行的内容不足以填充整个屏幕, 这样指定某一列的内容扩张以填满整个屏幕, 其他列的宽度不变 如果某一列有多行, 而每行的列数可能不相同, 那么可扩展列的宽度是一致的, 不会因为某一行有多余的空白而填充整行 也就是说, 不管在哪一行, 它的宽度都是相同的 android:layout_column: 控件在 TableRow 中所处的列 如果没有设置该属性, 默认情况下, 控件在一行中是一列挨着一列排列的 通过设置该属性, 可以指定控件所在的列, 这样就可以达到中间某一个列为空的效果 android:layout_span: 该控件所跨越的列数, 即将多列合并为一列 相对布局 表 2-3 相对布局中常用属性设置 属性 说明 android:layout_centerhorizontal 设置该组件是否位于父容器的水平居中位置 android:layout_centervertical 设置该组件是否位于父容器的垂直居中位置 android:layout_centerinparent 设置该组件是否位于父容器的正中央位置 android:layout_alignparenttop 设置该组件是否与父容器顶端对齐 android:layout_alignparentbottom 设置该组件是否与父容器底端对齐 android:layout_ alignparentleft 设置该组件是否与父容器左边对齐 android:layout_ alignparentright 设置该组件是否与父容器右边对齐 android:layout_torightof 指定该组件位于给定的 ID 组件的右侧 android:layout_toleftof 指定该组件位于给定的 ID 组件的左侧 android:layout_above 指定该组件位于给定的 ID 组件的上方 android:layout_below 指定该组件位于给定的 ID 组件的下方 android:layout_aligntop 指定该组件与给定的 ID 组件的上边界对齐 android:layout_ alignbottom 指定该组件与给定的 ID 组件的下边界对齐 android:layout_ alignleft 指定该组件与给定的 ID 组件的左边界对齐 android:layout_ alignright 指定该组件与给定的 ID 组件的右边界对齐 相对布局, 顾名思义就是相对于某个组件的位置, 由 RelativeLayout 类表示, 这种 布局的关键是找到一个合适的参照物, 如果甲组件的位置需要根据乙组件的位置来确定, 那么要求先定义乙组件, 再定义甲组件 31

45 在相对布局中, 每个组件的位置可通过它相对于某个组件的方位以及对齐方式来确定, 因此相对布局中常见的属性如表 2-3 所示 由于父容器是确定的, 所以与父容器方位与对齐的关系取值为 true 或 false 其他布局 除以上几种常用的布局方式外,Android 还提供了层布局 绝对布局 在此简介之 层布局也叫帧布局, 由 FrameLayout 类表示 其每个组件占据一层, 后面添加的层会覆盖前面的层, 后面的组件会叠放在先前的组件之上 如果后面组件的大小大于前面的组件, 那么前面的组件将会完全被覆盖, 不可见 ; 如果后面组件无法完全覆盖前面的组件, 则未覆盖部分, 显示先前的组件 这样看来, 层布局的显示效果有些类似于 Java 中 AWT 编程里的 CardLayout, 都是把组件一个接一个的叠在一起, 但 CardLayout 通过使用 first last previous 等能够看到所有的组件, 但是层布局没有这种功能 绝对布局, 即指定每个组件在手机上的具体坐标, 每个组件的位置和大小都是固定的 由于不同手机屏幕可能不同, 绝对布局只适合于固定的手机或屏幕, 不具有通用性, 现在已很少使用 布局的综合运用 通过上面对几种布局方式的介绍, 我们发现每种布局方式都有自己的优缺点, 在实际的开发中, 往往很难通过一种布局方式就能完成我们的界面设计, 需要多种布局方式的嵌套使用, 方能达到我们要求的效果 下面我们以一个简单的示例, 演示多种布局管理器的综合运用 该程序设计出一个通用计算器的界面, 程序运行效果如图 2-3 所示 32 图 2-3 计算器界面设计图 该界面中包含一个用于显示输入的数字和计算结果的文本编辑框和 28 个按钮 其中

46 两个按钮比较特别, 一个高度是普通按钮的两倍, 一个宽度是普通按钮的两倍 单独采 用某一种布局方式, 例如线性布局, 也可以达到该效果, 在线性布局中不断的嵌套线性 布局 在此, 我们根据各组件的特点, 综合运用多种布局 该界面整体采用垂直线性布 局, 先添加一个文本编辑框, 然后添加一个四行五列的表格布局, 最后再添加相对布 局, 摆放剩余的按钮 由于所有的按钮都需要设置高度 宽度 对齐方式 字体大小等属性, 在此我们定 义三种按钮样式, 分别对应于普通按钮, 较高的按钮以及较宽的按钮, 样式代码如下 程序清单 :codes\02\calculate\res\values\styles.xml 1 <resources xmlns:android=" 2 <style name="btn01"> 普通按钮的风格 3 <item name="android:layout_width">60dp</item> 设置按钮宽度 4 <item name="android:layout_height">50dp</item> 设置按钮高度 5 <item name="android:textsize">20sp</item> 设置按钮上字体大小 6 <item name="android:gravity">center_horizontal</item> 设置按钮文本对齐方式 7 </style> 8 <style name="btn02"> 较宽按钮的风格 9 <item name="android:layout_width">120dp</item> 按钮宽度为 120dp 10 <item name="android:layout_height">50dp</item> 按钮高度为 50dp 11 <item name="android:textsize">20sp</item> 按钮上字体大小为 20sp 12 <item name="android:gravity">center_horizontal</item> 按钮上文字水平居中 13 </style> 14 <style name="btn03"> 较高按钮的风格 15 <item name="android:layout_width">60dp</item> 按钮宽度为 60dp 16 <item name="android:layout_height">100dp</item> 按钮高度为 100dp 17 <item name="android:textsize">20sp</item> 按钮上字体大小为 20sp 18 <item name="android:gravity">center_horizontal</item> 按钮上文字水平居中 19 </style> 20 </resources> 每一种样式都以 <style> 标签开始, 样式中每一个属性值都用 <item> 标签表示, <item> 标签的 name 属性指定具体的属性,<item> 标签的内容为属性的值 引用时, 只需将组件的 style 样式名即可 首先整体采用线性布局, 代码如下 1 <LinearLayout xmlns:android=" 2 xmlns:tools=" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical"> 垂直线性布局 6 <EditText 文本编辑框 7 android:layout_width="match_parent" 8 android:layout_height="wrap_content" 9 android:minlines="2" /> 高度最少两行 10 <TableLayout.../ > 表格布局 11 <RelativeLayout.../> 相对布局 12 </LinearLayout> 33

47 34 表格布局中包含四行五列, 具体代码如下 1 <TableLayout 表格布局 2 android:layout_width="match_parent" 宽度填充父容器 3 android:layout_height="wrap_content" > 高度包裹内容 4 <TableRow 表格行 ( 第一行 ) 5 android:layout_width="match_parent" 宽度填充父容器 6 android:gravity="center_horizontal" > 内容水平居中对齐 7 <Button 插入一列 ( 第一列 ) 8 style="@style/btn01" 引用样式 btn01 9 android:text="mc" /> 按钮文字为 MC 10 <Button 插入一列 ( 第二列 ) 11 style="@style/btn01" 12 android:text="mr" /> 13 <Button 插入一列 ( 第三列 ) 14 style="@style/btn01" 15 android:text="ms" /> 16 <Button 插入一列 ( 第四列 ) 17 style="@style/btn01" 18 android:text="m+" /> 19 <Button 插入一列 ( 第五列 ) 20 style="@style/btn01" 21 android:text="m-" /> 22 </TableRow> 23 <TableRow 表格行 ( 第二行 ) 24 android:layout_width="match_parent" 25 android:gravity="center_horizontal" > 26 <Button 27 style="@style/btn01" 28 android:text=" " /> 29 <Button 30 style="@style/btn01" 31 android:text="ce" /> 32 <Button 33 style="@style/btn01" 34 android:text="c" /> 35 <Button 36 style="@style/btn01" 37 android:text="±" /> 38 <Button 39 style="@style/btn01" 40 android:text=" " /> 41 </TableRow> 42 <TableRow 表格行 ( 第三行 ) 43 android:layout_width="match_parent" 44 android:gravity="center_horizontal" > 45 <Button 46 style="@style/btn01" 47 android:text="7" />

48 48 <Button android:text="8" /> 51 <Button android:text="9" /> 54 <Button android:text="/" /> 57 <Button android:text="%" /> 60 </TableRow> 61 <TableRow 表格行 ( 第四行 ) 62 android:layout_width="match_parent" 63 android:gravity="center_horizontal" > 64 <Button 65 style="@style/btn01" 66 android:id="@+id/four" 添加 ID 属性 67 android:text="4" /> 68 <Button 69 style="@style/btn01" 70 android:text="5" /> 71 <Button 72 style="@style/btn01" 73 android:text="6" /> 74 <Button 75 style="@style/btn01" 76 android:text="*" /> 77 <Button 78 style="@style/btn01" 79 android:text="1/x" /> 80 </TableRow> 81 </TableLayout> 相对布局需要一个参照物 本例中, 按钮 2 以 1 按钮 (id 为 one, 见下面代码 第 6 行 ) 为参考, 与 1 顶端对齐 ( 见代码 14 行 ), 在 1 的右边 ( 见代码 15 行 ), 详见下面 代码 1 <RelativeLayout 相对布局 2 android:layout_width="match_parent" 3 android:layout_height="wrap_content" 4 android:gravity="center_horizontal" > 水平居中对齐 5 <Button 6 android:id="@+id/one" 添加 ID 属性, 供其他组件参考 7 style="@style/btn01" 8 android:text="1" 按钮文字 1 9 android:textcolor="#0000ff" 按钮文字为蓝色, #RRGGBB 10 android:textstyle="bold" /> 按钮文字为粗体字 35

49 36 11 <Button android:text="2" /> 17 <Button android:text="3" /> 23 <Button android:text="-" /> 29 <Button android:gravity="center" android:text="=" /> 36 <Button android:text="+" /> 42 <Button android:text="." /> 48 <Button android:text="0" /> 53 </RelativeLayout> 本界面设计中最关键的就是两个特殊按钮的摆放, 对于表格布局而言, 每列的宽度是一致的, 并且每一行中, 各列的高度也是相同的 而这两个按钮, 一个过高, 一个过宽, 因此采用表格布局不好处理这两个按钮, 而对于线性布局而言, 要么处于同一行, 要么处于同一列, 对于占多行或多列的组件需组合使用水平线性布局和垂直线性布局, 比较麻烦, 在此采用相对布局来处理

50 2.3 开发自定义 View Android 中所有的界面组件都是继承于 View 类,View 本身仅仅是一块空白的矩形区域, 不同的界面组件在这个矩形区域上绘制外观即可形成风格迥异的组件, 基于这个原理, 开发者完全可以通过继承 View 类来创建具有自己风格的组件 开发自定义 View 时的一般步骤如下 : 1) 定义自己组件的类名, 并让该类继承 View 类或一个现有的 View 的子类 2) 重写父类的一些方法, 通常需要提供一个构造器, 构造器是创建自定义组件的最基本方式, 当 Java 代码创建该组件或根据 XML 布局文件加载并构建界面时都将调用该构造器, 根据业务需要重写父类的部分方法 例如 ondraw() 方法, 用于实现界面显示, 其他方法还有 onsizechanged() onkeydown() onkeyup() 等 3) 使用自定义的组件, 既可以通过 Java 代码来创建, 也可以通过 XML 布局文件进行创建, 在 XML 布局文件中, 该组件的标签是完整的包名 + 类名, 而不再仅仅是原来的类名 例如我们自定义一个圆形组件 程序运行效果如图 2-4 所示 图 2-4 自定义组件 1 public class MyView extends View { 定义自定义组件类 2 public MyView(Context context, AttributeSet attrs) { 构造方法, 调用父类构造方法 3 super(context, attrs); 4 } 5 protected void ondraw(canvas canvas) { 重写父类的 ondraw() 方法 6 Paint paint = new Paint(); 创建一个画笔 7 paint.setcolor(color.blue); 设置画笔颜色 蓝色 8 canvas.drawcircle(50, 50, 50, paint); 画一个圆, 半径为 50 9 } 10 } 通过 XML 布局文件来使用该组件, 代码如下 1 <iet.jxufe.cn.android.myview 完整的包名 + 类名 2 android:layout_width="wrap_content" 3 android:layout_height="wrap_content"/> 37

51 2.4 本章小结 本章主要讲解了 Android 中界面组件的基本知识,Android 中所有的界面组件都继承于 View 类,View 类代表的是一块空白的矩形区域, 不同的组件在此区域中进行绘制从而形成了风格迥异的组件 View 类有一个重要的子类 :ViewGroup, 该类是所有布局类或容器类的基类, 在 ViewGroup 中可以包含 View 组件或 ViewGroup,ViewGroup 的这种嵌套功能从而形成了界面上组件的层次结构 除此之外, 我们详细介绍了几种最基本的界面组件的功能和常用属性, 包括文本显示框 文本编辑框和按钮等, 并通过 竞赛登录 ( 图 2-2) 示例演示了具体的用法 为了使这些组件排列美观, 我们继续学习了 Android 中几种常见的布局管理器, 包括线性布局 表格布局和相对布局, 它们各有优缺点, 线性布局方便, 需使用的属性较少, 但不够灵活 ; 表格布局中通过 TableRow 添加行, 每列的宽度一致 ; 相对布局则通过提供一个参照物来准确定义各个控件的具体位置, 通常我们在一个实例中会用到多种布局, 把各种布局结合起来达到我们所要的界面效果 本章最后通过一个综合的示例演示了如何综合运用多种布局设计一些比较复杂的界面 课后练习 1. 下列哪个属性可做 EditText 编辑框的提示信息 ( ) A)android:inputType B)android:text C)android:digits D)android:hint 2. 为下面控件添加 android:text= Hello 属性, 运行时无法显示文字的控件是 ( ) A)Button B)EditText C)ImageButton D)TextView 3. 下列选项中, 前后两个类不存在继承关系的是 ( ) A)TextView EditText B)TextView Button C)Button ImageButton D) ImageView ImageButton 4. 假设手机屏幕宽度为 400px, 现采取水平线性布局放置 5 个按钮, 设定每个按钮的宽 度为 100px, 那么该程序运行时, 界面显示效果为 ( ) A) 自动添加水平滚动条, 拖动滚动条可查看 5 个按钮 B) 只可以看到 4 个按钮, 超出屏幕宽度部分无法显示 C) 按钮宽度自动缩小, 可看到 5 个按钮 D) 程序运行出错, 无法显示 5. 表格布局中, 设置某一列是可扩展的正确的做法是 ( ) A) 设置 TableLayout 的属性 :android:stretchcolumns="x",x 表示列的序号 ; B) 设置 TableLayout 的属性 :android: shrinkcolumns="x",x 表示列的序号 ; C) 设置具体列的属性 :android:stretchable= true ; 38 D) 设置具体列的属性 :android: shrinkable= true ; 6. 相对布局中, 设置以下属性时, 属性值只能为 true 或 false 的是 ( ) A)android:layout_below B)android:layout_alignParentLeft C)android:layout_alignBottom D)android:layout_toRightOf 7. 布局文件中有一个按钮 (Button), 如果要让该按钮在其父容器中居中显示, 正确的

52 做法的设置是 ( ) A) 设置按钮的属性 :android:layout_gravity="center" B) 设置按钮的属性 :android:gravity="center" C) 设置按钮父容器的属性 :android:layout_gravity="center" D) 设置按钮父容器的属性 :android:gravity="center" 8.Android 中的水平线性布局不会自动换行, 当一行中组件的宽度超过了父容器的宽度时, 超出的部分将不会显示, 如果想以滚动条的形式显示超出的部分应该怎么做呢? 9. 运用表格布局设置 3 行 3 列的按钮, 要求 : 第一行中有一列空着, 第三列被拉伸 第三行中有一个按钮占两列, 运行效果如图所示 10. 根据所学的相对布局的知识, 设计出下图所示界面, 要求在文本编辑框内只能输入数字, 并且输入的内容会以 点 显示 11. 在 View 类的 XML 属性中 android:layout_gravity 和 android:gravity 都用于设置对齐方式, 它们之间有什么区别? 12. 学习了开发自定义 View 的知识, 试着编写一个自己的组件 13. 运用所学知识, 设计下图所示界面 要求 : 用户登录 这几个字大小为 28sp, 红色 登录 注册 找回密码 这几个按钮水平排列并且居中显示 39

53 本章要点 基于监听的事件处理模型 实现事件监听器的四种方式 基于回调的事件处理模型 事件传播 事件直接绑定到标签 Hanlder 消息传递机制 使用 Handler 动态生成随机数 AsyncTask 异步任务处理本章知识结构图 第 4 章事件处理 Android 事件处理机制 基于监听的事件处理 基于回调的事件处理 进阶 Android 消息传递机制 直接绑定到标签 Handler 消息传递 模型 步骤 实现方法 回调机制 事件传播 Handler 类 Message 类 消息传递步骤 AsyncTask 类 异步任务处理注意事项 72 获取事件源 注册监听器 实现监听器 内部类 匿名内部类 类自身 外部类 创建 Handler 类 子线程中调用 Handler 发送消息 调用 Handler 发送消息

54 前面两章我们学习了 Android 所提供的一些功能强大的界面组件, 这些组件主要是用来进行数据的显示, 如果用户想与之进行交互, 实现具体的功能, 则还需要相应事件处理的辅助 当用户在程序界面上执行各种操作时, 如单击一个按钮, 应用程序必须为用户动作提供响应动作, 这种响应动作就需要通过事件处理来完成 实际上, 我们在学习前面两章示例时, 已经使用到了 Android 的事件处理 Android 提供了三种事件处理方式 : 基于回调的事件处理 基于监听的事件处理和事件直接绑定到标签 熟悉传统图形界面编程的读者对于基于回调事件处理可能比较熟悉 ; 熟悉 Java AWT/Swing 开发方式的读者对于基于监听的事件处理可能比较熟悉 ; 对于熟悉 JavaScript 编程的读者对于直接绑定到标签的事件处理可能比较熟悉 Android 系统充分利用了三种事件处理的优点, 允许开发者采用自己熟悉的事件处理方式来为用户操作提供响应 在 Android 中, 用户界面属于主线程, 而子线程无法更新主线程的界面状态, 那么, 如何才能动态地显示用户界面呢? 本章我们将学习, 通过 Handler 消息传递来动态更新界面 如果在事件处理中需要做一些比较耗时的操作时, 直接放在主线程中将会阻塞程序的运行, 给用户以不好的体验, 甚至程序会没有响应或强制退出 本章我们将学习, 通过 AsyncTask 异步方式来处理耗时的操作 学完本章之后, 再结合前面所学知识, 读者将可以开发出界面友好 人机交互良好的 Android 应用 4.1 Android 的事件处理机制 不管是什么手机应用, 都离不开与用户的交互, 只有通过用户的操作, 才能知道用户的需求, 从而实现具体的业务功能, 因此, 应用中经常需要处理的就是用户的操作, 也就是需要为用户的操作提供响应, 这种为用户操作提供响应的机制就是事件处理 Android 提供了强大的事件处理机制, 包括三种事件处理机制 : (1) 基于监听的事件处理 : 主要做法就是为 Android 界面组件绑定特定的事件监听器, 在事件监听器的方法里编写事件处理代码, 前面我们已经见过大量这种事件处理的示例 (2) 基于回调的事件处理 : 主要做法就是重写 Android 组件特定的回调方法, 或者重写 Activity 的回调方法 Android 为绝大部分界面组件都提供了事件响应的回调方法, 我们只需重写它们即可, 由系统根据具体情景自动调用 (3) 直接绑定到标签 : 主要做法就是在界面布局文件中为指定标签设置事件属性, 属性值是一个方法的方法名, 然后再在 Activity 中定义该方法, 编写具体的事件处理代码 一般来说, 直接绑定到标签只适合于少数指定的事件, 实际应用中比较少见 ; 基于回调的事件处理代码比较简洁, 可用于处理一些具有通用性的系统为我们定义好的事件 但对于某些特定的事件, 无法使用基于回调的事件处理, 只能采用基于监听的事件处理 实际应用中, 基于监听的事件处理方法应用最广泛 73

55 4.1.1 基于监听的事件处理 Android 的基于监听的事件处理模型与 Java 的 AWT Swing 的处理方式几乎完全一样, 只是相应的事件监听器和事件处理方法名有所不同 在基于监听的事件处理模型中, 主要涉及三类对象 : EventSource( 事件源 ): 产生事件的组件即事件发生的源头, 如按钮 菜单等 ; Event( 事件 ): 具体某一操作的详细描述, 事件封装了该操作的相关信息, 如果程序需要获得事件源上所发生事件的相关信息, 一般通过 Event 对象来取得, 例如按键事件按下的是哪个键 触摸事件发生的位置等 ; EventListener( 事件监听器 ): 负责监听用户在事件源上的操作, 并对用户的各种操作做出相应的响应, 事件监听器中可包含多个事件处理器, 一个事件处理器实际上就是一个事件处理方法 那么在基于监听的事件处理中, 这三类对象又是如何协作的呢? 实际上, 基于监听的事件处理是一种委托式事件处理 普通组件 ( 事件源 ) 将整个事件处理委托给特定的对象 ( 事件监听器 ); 当该事件源发生指定的事情时, 系统自动生成事件对象, 并通知所委托的事件监听器, 由事件监听器相应的事件处理器来处理这个事件 具体的事件处理模型如图 4-1 所示 当用户在 Android 组件上进行操作时, 系统会自动生成事件对象, 并将这个事件对象以参数的形式传给注册到事件源上的事件监听器, 事件监听器调用相应的事件处理器来处理 触发事件源上事件 外部动作 事件源 3. 生成事件对象 事件 1. 将事件监听器注册到事件源 4. 触发事件监听器事件被作为参数传入事件处理器 事件监听器 事件处理器事件处理器 图 4-1 基于监听的事件处理模型 5. 调用事件处理器做出响应 委托式事件处理非常好理解, 就类似于生活中我们每个人能力都有限, 当碰到一些自己处理不了的事情时, 就委托给某个机构或公司来处理 你需要把你所遇到的事情和要求描述清楚, 这样, 其他人才能比较好地解决问题, 然后该机构会选派具体的员 工来处理这件事 其中, 我们自己就是事件源, 你遇到的事情就是事件, 该机构就是事件监听器, 具体解决事情的员工就是事件处理器 基于监听的事件处理模型的编程步骤主要有 : (1) 获取普通界面组件 ( 事件源 ), 也就是被监听的对象 ; (2) 实现事件监听器类, 该监听器类是一个特殊的 Java 类, 必须实现一个

56 XxxListerner 接口, 并实现接口里的所有方法, 每个方法用于处理一种事件 ; (3) 调用事件源的 setxxxlistener 方法将事件监听器对象注册给普通组件 ( 事件源 ), 即将事件源与事件监听器关联起来, 这样, 当事件发生时就可以自动调用相应的方法 在上述步骤中, 事件源比较容易获取, 一般就是界面组件, 根据 findviewbyid() 方法即可得到 ; 调用事件源的 setxxxlistener 方法是由系统定义好的, 我们只需要传入一个具体的事件监听器 ; 所以, 我们所要做的就是实现事件监听器 所谓事件监听器, 其实就是实现了特定接口的 Java 类的实例 在程序中实现事件监听器, 通常有如下几种形式 内部类形式 : 将事件监听器类定义为当前类的内部类 ; 外部类形式 : 将事件监听器类定义成一个外部类 ; 类自身作为事件监听器类 : 让 Activity 本身实现监听器接口, 并实现事件处理 方法 ; 匿名内部类形式 : 使用匿名内部类创建事件监听器对象 ; 下面以一个简单的程序来示范基于监听的事件处理模型的实现过程 该程序实现简单文本编辑功能, 程序界面布局中定义了一些文本显示框 若干个按钮, 以及一个文本编辑框 为所有的按钮注册了单击事件监听器, 为文本编辑框注册了编辑事件监听器, 为了演示各种实现事件监听器的方式, 该程序中使用了四种实现监听器的方式 界面分析与运行效果如图 4-2 所示 图 4-2 简单文本编辑器 整体采用垂直线性布局 水平线性布局 水平线性布局 水平线性布局 水平线性布局 内部类形式 外部类形式 类自身作为事件监听器 匿名内部类 界面布局文件见下页 在该布局文件中, 省略了一些类似的代码, 保留了整体结构, 整体采用垂直线性布 局, 里面又嵌套了若干个水平线性布局 界面设计完成后, 运行程序, 得到上述界面效果, 但此时单击按钮时没有任何反应, 下面为这些按钮添加事件监听器 75

57 76 程序清单 :codes\04\texteditor\res\layout\ activity_main.xml 1 <LinearLayout xmlns:android=" 2 xmlns:tools=" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 垂直线性布局 6 <TextView 7 android:id="@+id/testtext" 为文本框添加 ID, 便于查找 8 android:layout_width="match_parent" 文本框的宽度为填充父容器 9 android:layout_height="wrap_content" 文本框的高度为内容包裹 10 android:gravity="center_horizontal" 文本内容水平居中 11 android:text="@string/test_text" /> 设定文本显示内容 12 <LinearLayout 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:layout_marginleft="10dp" 左边距为 10dp 16 android:orientation="horizontal" > 水平线性布局 17 <TextView 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:text="@string/color" /> 21 <Button 22 android:id="@+id/red" 为按钮添加 ID 属性 23 android:layout_width="wrap_content" 24 android:layout_height="wrap_content" 25 android:text="@string/red" /> 26 <Button... /> 按钮属性与上面相似, 略 27 <Button... /> 按钮属性与上面相似, 略 28 </LinearLayout> 29 <LinearLayout>...</Linearlayout> 包含设置大小的按钮 30 <LinearLayout>...</Linearlayout> 包含设置样式的按钮 31 <LinearLayout>...</Linearlayout> 包含设置文本内容的编辑框 32 </LinearLayout> 首先为 红色 绿色 蓝色 三个按钮添加事件监听器, 这里采用内部类的形式实 现事件监听器, 关键代码如下 1 public class MainActivity extends Activity{ 2 private Button red, green, blue; 3 private TextView testtext; 4 public void oncreate(bundle savedinstancestate) { 5 super.oncreate(savedinstancestate); 6 setcontentview(r.layout.activity_main); 设置界面布局文件 7 testtext = (TextView) findviewbyid(r.id.testtext); 根据 ID 获取组件 8 red = (Button) findviewbyid(r.id.red); 根据 ID 获取组件 9 green = (Button) findviewbyid(r.id.green); 根据 ID 获取组件 10 blue = (Button) findviewbyid(r.id.blue); 根据 ID 获取组件 11 ColorListner mycolorlistner = new ColorListner(); 创建监听器对象 12 red.setonclicklistener(mycolorlistner); 注册监听器

58 13 green.setonclicklistener(mycolorlistner); 注册监听器 14 blue.setonclicklistener(mycolorlistner); 注册监听器 15 } 16 private class ColorListner implements OnClickListener { 实现监听器的内部 17 public void onclick(view v) { 类 18 switch (v.getid()) { 判断事件源 19 case R.id.red: 20 testtext.settextcolor(color.red); break; 将字体设置为红色 21 case R.id.blue: 22 testtext.settextcolor(color.blue); break; 将字体设置为蓝色 23 case R.id.green: 24 testtext.settextcolor(color.green); break; 将字体设置为绿色 25 default: break; 26 } 27 } 28 } 29 } 使用内部类作为事件监听器有两个优势 : (1) 使用内部类可以在当前类中复用该监听器类, 即多个事件源可以注册同一个监听器 ; (2) 使用内部类可以自由访问外部类的所有界面组件, 内部类实质上是外部类的成员 内部类形式比较适合于有多个事件源同时注册同一事件监听器的情形 下面我们为 增大 和 缩小 按钮添加事件监听器, 这里采用外部类的形式实现事件监听器, 关键代码如下 1 public class MainActivity extends Activity{ 2 private Button bigger,smaller; 3 public void oncreate(bundle savedinstancestate) { bigger = (Button) findviewbyid(r.id.bigger); 根据 ID 获取组件 6 smaller = (Button) findviewbyid(r.id.smaller); 根据 ID 获取组件 7 SizeListener mysizelistener=new SizeListener(testText); 创建监听器对象 8 bigger.setonclicklistener(mysizelistener); 注册监听器 9 smaller.setonclicklistener(mysizelistener); 注册监听器 10 } 11 } SizeListener 是一个外部类, 该类实现了 OnClickListener 接口, 可以处理单击事件, 但外部类是无法获取到 Activity 里的界面控件, 也就不能对控件进行设置和更新, 那么如何在该类中获取到需要改变的控件呢? 在这里采用通过构造方法传入的方式 SizeListener 的代码如下 77

59 78 程序清单 :codes\04\ TextEditor\src\iet\jxufe\cn\android\SizeListener.java 1 public class SizeListener implements OnClickListener { 2 private TextView tv; 3 public SizeListener(TextView tv) { 初始化需要传入的控件 4 this.tv = tv ; 5 } 6 public void onclick(view v) { 7 float f=tv.gettextsize(); 获取当前的字体大小 8 switch (v.getid()) { 判断是增大还是缩小 9 case R.id.bigger: 10 f=f+2; break; 字体每次增大 2 11 case R.id.smaller: 12 f=f-2; break; 字体每次减小 2 13 default: break; 14 } 15 if(f>=72){ f=72; } 判断字体是否大于 if(f<=8) { f=8; } 判断字体是否小于 8 17 tv.settextsize(f); 设置字体大小 18 } 19 } 使用外部类作为事件监听器类的形式较为少见, 主要有如下两个原因 : (1) 事件监听器通常属于特定的 GUI( 图形用户界面 ), 定义成外部类不利于提高程序的内聚性 ; (2) 外部类形式的事件监听器不能自由访问创建 GUI 界面中的组件, 编程不够简洁 但如果某个事件监听器确实需要被多个 GUI 界面所共享, 而且主要是完成某种业务逻辑的实现, 则可以考虑使用外部类的形式来定义事件监听器类 接着我们为 加粗 倾斜 默认 三个按钮添加事件处理器, 这里采用 Activity 类本身实现 OnClickListener 接口作为事件监听器, 代码如下 1 public class MainActivity extends Activity implements OnClickListener{ 2 private Button bold, italic,moren; 3 private int flag=0; 标志量, 默认为 0 4 public void oncreate(bundle savedinstancestate) { 5 6 testtext.settypeface(typeface.default); 设置字体样式 7 bold=(button)findviewbyid(r.id.bold); 根据 ID 获取组件 8 italic = (Button) findviewbyid(r.id.italic); 根据 ID 获取组件 9 moren=(button)findviewbyid(r.id.moren); 根据 ID 获取组件 10 italic.setonclicklistener(this); 注册监听器 11 bold.setonclicklistener(this); 注册监听器 12 moren.setonclicklistener(this); 注册监听器 13 } 14 public void onclick(view v) { 15 Typeface tf=testtext.gettypeface(); 获取当前字体样式

60 16 switch (v.getid()) { 判断哪个按钮被单击 17 case R.id.italic: 单击倾斜按钮 18 if(flag==2 flag==3){ 19 testtext.settypeface(typeface.monospace,typeface.bold_italic); 20 flag=3; 21 }else{ 22 testtext.settypeface(typeface.monospace, Typeface.ITALIC); 23 flag=1; 24 } break; 25 case R.id.bold: 单击加粗按钮 26 if(flag==1 flag==3){ 27 testtext.settypeface(typeface.monospace,typeface.bold_italic); 28 flag=3; 29 }else{ 30 testtext.settypeface(typeface.default_bold,typeface.bold); 31 flag=2; 32 } break; 33 case R.id.moren: 单击默认按钮 34 testtext.settypeface(typeface.monospace,typeface.normal); 35 flag=0; 36 break; 37 default: break; 38 } 39 } 40 } 由于 Activity 自身可以充当事件监听器, 因此为事件源注册监听器时, 只需要将当前对象传入即可, 而不用单独创建一个监听器对象 由于加粗和倾斜两种样式可以进行叠加, 因此, 需要有一个标志量来记录当前的样式,flag=0 表示当前没有任何样式, flag=1 表示当前为斜体,flag=2 表示当前为粗体,flag=3 表示当前为粗斜体 单击按钮时, 先判断当前样式, 然后再进行相应样式设置 Activity 类本身作为事件监听器, 就如同生活中, 我们自己刚好能够处理某一件事, 不需要委托给他人处理, 可以直接在 Activity 类中定义事件处理器方法, 这种形式非常简洁, 但这种做法有两个缺点 (1) 可能造成程序结构混乱,Activity 的主要职责应该是完成界面初始化工作, 但此时还需包含事件处理器方法, 从而引起混乱 ; (2) 如果 Activity 界面类需要实现监听器接口, 给人感觉比较怪异 思考 : 在上面的程序中, 单击事件监听器的具体事件处理器中, 并没有接收到事件参数, 即我们并没有发现事件的 踪迹, 这是为什么呢? 这是因为 Android 对事件 监听模型做了进一步简化 : 如果事件源触发的事件足够简单 事件里封装的信息比较有限, 那就无须封装事件对象 而对于键盘事件 触摸事件等, 程序需要获取事件发生的详细信息, 如键盘中的哪个键触发的事件, 触摸所发生的位置等, 对于这种包含更多信息的事件,Android 会将事件信息封装成 XxxEvent 对象, 然后传递给事件监听器 最后, 我们来为文本编辑框添加输入事件监听器, 采用匿名内部类的形式来实现该 79

61 监听器, 具体代码如下 80 1 public class MainActivity extends Activity{ 2 private EditText content; 3 public void oncreate(bundle savedinstancestate) { 4 系统自动生成代码 ( 略 ) 5 content = (EditText) findviewbyid(r.id.content); 根据 ID 获取组件 6 content.setoneditoractionlistener(new OnEditorActionListener() { 7 public boolean oneditoraction(textview v, int actionid, KeyEvent event) { 8 testtext.settext(content.gettext().tostring()); 设置文本框内容 9 return false; 10 } 11 }); 12 } 13 } 注意 :testtext 应定义为 MainActivity 的成员变量或者为 final 修饰的局部变量, 否则无法在匿名内部类中访问该变量 大部分时候, 事件处理器都没有什么复用价值 ( 可复用代码通常都被抽象成了业务逻辑方法 ), 因此大部分事件监听器只是临时使用一次, 所以使用匿名内部类形式的事件监听器更合适 实际上, 这种形式也是目前使用最广泛的事件监听器形式 Android 中常见事件监听器接口及其处理方法如表 4-1 所示 表 4-1 常见事件监听器接口及其处理方法 事件接口处理方法描述 单击事件 View.OnClickListener public abstract void onclick (View v) 单击组件时触发 单击事件 键盘事件 焦点事件 触摸事件 创建上下文菜单 View.OnLongClickListener View.OnKeyListener View.OnFocusChangeListe ner View.OnTouchListener public abstract boolean onlongclick(view v) public abstract boolean onkey (View v, int keycode, KeyEvent event) public abstract void onfocuschange (View v, boolean hasfocus) public abstract boolean ontouch (View v, MotionEvent event) public abstract void OnCreateContextMenu (ContextMenu menu, View v, ContextMenu. ContextMenuInfo menuinfo) 长按组件时触发 处理键盘事件 当焦点发生改变时触发 产生触摸事件 View.OnCreateContextMen ulistener 当上下文菜单创建时触发 事件监听器要与事件源关联起来, 还需要相应注册方法的支持, 事件源通常是界面的某个控件, 而所有的界面控件都继承于 View 类, 因此,View 类所拥有的事件注册方法, 所有的控件都可以调用, 表 4-2 列出了 View 类常见的事件注册方法

62 表 4-2 View 类的常见事件注册方法 方法 类型 描述 public void setonclicklistener(view.onclicklistener l) 普通 注册单击事件 public void setonlongclicklistener(view.onlongclicklistener l) 普通 注册长按事件 public void setonkeylistener(view.onkeylistener l) 普通 注册键盘事件 public void setonfocuschangelistener(view.onfocuschangelistener l) 普通 注册焦点改变事件 public void setontouchlistener(view.ontouchlistener l) 普通 注册触摸事件 public void setoncreatecontextmenulistener( View.OnCreateContextMenuListener l) 普通 注册上下文菜单事件 基于回调的事件处理 Android 平台中, 每个 View 都有自己处理特定事件的回调方法, 开发人员可以通过重写 View 中的这些回调方法来实现需要的响应事件 View 类包含的回调方法主要有 : boolean onkeydown (int keycode, KeyEvent event): 它是接口 KeyEvent.Call back 中的抽象方法, 用于捕捉手机键盘被按下的事件 keycode 为被按下的键值即 键盘码,event 为按键事件的对象 包含了触发事件的详细信息, 如事件的状 态 类型 发生的时间等 当用户按下按键时, 系统会自动将事件封装成 KeyEvent 对象供应用程序使用 ; boolean onkeyup (int keycode, KeyEvent event): 用于捕捉手机键盘按键抬起的 事件 ; boolean ontouchevent (MotionEvent event): 该方法在 View 类中定义, 该方法 用于处理手机屏幕的触摸事件, 包括屏幕被按下 屏幕被抬起 在屏幕中拖动 如果说事件监听机制是一种委托式的事件处理, 那么回调机制则与之相反 在基于回调的事件处理模型中, 事件源和事件监听器是统一的, 或者说事件监听器完全消失了, 当用户在 GUI 组件上激发某个事件时, 组件自己特定的方法将负责处理该事件 为了使用回调机制类来处理 GUI 组件上所发生的事件, 需要为该组件提供对应的事件处理方法, 而 Java 又是一种静态语言, 我们无法为每个对象动态地添加方法, 因此只能通过继承 GUI 组件类, 并重写该类的事件处理方法来实现 下面以一个简单的程序来示范基于回调的事件处理机制 由于需要重写组件类的回调方法, 因此通过自定义 View 来模拟, 自定义 View 时重写该 View 的事件处理方法即可 ( 见下页的 codes\04\ CallbackEventTest\src\iet\jxufe\cn\android\MyButton.java 程序 ) 在自定义的 MyButton 类中, 我们重写了 Button 类的 ontouchevent(motionevent event) 方法, 该方法将会负责处理触摸事件 接下来在界面布局文件中使用这个自定义 的 View ( 见下页的 codes\04\ CallbackEventTest\res\layout\ activity_main.xml 文件 ) 几乎所有基于回调的事件处理方法都有一个 boolean 类型的返回值, 该返回值用于标识该处理方法是否能完全处理该事件 如果处理事件的回调方法返回 true, 表明该处理方法已完全处理该事件, 该事件不会传播出去 ; 如果处理事件的回调方法返回 false, 表明该处理方法并未完全处理该事件, 该事件会传播出去 81

63 82 程序清单 :codes\04\ CallbackEventTest\src\iet\jxufe\cn\android\MyButton.java 1 public class MyButton extends Button { 2 private Context context; 3 public MyButton(Context context, AttributeSet attrs){ 构造方法中必须要有 AttributeSet 参数 4 super(context, attrs); 5 this.context=context; 6 } 7 public boolean ontouchevent(motionevent event) { 8 Toast.makeText(context, "MyButton 中触摸事件触发了! ",Toast.LENGTH_SHORT).show(); 9 return true; 10 } 11 } 程序清单 :codes\04\ CallbackEventTest\res\layout\ activity_main.xml 1 <LinearLayout xmlns:android=" 2 xmlns:tools=" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" > 5 <iet.jxufe.cn.android.mybutton 使用自定义 View 时应使用 6 android:layout_width="wrap_content" 完整的包名 + 类名 7 android:layout_height="wrap_content" 8 android:text="@string/mybtn" /> 9 </LinearLayout> 对于基于回调事件传播而言, 某组件上所发生的事情不仅激发该组件上的回调方 法, 也会触发该组件所在 Activity 的回调方法 ( 前提是事件能传播到 Activity) 当同一组件既采用监听模式, 又采用回调模式, 并且重写了该组件所在 Activity 对 应的回调方法, 而且程序没有阻止事件传播, 即每个方法都返回为 false 那么 Android 系统处理事件的顺序是怎样的呢? 下面我们以一个简单的例子来模拟这种情况, 为上面自定义的按钮注册触摸事件监听器并重写它所在 Activity 上的触摸回调方法, 在每个方法中打印出该方法被调用的信息, 观察控制台里打印的信息 自定义组件代码如下 程序清单 :codes\04\ EventTransferTest \src\iet\jxufe\cn\android \MyButton.java 1 public class MyButton extends Button { 2 public MyButton(Context context, AttributeSet attrs) { 自定义组件的构造方法 3 super(context, attrs); 4 } 5 public boolean ontouchevent(motionevent event) { 6 System.out.println("MyButton 中触摸事件触发了!"); 7 return false; 返回 false, 表示事件可 8 } 以向外传播 9 }

64 新的布局文件代码如下 程序清单 :codes\04\eventtransfertest \res\layout\activity_main.xml 1 <LinearLayout xmlns:android=" 2 xmlns:tools=" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" > 5 <iet.jxufe.cn.android.mybutton 使用自定义 View 时应使用完整的包名 + 类名 6 android:id="@+id/mybtn" 7 android:layout_width="wrap_content" 8 android:layout_height="wrap_content" 9 android:text="@string/mybtn" /> 10 </LinearLayout> 程序清单 :codes\04\ EventTransferTest \src\iet\jxufe\cn\android \MainActivity.java 1 public class MainActivity extends Activity { 2 public void oncreate(bundle savedinstancestate) { 3 super.oncreate(savedinstancestate); 4 setcontentview(r.layout.activity_main); 5 MyButton mybutton=(mybutton)findviewbyid(r.id.mybtn); 6 mybutton.setontouchlistener(new OnTouchListener() { 7 public boolean ontouch(view v, MotionEvent event) { 8 System.out.println(" 监听器中的触摸事件触发了!"); 9 return false; 返回 false, 表示事件可以向外传播 10 } 11 }); 12 } 13 public boolean ontouchevent(motionevent event) { 14 System.out.println("MainActivity 中的触摸事件触发了!"); 15 return false; 返回 false, 表示事件可以向外传播 16 } 17 } 程序运行后控制台打印信息如图 4-3 所示 图 4-3 控制台打印信息 通过打印结果, 可知最先触发的是该组件所绑定的事件监听器, 接着才触发该组 件提供的事件回调方法, 最后才传播到该组件所在的 Activity, 调用 Activity 相应的事件回调方法 如果我们让某一个事件处理方法返回 true, 那么该事件将不会继续向外传播 试一试 : 改变方法的返回值 ( 将 true 改为 false), 观察控制台输出结果 基于监听的事件处理模型分工更明确, 事件源 事件监听由两个类分开实现, 因此 83

65 具有更好的可维护性 ;Android 的事件处理机制保证基于监听的事件监听器会被优先触发 直接绑定到标签 Android 还有一种简单的绑定事件的方式, 直接在界面布局文件中为指定标签绑定事件处理方法 对于很多 Android 界面组件标签而言, 它们都支持如 onclick onlongclick 等属性, 这种属性的属性值就是一个形如 xxx(view source) 的方法的方法名 例如在布局文件中为组件添加单击事件的处理方法, 布局文件如下所示 84 程序清单 :codes\04\eventbinding\res\layout\activity_main.xml 1 <Button 2 android:id="@+id/mybtn" 3 android:layout_width="wrap_content" 4 android:layout_height="wrap_content" 5 android:text="@string/bind_btn" 6 android:onclick="clickeventhandler"/> 为按钮添加自定义事件处理方法 然后我们在该界面布局对应的 Activity 中定义一个 void clickeventhandler (View source) 方法, 该方法将会负责处理该按钮上的单击事件 详细代码如下页所示 如果此时为该按钮同时添加了事件监听器, 那么执行结果如何呢? 取消上述代码中的注释, 执行程序, 结果是程序只执行监听事件处理, 而不会执行我们自定义的事件处理方法 注意这和前面的基于回调的事件传播有所不同 单击事件方法返回值是 void 而不是 boolean 类型 程序清单 :codes\04\eventbinding\src\iet\jxufe\cn\android \MainActivity.java 1 public class MainActivity extends Activity { 2 // private Button mybtn; 3 public void oncreate(bundle savedinstancestate) { 4 super.oncreate(savedinstancestate); 5 setcontentview(r.layout.activity_main); 6 // mybtn = (Button) findviewbyid(r.id.mybtn); 7 // mybtn.setonclicklistener(new OnClickListener() { 8 // public void onclick(view v) { 9 // Toast.makeText(MainActivity.this, " 监听器中的处理方法 ", 10 // Toast.LENGTH_SHORT).show(); 11 // } 12 // }); 13 } 14 public void clickeventhandler(view source) { 15 Toast.makeText(this, " 自定义事件处理方法 ", Toast.LENGTH_SHORT).show(); 16 } 17 } 4.2 Handler 消息传递机制 Android 平台不允许 Activity 新启动的线程访问该 Activity 里的界面组件, 也不允许将

66 运行状态外送出去, 这样就会导致新启动的线程无法动态改变界面组件的属性值, 与 Activity 进行交互 但在实际 Android 应用开发中, 尤其是涉及动画的游戏开发, 需要让新启动的线程周期性地改变界面组件的属性值, 这就需要借助 Handler 的消息传递机制实现 Handler 类的常用方法如表 4-3 所示 表 4-3 Handler 类的常用方法 方法签名 描述 public void handlemessage (Message msg) 通过该方法获取 处理信息 public final boolean sendemptymessage (int what) 发送一个只含有 what 值的消息 public final boolean sendmessage (Message msg) 发送消息到 Handler, 通过 handlemessage 方法接收 public final boolean hasmessages (int what) 监测消息队列中是否有 what 值的消息 public final boolean post (Runnable r) 将一个线程添加到消息队列 从 Handler 类的方法可知,Handler 类主要有两个作用 : 在新启动的线程中发送消息 ; 在主线程中获取 处理消息 那么新启动的线程何时发送消息? 主线程又如何去获取并处理消息呢? 为了让主线程能 适时 地处理新启动的线程所发送的消息, 显然只能通过回调的方式来实现 我们只要重写 Handler 类中处理消息的方法, 当新启动的线程发送消息时,Handler 类中处理消息的方法被自动回调 开发带有 Handler 类的程序步骤如下 (1) 创建 Handler 类对象, 并重写 handlemessage() 方法 ; (2) 在新启动的线程中, 调用 Handler 对象的发送消息方法 ; (3) 利用 Handler 对象的 handlemessage() 方法接收消息, 然后根据不同的消息执行不同的操作 下面的程序通过一个新线程来动态生成随机数, 然后显示在主线程的文本显示框上 该程序界面布局非常简单, 只有一个 TextView 组件, 在此不给出界面布局代码 程序清单 :codes\04\handlertest\src\iet\jxufe\cn\android\mainactivity.java (1) 最初想法 : 通过启动一个线程, 在线程中动态的改变主线程的界面 1 public class MainActivity extends Activity { 2 private TextView mytext; 3 public void oncreate(bundle savedinstancestate) { 4 super.oncreate(savedinstancestate); 5 setcontentview(r.layout.activity_main); 6 mytext=(textview)findviewbyid(r.id.mytext); 7 mytext.settext(" 生成的随机数为 :"+Math.random()); 8 new Thread(new Runnable(){ 单独启动一个线程动态生成的随机数 9 public void run() { 10 try { 11 while(true){ 12 Thread.sleep(300); 程序休眠 0.3 秒 13 Double random=math.random(); 生成随机数 14 mytext.settext(" 生成的随机数为 :"+random); 85

67 86 15 // 这句代码无法执行, 控制台打印错误信息,Only the original thread that 16 //created a view hierarchy can touch its views. 即该线程不能改变 17 //TextView 的显示, 只有创建 TextView 的线程可以改变 18 } 19 } catch (Exception e) { 20 e.printstacktrace(); 21 } 22 }; 23 }).start(); 24 } 25 } 该程序显示的是第一个生成的随机数, 没有动态变化的效果 因为 Android 中不允许子线程更改主线的界面组件, 在控制台会打印出错误信息, 但程序不会强制退出 (2) 既然子线程不能更改主线程的界面组件, 那么我们模拟一下在主线程中进行更改, 代码见后 1 public class MainActivity extends Activity { 2 private TextView mytext; 3 public void oncreate(bundle savedinstancestate) { 4 super.oncreate(savedinstancestate); 5 setcontentview(r.layout.activity_main); 6 mytext = (TextView) findviewbyid(r.id.mytext); 7 mytext.settext(" 生成的随机数为 :" + Math.random()); 8 try { 9 for (int i = 0; i < 5; i++) { 10 Thread.sleep(300); 程序休眠 0.3 秒 11 Double random = Math.random(); 12 System.out.println(random); 控制台打印生成的随机数 13 mytext.settext(" 生成的随机数为 :" + random); 14 } 15 } catch (Exception e) { 16 e.printstacktrace(); 17 } 18 } 19 } 如果将上面的 for 循环, 改为 while(true) 循环, 程序将进入死循环, 没有任何显示 在此模拟 5 次生成随机数, 运行结果发现仍然不能达到效果 查看控制台打印信息发现有 5 条信息, 而此时 TextView 显示的结果与最后生成的随机数相同 原因是,Android 中 是通过调用 oncreate() 方法来完成界面的显示, 我们这里是在 oncreate() 方法内进行线程休眠, 只是将 oncreate() 方法的执行过程延迟了, 因此无法达到动态改变界面的显示, 只能显示一次 除非我们能多次调用 oncreate() 方法, 而该方法是由系统自动调用的 (3) 使用消息传递机制实现该功能, 界面每隔 0.3 秒更新一次 主要思路, 在子线程里发送消息, 然后主线程收到消息后进行相应的处理即在主线程修改界面显示

68 1 public class MainActivity extends Activity { 2 private TextView mytext; 3 private Handler myhandler; 4 public void oncreate(bundle savedinstancestate) { 5 super.oncreate(savedinstancestate); 6 setcontentview(r.layout.activity_main); 7 mytext = (TextView) findviewbyid(r.id.mytext); 8 mytext.settext(" 生成的随机数为 :" + Math.random()); 9 myhandler = new Handler() { 10 public void handlemessage(message msg) { 11 super.handlemessage(msg); 12 if(msg.what==0x12){ 如果该消息是本程序所发送的, 前后标记一致 13 mytext.settext(" 生成的随机数为 :\n" + Math.random()); 14 } 15 } 16 }; 17 new Thread(new Runnable() { 18 public void run() { 19 try { 20 while (true) { 21 Thread.sleep(300); 22 Message msg=new Message(); 23 msg.what=0x12; 消息的标记 24 myhandler.sendmessage(msg); 25 } 26 } catch (Exception e) { 27 e.printstacktrace(); 28 } 29 }; 30 }).start(); 31 } 32 } 该程序重写了 Handler 类的 handlemessage(message msg) 方法, 该方法用于处理消息, 当新线程发送消息时, 该方法会被自动回调, 然后根据消息的标记, 对不同的消息进行不同的业务逻辑处理, 由于 handlemessage(message msg) 方法依然位于主线程, 所以可以动态的修改 TextView 组件的文本 注意 : 发送消息和处理消息的是同一个 Handler 对象 4.3 异步任务处理 在开发 Android 移动客户端的时候往往要使用多线程来进行操作, 我们通常会将耗时的操作放在单独的线程执行, 避免其占用主线程而给用户带来不好的用户体验 但是在子线程中无法去操作主线程 (UI 线程 ), 在子线程中操作 UI 线程会出现错误 因此 Android 提供了一个类 Handler 在子线程中来更新 UI 线程, 用发消息的机制更新 UI 界面, 87

69 呈现给用户 这样就解决了子线程更新 UI 的问题 但是费时的任务操作总会启动一些匿名的子线程, 给系统带来巨大的负担, 随之带来一些性能问题 因此 Android 提供了一个工具类 AsyncTask, 即异步执行任务 这个 AsyncTask 生来就是处理一些后台的比较耗时的任务, 给用户带来良好用户体验的, 从编程的语法上显得优雅了许多, 不再需要子线程和 Handler 就可以完成异步操作并且刷新用户界面 Android 的 AsyncTask 类对线程间通讯进行了包装, 提供了简易的编程方式来使后台线程和 UI 线程进行通讯, 即后台线程执行异步任务, 并把操作结果通知 UI 线程 AsyncTask 是抽象类,AsyncTask 定义了三种泛型类型 Params Progress 和 Result Params: 启动任务执行的输入参数, 如 HTTP 请求的 URL; Progress: 后台任务执行的百分比 ; Result: 后台执行任务最终返回的结果, 如 String,Integer 等 AsyncTask 类中主要有以下几个方法 : onpreexecute(): 该方法将在执行实际的后台操作前被 UI 线程调用 可以在该方法中做一些准备工作, 如在界面上显示一个进度条, 或者一些控件的实例化, 这个方法可以不用实现 doinbackground(params...): 将在 onpreexecute 方法执行后马上执行, 该方法运行在后台线程中 这里将主要负责执行那些比较耗时的后台处理工作 可以调用 publishprogress 方法来实时更新任务进度 该方法是抽象方法, 子类必须实现 onprogressupdate(progress...): 在 publishprogress 方法被调用后,UI 线程将调用这个方法从而在界面上展示任务的进展情况, 例如通过一个进度条进行展示 onpostexecute(result): 在 doinbackground 执行完成后,onPostExecute 方法将被 UI 线程调用, 后台的计算结果将通过该方法传递到 UI 线程, 并且在界面上展示给用户 oncancelled(): 在用户取消线程操作的时候调用 在主线程中调用 oncancelled() 的时候调用 doinbackground 方法和 onpostexecute 的参数必须对应, 这两个参数在 AsyncTask 声明的泛型参数列表中指定, 第一个为 doinbackground 接收的参数, 第二个为显示进度的参数, 第三个为 doinbackground 返回值和 onpostexecute 传入的参数 为了正确使用 AsyncTask 类, 必须遵守以下几条准则 : (1)AsyncTask 的实例必须在 UI 线程中创建 ; 88 (2)execute(Params...) 方法必须在 UI 线程中调用 ; (3) 不要手动的调用 onpreexecute(), onpostexecute(result),doinbackground (Params...), onprogressupdate(progress...) 这几个方法, 需要在 UI 线程中实例化这个 task 来调用 ; (4) 该 task 只能被执行一次, 否则多次调用时将会出现异常 下面以一个简单的示例, 演示 AsyncTask 的使用, 该程序通过睡眠来模拟耗时操作, 程序代码如下

70 程序清单 :codes\04\ AsyncTaskTest\res\layout\ activity_main.xml 1 <LinearLayout xmlns:android=" 2 xmlns:tools=" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 <Button 7 android:id="@+id/mybtn" 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:text="@string/down"/> 11 <TextView 12 android:id="@+id/mytext" 13 android:layout_width="match_parent" 14 android:layout_height="wrap_content" /> 15 <ProgressBar 16 android:id="@+id/mybar" 17 android:layout_width="match_parent" 18 android:layout_height="wrap_content" 19 android:visibility="invisible" 初始时进度条不可见 20 android:max="100" 进度条最大值为 style="?android:attr/progressbarstylehorizontal"/> 设置进度条样式, 调 22 </LinearLayout> 用系统资源 异步任务处理类代码如下 程序清单 :codes\04\ AsyncTaskTest\src\iet\jxufe\cn\android\DownTask.java 1 public class DownTask extends AsyncTask<Integer, Integer, String> { 2 private TextView tv; 3 private ProgressBar pb; 4 public DownTask(TextView tv,progressbar pb){ 5 this.tv=tv; 初始化控件 6 this.pb=pb; 7 } 8 public DownTask(){ 提供一个无参的构造方法 9 } 10 protected String doinbackground(integer... param) { 11 for(int i=0;i<=100;i++){ 12 publishprogress(i); 13 try{ 14 Thread.sleep(param[0]); 15 }catch (Exception e) { 16 e.printstacktrace(); 17 } 18 } 19 return " 下载完毕 "; 20 } 21 protected void onpreexecute() { 22 super.onpreexecute(); 23 } 89

71 90 24 protected void onpostexecute(string result) { 执行结束后, 相关界面组件属性 25 tv.settext(result); 的设置 26 tv.settextcolor(color.red); 27 tv.settextsize(20); 28 pb.setvisibility(view.invisible); 29 super.onpostexecute(result); 30 } 31 protected void onprogressupdate(integer... param) { 更改界面组件的属性 32 tv.settext(" 当前完成任务的 "+param[0]+"%"); 33 pb.setprogress(param[0]); 34 tv.setvisibility(view.visible); 35 pb.setvisibility(view.visible); 36 super.onprogressupdate(param); 37 } 38 } 程序清单 :codes\04\ AsyncTaskTest\src\iet\jxufe\cn\android\MainActivity.java 1 public class MainActivity extends Activity { 2 private Button mybtn=null; 3 private TextView mytext=null; 4 private ProgressBar mybar=null; 5 public void oncreate(bundle savedinstancestate) { 6 super.oncreate(savedinstancestate); 7 setcontentview(r.layout.activity_main); 8 mybtn=(button)findviewbyid(r.id.mybtn); 9 mytext=(textview)findviewbyid(r.id.mytext); 10 mybar=(progressbar)findviewbyid(r.id.mybar); 11 mybtn.setonclicklistener(new OnClickListener() { 12 public void onclick(view v) { 13 DownTask downtask=new DownTask(myText,myBar); 14 downtask.execute(100); 每隔 0.1 秒更新一次 15 } 16 }); 17 } 18 } 上面程序中, 异步处理类是单独作为一个外部类, 放在外面, 因此, 需要把主线程 中的相应的界面组件以参数的形式传递给异步处理类 其实, 为了方便可以把异步处理 类放在 Activity 内部, 作为它的一个内部类, 这样就省去了组件初始化的步骤, 可自由 调用 Activity 中的相关组件, 更简洁些 但并不提倡这样做, 因为异步处理类一般来说 业务逻辑比较复杂, 放在 Activity 中会显得比较臃肿, 结构比较混乱 程序的运行效果与执行流程如图 4-4 所示 初始化时, 文本显示框和进度条都是不可见的, 界面中只有一个开始下载按钮, 单击该按钮后, 文本显示框和进度条都显示出来, 并且它们的值是动态变化的 当下载完毕后, 进度条消失, 文本显示框给出下载完毕的提示

72 下载之前 下载中 下载完成 图 4-4 程序运行结果及说明 初始时, 文本显示框和进度条都是不可见的, 开始下载后才会显示它们, 下载结束后, 进度条消失, 文本显示框显示提示信息 程序执行流程 : 单击 开始下载 按钮后, 创建 DownTask 对象, 并调用该对象的 execute() 方法 该方法内部调用该类的 onpreexcute() 方法, 该方法执行完成后, 会执行该类的 doinbackground(), 在这个方法中显式的调用了 publishprogress() 方法, 从而触发 onprogressupdate() 方法, 更新界面 doinbackground() 方法调用结束后, 系统自动调用 onpostexcute() 方法, 完成整个过程 异步任务处理方法调用顺序如图 4-5 所示 图中, 较密的虚线框里的方法是在主线程中执行的, 实线框中的方法是在子线程中执行的, 较疏的虚线框里的方法会循环多次调用该方法 具体过程如下 :execute() 方法传入的参数, 将会传给 doinbackground() 方法, 在 doinbackground() 方法中, 循环调用 publishprogress() 方法, 而该方法又会触发 onprog ressupdate() 方法, 并且 publishprogress() 方法传入的参数会传递给 onprogressupdate() 方法 doinbackground() 方法执行结束后, 会将结果作为参数传递给 onpostexecute() 方法, 而这些参数的类型, 在类声明的时候就已经指定了 execute() onpreexecute() 循环执行 onprogressupdate() onpostexecute() 图 4-5 方法调用流程图 doinbackground() publishprogress() 注意 : 以上方法中只有 doinbackground() 方法以及 publishprogress() 方法是在子线程中执行的, 其他的方法都是在主线程中执行的, 所以可以在这些方法中更新界面组件 91

73 4.4 本章小结 本章是对前面两章的补充, 图形界面编程肯定需要与事件处理相结合, 当我们设计了界面友好的应用之后, 我们必须为界面上的相应组件提供响应, 从而当用户操作时, 能执行相应的功能, 这种响应动作就是由事件处理来完成的 本章的重点就是掌握 Android 的事件处理机制 : 基于监听的事件处理 基于回调的事件处理以及直接绑定到标签的事件处理 了解事件传播的顺序 常见的事件监听器接口及其注册方法 此外还着重讲解了动态改变界面组件的显示, 需要注意的是,Android 不允许在子线程中更新主线程的界面组件 因此, 我们讲解通过 Handler 消息处理机制, 当需要子线程需要更改界面显示时, 子线程就向主线程发送一条消息, 主线程接收到消息后, 自己对界面显示进行修改 最后, 我们讲解了异步任务处理, 异步任务处理主要处理一些比较耗时的操作, 是对消息处理机制的一种补充 课后练习 1.Android 中事件处理方式主要有哪三种? 2. 基于监听的事件处理模型中, 主要包含的三类对象是什么? 3. 简单描述基于监听的事件处理的过程? 4. 实现事件监听器的方式有 和 5. 当一个控件, 既重写了该控件的事件回调方法 同时重写了该控件所在 Activity 的回调方法 还为其添加了相应的事件监听器, 当事件触发时, 事件处理的顺序是怎样的? 6. 简要描述 Handler 消息传递机制的步骤? 7. 使用异步任务处理时, 以下方法中, 不能更改界面组件显示的是 ( ) A)onPreExecute() B)doInBackground() C)onPostExecute() D)onProgressUpdate() 92

74 本章要点 第 7 章 Android 中的数据存取 读 写 Android 手机中存储的普通文件 读 写 SharedPreferences( 以 XML 存储的配置文件 ) 内容 SQLite 数据库的使用 统一内容提供者 ContentProvider 类及其应用 读取网络资源的读取 本章知识结构图 数据存储 文件存储 SharedPreferences SQLite 数据库 ContentProvider ContentValues 访问系统联系人数据共享备忘录数据 137 获取网络资源 手机文件存取 SD 卡文件存取 访问本应用 SharedPreferences 访问其他应用 SharedPreferences SQLite 概述 相关类库 备忘录示例 概述 相关类库 应用示例 通过 URL 访问网络资源 WebView 控件显示网页 SQLiteOpenHelper SQLiteDatabase Uri UriMatcher ContentUris ContentResolver

75 本章示例 138 SQLite 文件操作 数据存取 网络 Sharedpreferences ContentProvider

76 一个比较好的应用程序, 应该能够为用户提供一些个性化的设置, 能够保存用户的使用记录, 而这些都离不开数据的存储 Android 系统提供了多种数据存储方式, 开发者可根据具体情景选择合适的存储方式, 例如你的数据是仅限于本应用程序访问还是允许其他应用程序访问, 以及数据所需要的空间等 主要有以下五种方式 : (1) 文件存储 : 以流的方式读取数据 ; (2)SharedPreferences: 以键值对的形式存储私有的简单的数据 ; (3)SQLite 数据库 : 在一个私有的数据库中存储结构化数据 ; (4)ContentProvider( 内容提供者 ): 用于在应用程序间共享数据 ; (5) 网络文件存储 : 从网络中读取数据, 上传数据 Android 应用开发是基于 Java 的, 因此 Java IO 中的相关经验大部分都可 移植 到 Android 应用开发上 Android 系统还提供了一些专门的输入输出 API, 通过这些 API 可以更有效地进行输入 输出操作 如果应用程序只有少量数据需要保存, 那么使用普通文件就可以 ; 如果应用程序只需保存一些简单类型的配置信息, 那么使用 SharedPreferences 就可以 ; 如果应用程序需要保存结构比较复杂的数据时, 就需要借助于数据库,Android 系统内置了一个轻量级的 SQLite 数据库, 它没有后台进程, 整个数据库就对应于一个文件 Android 为访问 SQLite 数据库提供了大量便捷的 API; 如果想从网络上下载一些资源, 则需要用到网络存取 为了在应用程序之间交互数据,Android 提供了一种将私有数据暴露给其他应用程序的方式 ContentProvider,ContentProvider 是 Android 的组件之一, 是不同应用程序之间进行数据交换的标准 API 本章将详细讲解各种数据存取方式的使用, 掌握本章知识, 将可以为我们的应用实现普通文件存取和个性化设置参数的设置等操作 7.1 普通文件存储 Android 是基于 Java 语言的, 在 Java 中提供了一套完整的输入输出流操作体系, 与文件相关的有 FileInputStream FileOutputStream 等, 通过这些类可以非常方便地访问磁盘上的文件内容 同样的 Android 也支持这种方式来访问手机上的文件 Android 手机中的文件有两个存储位置 : 内置存储空间和外部 SD 卡, 针对不同位置的文件的存储有所不同, 下面分别讲解对它们的操作 手机内部存储空间文件的存取 Android 中文件的读取操作主要是通过 Context 类来完成的, 该类提供了如下两个方 法来打开本应用程序的数据文件夹里的文件 IO 流 openfileinput(string name): 打开 app 数据文件夹 (data) 下 name 文件对应的输入流 ; openfileoutput(string name, int mode): 打开应用程序的数据文件夹下的 name 文件对应输出流 name 参数用于指定文件名称, 不能包含路径分隔符 /, 如果文件不存 在,Android 会自动创建,mode 参数用于指定操作模式,Context 类中定义了四种操作模 139

77 式常量, 分别为 : Context.MODE_PRIVATE=0: 为默认操作模式, 代表该文件是私有数据, 只能被应用本身访问, 在该模式下, 写入的内容会覆盖原文件的内容 Context.MODE_APPEND=32768: 模式会检查文件是否存在, 存在就往文件追加内容, 否则创建新文件再写入内容 ; Context.MODE_WORLD_READABLE=1: 表示当前文件可以被其他应用读取 ; Context.MODE_WORLD_WRITEABLE=2: 表示当前文件可以被其他应用写入 提示 : 如果希望文件既能被其他应用读也能写, 可以传入 : Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE 或者直接传入数值 3, 四种模式中除了 Context.MODE_APPEND 会将内容追加到文件末尾, 其他模式都会覆盖掉原文件的内容 在手机上创建文件和向文件中追加内容的步骤如下 : (1) 调用 openfileoutput() 方法, 传入文件的名称和操作的模式, 该方法将会返回一个文件输出流 ; (2) 调用 write() 方法, 向这个文件输出流中写入内容 ; (3) 调用 close() 方法, 关闭文件输出流 读取手机上文件的一般步骤如下 : (1) 调用 openfileinput() 方法, 传入需要读取数据的文件名, 该方法将会返回一个文件输入流对象 ; (2) 调用 read() 方法读取文件的内容 ; (3) 调用 close() 方法, 关闭文件输入流 下面以一个简单的示例, 来演示文件读取的操作, 程序运行界面如图 7-1 所示, 界面中包含两个文本输入框, 一个用于向文件中写入内容, 一个用于显示从文件中读取的内容 界面布局文件如下 140 图 7-1 读取手机文件运行界面图

78 程序清单 :codes\07\filetest\res\layout\ activity_main.xml 1 <LinearLayout xmlns:android=" 2 xmlns:tools=" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 垂直线性布局 6 <EditText 7 android:id="@+id/writetext" 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:minlines="2" 设置文本输入框的最少为两行 11 android:hint="@string/hint"/> 设置文本输入框的提示信息 12 <Button 13 android:id="@+id/write" 14 android:layout_width="wrap_content" 15 android:layout_height="wrap_content" 16 android:text="@string/write"/> 17 <EditText 18 android:id="@+id/readtext" 19 android:layout_width="match_parent" 20 android:layout_height="wrap_content" 21 android:editable="false" 设置文本输入框为不可编辑状态 22 android:hint="@string/readhint"/> 23 <Button 24 android:id="@+id/read" 25 android:layout_width="wrap_content" 26 android:layout_height="wrap_content" 27 android:text="@string/read"/> 28 </LinearLayout> 在 MainActivity.java 中分别为写入内容和读取内容按钮添加事件处理, 代码如下 程序清单 :codes\07\filetest\src\iet\jxufe\cn\android\mainactivity.java 1 public class MainActivity extends Activity { 2 private Button read, write; 3 private EditText readtext, writetext; 4 private String filename="content.txt"; 设置保存的文件名 5 public void oncreate(bundle savedinstancestate) { 6 super.oncreate(savedinstancestate); 7 setcontentview(r.layout.activity_main); 8 read = (Button) findviewbyid(r.id.read); 获取读取内容按钮 9 write = (Button) findviewbyid(r.id.write); 获取写入内容按钮 10 readtext = (EditText) findviewbyid(r.id.readtext); 11 writetext = (EditText) findviewbyid(r.id.writetext); 12 read.setonclicklistener(new OnClickListener() { 添加事件处理 13 public void onclick(view v) { 14 readtext.settext(read()); 将读取的内容显示在文 15 } 本编辑框上 141

79 16 }); 17 write.setonclicklistener(new OnClickListener() { 18 public void onclick(view v) { 19 write(writetext.gettext().tostring()); 将文本编辑框的内容写 20 } 入文件 21 }); 22 } 23 public void write(string content) { 该方法将字符串内容写 24 try { 入文件 25 FileOutputStream fos = openfileoutput(filename, Context.MODE_APPEND); 26 PrintStream ps = new PrintStream(fos); 27 ps.print (content); 28 ps.close(); 29 fos.close(); 30 } catch (Exception e) { 31 e.printstacktrace(); 32 } 33 } 34 public String read() { 该方法用于读取文件信 35 StringBuilder sbbuilder = new StringBuilder(""); 息, 并以字符串返回 36 try { 37 FileInputStream is = 获取文件输入流 openfileinput(filename); 38 byte[] buffer = new byte[64]; 定义缓冲区的大小 39 int hasread; 记录每次读取的字节数 40 while ((hasread = is.read(buffer))!= -1) { 41 sbbuilder.append(new String(buffer, 0, hasread)); 42 } 43 } catch (Exception e) { 44 e.printstacktrace(); 45 } 46 return sbbuilder.tostring(); 47 } 48 } 当我们第一次在第一个文本编辑框中, 写入一些内容, 单击写入内容按钮后, 系统 首先会查找手机上是否存在该文件, 如果不存在则创建该文件 应用程序的数据文件 默认保存在 /data/data/<package name>/files 目录下, 文件的后缀名由开发人员设定 其 中 package name 为当前应用的包名 生成的文件如何查看呢? 将当前视图切换到 DDMS 视图, 切换方法是在 Eclipse 的右上角选择 DDMS 视图, 如果没有 DDMS 视图, 可 通过 Eclipse 的菜单栏中 Window Open Perspective other... DDMS 打开该视图 在该 视图中有一个 File Explorer 面板, 可浏览机器上的所有文件, 如图 7-2 所示 142 图 7-2 DDMS 视图中 File Explorer 面板位置

80 运行程序后, 会发现在模拟器的 /data/data/iet.jxufe.cn.android/files 目录下多了一个 context.txt 文件 如图 7-3 所示 这个文件是不能直接在 Eclipse 中打开的, 需要先下载到电脑上才能查看里面的内容 方法是在该面板的右上方, 有一个 pull a file from the device 按钮 ( 从设备上取出文件 ), 同样我们也可以将电脑上的文件上传到设备中 图 7-3 DDMS 视图中文件生成的位置 默认情况下, 内存中的文件只属于当前应用程序, 而其他应用程序是不能访问的, 当用户卸载了该应用程序时, 这些文件也会被移除 问题与讨论 : (1) 当手机上不存在该文件时, 我们先写后读与先读后写有区别吗? 程序会不会出错?( 具体做法 : 将手机上的 context.txt 文件删除, 重新启动程序, 然后分别进行先写后读与先读后写操作, 观察效果 ) ( 注 : 若手机上不存在该文件, 当我们向该文件中写入内容时, 系统会自动地为我们创建该文件, 而未写先读时, 读出的内容为空, 系统会生成 files 文件夹, 但并不会生成该文件 只有在写的时候才会生成该文件 实际上, 此时系统会在控制台打印出警告信息, 但程序不会强制退出 ) (2) 不同操作模式的区别, 当我们多次执行写入操作时, 文件里的内容是被覆盖还是不断地在文件末尾附加新数据?( 具体做法 : 修改 openfileoutput() 方法的第二个参数 ) ( 注 : 若采用附加模式, 则多次写入时, 会在文件末尾进行添加新写的内容, 不会覆盖以前的内容, 如果改成其他模式, 则会进行覆盖 ) 读写 SD 卡上的文件 前面我们学习了如何读取手机内存中的文件, 对于手机而言, 内存是非常宝贵的, 相对而言也是比较小的 内存的空间直接会影响到手机的运行速度, 通常不建议将数据保存到手机内存中, 特别是一些比较大的资源如图片 音频 视频等 那么这些资源存放在哪里呢? 存放在外存上, 几乎所有的 Android 设备, 都会配有外存设备, 最常见的就是 SD 卡 读取 SD 卡上的文件和读取手机上的文件类似, 都是通过文件操作流的方式进行读 取的,Android 中没有提供单独的 SD 卡文件操作类, 直接使用 Java 中的文件操作即可, 关键是如何确定文件的位置 因为 SD 卡的可移动性, 因此, 在访问之前, 我们需要验证手机的 SD 卡的状态,Android 为我们提供了 Environment 类来完成这一操作 要想在模拟器中使用 SD 卡, 首先需要创建一张 SD 卡 ( 当然不是真的 SD 卡, 只是一个镜像文件 ) 创建 SD 卡可以在 Eclipse 创建模拟器时随同创建, 也可以使用 Android 提 143

81 供的命令在命令行进行创建 打开命令行窗口进入 android SDK 安装路径的 tools 目录下, 输入以下命令在 D 盘创建一张容量为 2G 的 SD 卡, 文件后缀名可以随便取, 建议使用.img, 生成的文件为镜像文件 如果你在环境变量中添加了 Android tools 目录, 则可直接输入相应的命令即可 144 mksdcard 2048M D:\sdcard.img 读 写 SD 卡上的文件步骤如下 : (1) 调用 Environment 的 getexternalstoragestate() 方法判断手机上是否插入了 SD 卡, 并且应用程序具有读写 SD 卡的权限 Environment.getExternalStorageState() 方法用于获取 SD 卡的状态, 如果手机装有 SD 卡, 并且可以进行读写, 那么方法返回的状态等于 Environment.MEDIA_MOUNTED; (2) 调用 Environment 的 getexternalstoragedirectory() 方法来获取外部存储器, 也就是 SD 卡的目录 ( 也可以使用绝对路径, 但不提倡, 因为不同版本的绝对路径可能不一样 ); (3) 使用 FileInputStream FileOutputStream FileReader FileWriter 读 写 SD 卡里的文件 注意 : 为了读 写 SD 卡上的数据, 必须在应用程序的清单文件 (AndroidManifest.xml) 中添加读 写 SD 卡的许可权限, 如下所示 1 <!-- 在 SD 卡中创建与删除文件权限 --> 2 <uses-permission android:name= android.permission.mount_unmount_filesystems /> 3 <! 向 SD 卡写入数据权限 --> 4 <uses-permission android:name= android.permission.write_external_storage /> 下面仍然以上面的程序为例, 只是这次将数据写入到 SD 卡上的文件, 程序界面布局一致, 在此不再列出 关键代码区别在于, 在读写之前需先判断手机上是否存在 SD 卡, 然后运用 Java 的输入输出流技术进行读写操作, 关键代码如下

82 程序清单 :codes\07\sdcardfiletest\src\iet\jxufe\cn\android\mainactivity.java 1 public void write(string content) { 向文件中写入内容 2 try { 判断手机中 SD 卡状态 3 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 4 File sdcarddir = Environment.getExternalStorageDirectory(); 获取 SD 卡目录 5 File destfile = new 根据路径和文件名创建文 File(sdCardDir.getCanonicalPath() 件 6 + File.separator + filename); 7 RandomAccessFile raf = new RandomAccessFile(destFile, "rw"); 8 raf.seek(destfile.length()); 把指针定位到文件末尾 9 raf.write(content.getbytes()); 在文件末尾追加新的内容 10 raf.close(); 关闭文件 11 } 12 } catch (Exception e) { 13 e.printstacktrace(); 14 } 15 } 16 public String read() { 从文件中读取内容 17 StringBuilder sbbuilder = new StringBuilder(""); 18 try { 19 if(environment.getexternalstoragestate(). 判断手机中是否存在可用 20 equals(environment.media_mounted)){ 的 SD 卡 21 File sdcard=environment.getexternalstoragedirectory(); 22 File destfile = new File(sdCard.getCanonicalPath() 23 + File.separator + filename); 24 FileInputStream fis=new FileInputStream(destFile); 创建文件输入流 25 byte[] buffer = new byte[64]; 定义临时缓存区大小 26 int hasread; 记录每次读取的字节大小 27 while ((hasread = fis.read(buffer))!= -1) { 不断循环直到文件末尾 28 sbbuilder.append(new String(buffer, 0, hasread)); 在字符串后追加内容 29 } 30 return sbbuilder.tostring(); 31 } 32 } catch (Exception e) { 33 e.printstacktrace(); 34 } 35 return null; 36 } 上面向文件中写入内容时, 使用到 Java 的 RandomAccessFile 类, 该类支持随机访问文件内容, 主要是通过 seek 方法来设定文件指针的位置, 每次读写内容时, 都是从该指针处开始进行读取的, 从而实现了随机访问该文件内容的功能 该类还有一个特点, 就是既可以读也可以写, 创建时需指定它的模式 详细用法可查看 Java 帮助文档 注意 : 程序中 raf.seek(destfile.length()) 用于将文件的指针定位到文件的末尾, 从而实现将新内容附加到文件的目的 如果没有这句代码, 多次向文件中写入内容时, 后写的内容会替换前面的内容 读取操作时, 采用的是简单的文件输入输出流, 每次都是读 取整个文件内容 145

83 注意 : 在程序运行前, 别忘了在 AndroidManifest.xml 文件中添加读写 SD 卡的许可权限 程序运行后, 打开 File Explorer, 我们可以在 /mnt/sdcard 目录下找到我们读取的文件, 如图 7-4 所示 它在第一次写入时, 由系统自动创建的 146 图 7-4 SD 卡中文件的存储位置 7.2 共享参数 (SharedPreferences) 文件存取 通常我们开发的应用程序都需要向用户提供软件参数设置功能, 这样用户可根据自己的爱好进行设置, 为了使用户下次使用时, 不用重复设置, 我们需要保存这些设置信息 对于软件配置参数的保存, 如果是 Windows 软件通常会采用 ini 文件进行保存, 在 java 中, 我们可以采用 properties 属性文件或者 xml 文件进行保存 类似的,Android 为我们提供了一个 SharedPreferences 接口, 来保存配置参数, 它是一个轻量级的存储类 SharedPreferences 的存储位置和格式 应用程序使用 SharedPreferences 接口可以快速而高效的以键值对的形式保存数据, 非常类似于 Bundle 信息以 XML 文件的形式存储在 Android 设备上 Sharedpreferences 经常用来存储应用程序的设置例如用户设置 主题以及其他应用程序属性 它也可以保存用户名 密码 自动登录标志以及记住用户标志等登录信息 Sharedpreferences 里的数据可被该应用的所有组件所访问 SharedPreferences 接口本身只提供了读取数据的功能并没有提供写入数据的功能, 如果需要实现写入功能, 则需通过 SharedPreferences 的内部接口 Editor 来实现, SharedPreferences 调用 edit() 方法即可获取它对应的 Editor 对象 SharedPreferences 本身是一个接口, 不能直接实例化, 只能通过 Context 提供的 getsharedpreferences(string name, int mode) 方法来获取 SharedPreferences 实例, 第一个参数表示保存信息的文件名, 不需要后缀 ; 第二个参数表示 Sharedpreferences 的访问权

84 限, 和前面读取应用程序中的文件类似, 包括只能被本应用程序读 写, 能被其他应用程序读 能被其他应用程序写 下面我们以一个简单的示例来讲解 SharedPreferences 的使用方法, 并实现保存用户登录信息的功能 实际应用中几乎所有的需登录的应用都提供了这一功能, 每次登录时提示使用该程序的次数 程序运行界面如图 7-5 所示, 用户第一次登录时, 可设置是否记录密码和是否自动登录, 以避免用户每次登录时都需重新输入 如果用户勾选记住密码复选框, 则下次登录时, 会直接显示用户名和密码, 用户只需单击登录即可, 界面如图 7-6 所示 如果用户勾选自动登录复选框, 则每次打开应用时都会直接跳转到欢迎界面, 并显示当前用户名称 图 7-5 第一次运行时界面显示效果 登录界面布局文件代码如下 图 7-6 记住密码后运行的界面效果 程序清单 :codes\07\savelogininfo\res\layout\ activity_main.xml 1 <LinearLayout xmlns:android=" 2 xmlns:tools=" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 垂直线性布局 6 <TableLayout 表格布局 7 android:layout_width="match_parent" 8 android:layout_height="wrap_content" 9 android:layout_marginleft="20dp"> 文本距离左边距为 20dp 10 <TableRow> 11 <TextView 用户名文本显示框 12 android:layout_width="wrap_content" 13 android:layout_height="wrap_content" 14 android:text="@string/name" 15 android:textsize="18sp" /> 文本文字大小 16 <EditText 输入用户的文本编辑框 17 android:id="@+id/name" 18 android:layout_width="240dp" 文本编辑框宽度为 240dp 19 android:layout_height="wrap_content" /> 20 </TableRow> 21 <TableRow> 22 <TextView 密码文本显示框 23 android:layout_width="wrap_content" 147

85 android:layout_height="wrap_content" android:layout_marginleft="20dp" 27 android:textsize="18sp" /> 28 <EditText android:layout_height="wrap_content" 31 android:inputtype="textpassword" /> 设置为密码框 32 </TableRow> 33 </TableLayout> 34 <LinearLayout 35 android:layout_width="match_parent" 36 android:layout_height="wrap_content" 37 android:orientation="horizontal" > 水平线性布局 38 <CheckBox 是否记住密码复选框 android:layout_width="wrap_content" 41 android:layout_height="wrap_content" 42 /> 43 <CheckBox 是否自动登录复选框 android:layout_width="wrap_content" 46 android:layout_height="wrap_content" 47 /> 48 <Button android:layout_width="match_parent" 51 android:layout_height="wrap_content" 52 /> 53 </LinearLayout> 54 </LinearLayout> 下面为登录按钮添加事件处理, 由于并不是每一次都会进入登录界面, 因此需在界 面中进行判断, 如果保存的登录信息中, 自动登录为 true, 那么就直接显示欢迎界面, 否则才会显示登录界面 在本应用程序中, 是通过改变界面布局来改变显示内容的, 整 个应用仍然只有一个 Activity 只是该 Activity 在不同布局间切换, 而不是不同 Activity 间 相互跳转 详细代码如下 程序清单 :codes\07\savelogininfo\src\iet\jxufe\cn\android\mainactivity.java 1 public void oncreate(bundle savedinstancestate) { 2 super.oncreate(savedinstancestate); 3 loginpreferences = getsharedpreferences("login", Context.MODE_PRIVATE); 4 accesspreferences = getsharedpreferences("access", 5 Context.MODE_WORLD_READABLE); 其他应用程序可读 6 int count=accesspreferences.getint("count",1); 获取访问次数, 默认为 1 Toast.makeText(MainActivity.this," 欢迎您, 这是第 "+count+" 次访问!", 7 Toast.LENGTH_LONG).show(); 每次登录时显示访问次数信息 8 logineditor = loginpreferences.edit(); 获取写入登录信息的 Editor

86 对象 9 accesseditor = accesspreferences.edit(); 获取写入访问信息的 Editor 对象 10 accesseditor.putint("count",++count); 写入访问次数信息, 每次自动加 1 11 accesseditor.commit(); 提交写入的数据 12 username = loginpreferences.getstring("name", null) ; 获取保存的用户信息 13 userpsd = loginpreferences.getstring("psd", null); 获取保存的密码信息 14 issavepsd=loginpreferences.getboolean("issavepsd",false); 是否保存密码 15 isautologin=loginpreferences.getboolean("isautologin", false); 是否自动登录 16 if (isautologin) { 如果自动登录为 true 17 this.setcontentview(r.layout.activity_welcome); 显示欢迎界面 18 userinfo = (TextView) findviewbyid(r.id.userinfo); 19 userinfo.settext(" 欢迎您 :" + username + ", 登陆成功!"); 20 } else{ 如果自动登录为 false 21 loadactivity(); 22 } 23 } 加载新的布局文件作为界面的方法, 代码如下 1 public void loadactivity() { 2 this.setcontentview(r.layout.activity_main); 设置界面为登录界面 3 login = (Button) findviewbyid(r.id.login); 4 rememberpsdbox = (CheckBox) findviewbyid(r.id.rememberpsd); 5 autologinbox = (CheckBox) findviewbyid(r.id.autologin); 6 name = (EditText) findviewbyid(r.id.name); 7 psd = (EditText) findviewbyid(r.id.psd); 8 if (issavepsd) { 如果获取的保存密码为 true 9 psd.settext(userpsd); 设置密码框的值为保存的值 10 name.settext(username); 显示用户名为保存的用户名 11 rememberpsdbox.setchecked(true); 设置保存密码复选框为选中状态 12 } 13 login.setonclicklistener(new OnClickListener() { 14 public void onclick(view v) { 15 logineditor.putstring("name", name.gettext().tostring()); 写入用户名 16 logineditor.putstring("psd", psd.gettext().tostring()); 写入密码 17 logineditor.putboolean("issavepsd", rememberpsdbox.ischecked()); 18 logineditor.putboolean("isautologin", autologinbox.ischecked()); 19 logineditor.commit(); 提交写入的登录信息 20 MainActivity.this.setContentView(R.layout.activity_welcome); 切换到欢迎界面 21 userinfo = (TextView) findviewbyid(r.id.userinfo); 22 userinfo.settext(" 欢迎您 :" + name.gettext().tostring() + ", 登陆成功!"); 23 } 24 }); 25 } 当程序所读取的 SharedPreferences 文件不存在时, 程序也会返回默认值, 并不会抛 149

87 出异常 运行上面的程序, 登录后将得到如图 7-7 所示效果, 切换到 DDMS 视图, 打开 File Explore 面板, 展开文件浏览树, 将会发现在 /data/data/iet.jxufe.cn.android/shared_prefs 目录下, 生成了我们上面定义的文件, 如图 7-8 所示 提示 :SharedPreferences 数据总是保存在 /data/data/<package_name>/shared_prefs 目录下, 并且 SharedPreferences 数据总是以 XML 格式保存 150 图 7-7 登录成功界面效果 图 7-8 SharedPreferences 生成文件的存储位置 将这两个文件下载到计算机上, 打开 login.xml 文件, 内容如下 1 <?xml version='1.0' encoding='utf-8' standalone='yes'?> 2 <map> 3 <string name="psd">123456</string> 4 <boolean name="issavepsd" value="true" /> 5 <string name="name">zhangsan</string> 6 <boolean name="isautologin" value="false" /> 7 </map> access.xml 文件的内容如下 1 <?xml version='1.0' encoding='utf-8' standalone='yes'?> 2 <map> 3 <int name="count" value="7" /> 4 </map> 到此, 保存登录信息的功能就实现了, 但是存在一个问题, 勾选自动登录复选框后, 每次都会直接跳转到欢迎界面, 如果想用另一个账号登录怎么办? 似乎不可能, 为此, 我们为应用程序添加了菜单选项, 包含注销和退出两个菜单, 单击菜单键, 弹出如图 7-9 所示菜单 单击注销菜单项则会跳转到用户登录界面, 要求输入相应登录信息, 单击退出菜单项, 退出程序 菜单的资源文件如下

88 图 7-9 单击菜单按钮显示的菜单 程序清单 :codes\07\savelogininfo\res\menu\ activity_main.xml 1 <menu xmlns:android=" 2 <item android:id="@+id/menu_settings" 注销菜单项 3 android:title="@string/menu_settings"/> 4 <item android:id="@+id/exit" 退出菜单项 5 android:title="@string/exit"/> 6 </menu> 接着将菜单添加到应用程序中, 并为相应的菜单项添加事件处理方法 1 public boolean oncreateoptionsmenu(menu menu) { 创建上下文菜单方法 2 getmenuinflater().inflate(r.menu.activity_main, menu); 绑定菜单资源文件 3 return true; 4 } 5 public boolean onoptionsitemselected(menuitem item) { 为菜单项添加事件处理 6 switch (item.getitemid()) { 7 case R.id.menu_settings: 注销菜单项的事件处理 8 logineditor.putboolean("isautologin", false); 9 logineditor.commit(); 提交写入的登录信息 10 oncreate(null); 重新调用 oncreate 方法, 11 break; 显示登录界面 12 case R.id.exit: 退出菜单项事件处理 13 this.finish(); 结束当前 Activity 14 break; 15 default: 16 break; 17 } 18 return true; 19 } 提示 : SharedPreferences 内部使用 xml 文件保存数据, getsharedpreferences (name,mode) 方法的第一个参数用于指定该文件的名称, 名称不用带后缀, 后缀会由 Android 自动加上 并且该 XML 文件以 map 为根元素,map 元素的每个子元素代表一个 key-value 对, 子元素名称为 value 对应的类型名,SharedPreferences 只能保存几种简单类型的数据 读写其他应用 SharedPreferences Context 提供的 getsharedpreferences(string name,int mode) 方法中, 第二个参数可设置该 SharedPreferences 数据能被其他应用程序读或写, 本节就来详细讲解如何读取其他应用程序的 SharedPreferences 数据 151

89 要读写其他应用的 SharedPreferences, 前提是创建该 SharedPreferences 的应用程序指 定了相应的访问权限, 主要步骤如下 (1) 创建所需访问程序对应的 Context Context 提供了 CreatePackageContext(String packagename,int flages) 方法来创建应用程序上下文 ; 第一个参数表示应用程序的包名, 第二个参数表示标志 Android 系统是根据应用程序的包名来作为该程序的标志的, 常 见的标志为 Context.CONTEXT_IGNORE_SECURITY, 表示忽略所有可能产生的安全问 题 (2) 调用其他应用程序的 Context 的 getsharedpreferences(string name, int mode) 即可 获取相应的 SharedPreferences 对象 ; (3) 如果需要向其他应用的 SharedPreferences 数据写入数据, 调用 SharedPreferences 的 edit() 方法获取相应的 Editor 即可 注意 : 前提是创建该 SharedPreferences 的应用程序指定了相应的访问权限 下面我们就来写一个简单的示例来访问前面应用程序中的 SharedPreferences 数据, 在前面的应用程序中, 包含两个 SharedPreferences, 一个用于保存登录信息的, 是私有 的 ; 一个是用来保存该应用程序访问的次数的, 是可以被其他应用程序读取的 下面我 们尝试访问这两个数据, 看有什么差别 程序关键代码如下, 程序清单 : codes\07\readothersharedpreferences\src\iet\jxufe\cn\android\shared\mainactivity.java 1 public class MainActivity extends Activity { 2 public void oncreate(bundle savedinstancestate) { 3 super.oncreate(savedinstancestate); 4 setcontentview(r.layout.activity_main); 5 SharedPreferences accesspreferences, loginpreferences; 6 Context appcontext = null; 7 try { 8 appcontext = createpackagecontext("iet.jxufe.cn.android", 9 Context.CONTEXT_IGNORE_SECURITY); 创建上下文 10 } catch (Exception e) { 11 e.printstacktrace(); 12 } 13 accesspreferences = appcontext.getsharedpreferences("access", 14 Context.MODE_WORLD_READABLE); 15 int count = accesspreferences.getint("count", 0); 16 loginpreferences = appcontext.getsharedpreferences("login", 17 Context.MODE_WORLD_READABLE); 18 String name = loginpreferences.getstring("name", null); 19 Toast.makeText(this," 你好," + name + ",SaveLoginInfo 应用程序已经被使用了 " + count + " 次!",Toast.LENGTH_LONG).show(); 20 } 21 } 152 运行该程序, 结果如图 7-10 所示, 用户名信息无法获得, 使用默认的 null, 而访问次数值和 access.xml 文件中一致 并且随着 SaveLoginInfo 应用程序的运行而变化 查看控制台打印信息发现有一条错误信息, 试图打开没有许可的 login.xml 文件

90 图 7-10 程序运行结果 注意 :ReadOtherSharedPreferences 与 SaveLoginInfo 应用程序的包名不能一样, 否则它们属于同一个应用程序, 可以共享数据信息, 即可以访问 login.xml 文件的内容, 也就达不到本示例的结果 7.3 SQLite 数据库 SharedPreferences 存储的只是一些简单的 key-value 对, 如果想存储结构有些复杂的数据, 则不能满足要求, 需要用到数据库来保存 在 Android 平台上, 嵌入了一个轻量级的关系型数据库 -SQLite SQLite 并没有包含大型客户 / 服务器数据库 ( 如 Oracle SQL Server) 的所有特性, 但它包含了操作本地数据的所有功能, 简单易用 反应快 SQLite 数据库简单介绍 SQLite 内部只支持 NULL INTEGER REAL( 浮点数 ) TEXT( 字符串文本 ) 和 BLOB( 二进制对象 ) 这五种数据类型, 但实际上 SQLite 也接受 varchar(n) char(n) decimal(p,s) 等数据类型, 只不过在运算或保存时会转成上面对应的数据类型 SQLite 最大的特点是可以把各种类型的数据保存到任何字段中, 而不用关心字段声明的数据类型是什么 例如 : 可以把字符串类型的值存入 INTEGER 类型字段中, 或者在布尔型字段中存放数值类型等 但有一种情况例外 : 定义为 INTEGER PRIMARY KEY 的字段只能存储 64 位整数, 当向这种字段保存除整数以外的数据时,SQLite 会产生错误 由于 SQLite 允许存入数据时忽略底层数据列实际的数据类型, 因此 SQLite 在解析建表语句时, 会忽略建表语句中跟在字段名后面的数据类型信息, 如下面语句会忽略 name 字段的类型信息 :create table person_tb (id integer primary key autoincrement, name varchar(20)), 因此在编写建表语句时我们可以省略数据列后面的类型声明 SQLite 数据库支持绝大部分 SQL92 语法, 也允许开发者使用 SQL 语句操作数据库中的数据, 但 SQLite 数据库不需要安装 启动服务进程, 其底层只是一个数据库文件 本质上看,SQLite 的操作方式只是一种更为便捷的文件操作 常见 SQL 标准语句示例 : 查询语句 : select * from 表名 where 条件子句 group by 分组子句 having... order by 排序子句 例如 : select * from person 查询 person 表中所有记录 select * from person order by id desc 查询 person 表中所有记录, 按 id 号降序排列 153

91 154 select name from person group by name having count(*)>1 查询 person 表中 name 字段值出现超过 1 次的 name 字段的值 分页 SQL:select * from 表名 limit 显示的记录数 offset 跳过的记录数 select * from person limit 5 offset 3 或者 select * from person limit 3,5 从 person 表获取 5 条记录, 跳过前面 3 条记录 插入语句 :insert into 表名 ( 字段列表 ) values( 值列表 ) 如 :insert into person(name, age) values( 张三,26) 向 person 表插入一条记录 更新语句 :update 表名 set 字段名 = 值 where 条件子句 如 :update person set name= 李四 where id=10 将 id 为 10 的人的姓名改为李四 删除语句 :delete from 表名 where 条件子句 如 :delete from person where id=10 删除 person 表中 id 为 10 的记录 SQLite 数据库相关类 为了操作和管理数据库,Android 系统中为我们提供了一些相关类, 常用的有 SQLiteOpenHelper SQLiteDataBase Cursor, 其他的可查看 Android 帮助文档的 android.database.sqlite 包和 android.database 包 SQLiteOpenHelper 是 Android 提供的管理数据的工具类, 主要用于数据库的创建 打开和版本更新 一般用法是创建 SQLiteOpenHelper 类的子类, 并扩展它的 oncreate() 和 onupgrade() 方法 ( 这两个方法是抽象的, 必须扩展 ), 选择性的扩展它的 onopen() 方法 SQLiteOpenHelper 包含如下常用方法 SQLiteDatabase getreadabledatabase(): 以读写的方式打开数据库对应的 SQLiteDatabase 对象, 该方法内部调用 getwritabledatabase() 方法, 返回对象与 getwritabledatabase() 返回对象一致, 除非数据库的磁盘空间满了, 此时, getwritabledatabase() 打开数据库就会出错, 当打开失败后, getreadabledatabase() 方法会继续尝试以只读方式打开数据库 SQLiteDatabase getwritabledatabase (): 以写的方式打开数据库对应的 SQLiteDatabase 对象, 一旦打开成功, 将会缓存该数据库对象 abstract void oncreate (SQLiteDatabase db): 当数据库第一次被创建的时候调用该方法 abstract void onupgrade (SQLiteDatabase db, int oldversion, int newversion): 当数 据库需要更新的时候调用该方法 void onopen (SQLiteDatabase db): 当数据库打开时调用该方法 当调用 SQLiteOpenHelper 的 getwritabledatabase() 或者 getreadabledatabase() 方法获取 SQLiteDatabase 实例的时候, 如果数据库不存在,Android 系统会自动生成一个数据库, 然后调用 oncreate() 方法, 在 oncreate() 方法里可以生成数据库表结构及添加一些应用需

92 要的初始化数据 onupgrade() 方法在数据库的版本发生变化时会被调用, 一般在软件升级时才需改变版本号, 而数据库的版本是由开发人员控制的, 假设数据库现在的版本是 1, 由于业务的变更, 修改了数据库表结构, 这时候就需要升级软件, 升级软件时希望更新用户手机里的数据库表结构, 为了实现这一目的, 可以把数据库版本设置为 2, 并在 onupgrade() 方法里实现表结构的更新 onupgrade() 方法可以根据原版号和目标版本号进行判断, 然后作出相应的表结构及数据更新 SQLiteDatabase 是 Android 提供的代表数据库的类 ( 底层就是一个数据库文件 ), 该类封装了一些操作数据库的 API, 使用该类可以完成对数据进行添加 (Create) 查询 (Retrieve) 更新(Update) 和删除 (Delete) 操作 对 SQLiteDatabase 的学习, 应该重点掌握 execsql() 和 rawquery() 方法 execsql() 方法可以执行 insert delete update 和 create table 之类有更改行为的 SQL 语句 ; 而 rawquery() 方法用于执行 select 语句 execsql(string sql,object[] bindargs): 执行带占位符的 SQL 语句, 如果 sql 语 句中没有占位符, 则第二个参数可传 null; execsql(string sql): 执行 SQL 语句 ; rawquery(string sql,string[] selectionargs): 执行带占位符的 SQL 查询 除了 execsql() 和 rawquery() 方法,SQLiteDatabase 还专门提供了对应于添加 删除 更新 查询的操作方法 :insert() delete() update() 和 query() 这些方法主要是给那些不太了解 SQL 语法的人员使用的, 对于熟悉 SQL 语法的程序员而言, 直接使用 execsql() 和 rawquery() 方法执行 SQL 语句就能完成数据的添加 删除 更新 查询操作, 实际上, 这些方法的内部也是执行 SQL 语句, 由系统根据这些参数拼接一个完整的 SQL 语句 例如 :Cursor query(string table, String[] columns, String selection,string[] selectionargs, String groupby,string having,string orderby, String limit) 方法各参数的含义 : table: 表名, 相当于 select 语句 from 关键字后面的部分 如果是多表联合查询, 可以用逗号将两个表名分开 ; columns: 要查询的列名, 可以是多列, 相当于 select 语句 select 关键字后面的部分 selection: 查询条件子句, 相当于 select 语句 where 关键字后面的部分, 在条件子句允许使用占位符? selectionargs: 对应于 selection 语句中占位符的值, 值在数组中的位置与占位符在语句中的位置必须一致, 否则就会有异常 groupby: 相当于 select 语句 group by 关键字后面的部分 having: 相当于 select 语句 having 关键字后面的部分 orderby: 相当于 select 语句 order by 关键字后面的部分, 如 :personid desc, age asc; limit: 指定偏移量和获取的记录数, 相当于 select 语句 limit 关键字后面的部分 Cursor 接口主要用于存放查询记录的接口,Cursor 是结果集游标, 用于对结果集进行随机访问, 如果熟悉 JDBC, 可发现 Cursor 与 JDBC 中的 ResultSet 作用很相似, 提供了如下方法来移动查询结果的记录指针 move(int offset): 将记录指针向上或向下移动指定的行数 offset 为正数就向下 移动, 为负数就向上移动 155

93 movetonext() 方法可以将游标从当前记录移动到下一记录, 如果已经移过了结果集的最后一条记录, 返回结果为 false, 否则为 true movetoprevious() 方法用于将游标从当前记录移动到上一记录, 如果已经移过了结果集的第一条记录, 返回值为 false, 否则为 true movetofirst() 方法用于将游标移动到结果集的第一条记录, 如果结果集为空, 返回值为 false, 否则为 true movetolast() 方法用于将游标移动到结果集的最后一条记录, 如果结果集为空, 返回值为 false, 否则为 true 使用 SQLiteDatabase 进行数据库操作的步骤如下 : (1) 获取 SQLiteDatabase 对象, 它代表了与数据库的连接 ; (2) 调用 SQLiteDatabase 的方法来执行 SQL 语句 ; (3) 操作 SQL 语句的执行结果 ; (4) 关闭 SQLiteDatabase, 回收资源 下面我们以一个简单的示例, 讲解数据库的操作, 以及这些类的用法 该程序实现备忘录功能, 用于记录生活中的一些重要事情, 并提供查询功能, 可按条件进行模糊查询 运行效果如图 7-11 所示 156 图 7-11 程序运行首界面 图 7-12 选择时间对话框 图 7-13 查询结果显示界面 该程序可输入主题 相关内容以及选择时间 单击选择日期按钮后, 弹出日期选择对话框, 如图 7-12 所示, 确定后会将选择的时间显示在文本编辑框内 单击添加按钮

94 时, 会将相关数据写入数据库, 单击查询按钮时, 会根据主题 内容以及时间进行精确和模糊查询, 查询时, 可指定零或多个条件, 当没有指定任何条件时, 会显示所有的记录, 查询结果显示如图 7-13 所示 下面详细分析其具体实现, 首界面布局文件如下 程序清单 :codes\07\ Memento\res\layout\activity_main.xml 1 <LinearLayout xmlns:android=" 2 xmlns:tools=" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 垂直线性布局 6 <TableLayout 表格布局,3 行 2 列 7 android:layout_width="match_parent" 8 android:layout_height="wrap_content" > 9 <TableRow> 10 <TextView 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:layout_marginleft="10dp" 左边距 10dp 14 android:text="@string/subject" 显示主题标签 15 android:textsize="20sp" /> 文本字体大小 20sp 16 <EditText 17 android:id="@+id/subject" 输入主题的文本编辑框 18 android:layout_width="match_parent" 19 android:layout_height="wrap_content" /> 20 </TableRow> 21 <TableRow> 22 <TextView 23 android:layout_width="wrap_content" 24 android:layout_height="wrap_content" 25 android:layout_marginleft="10dp" 26 android:text="@string/body" 27 android:textsize="20sp" /> 28 <EditText 29 android:id="@+id/body" 30 android:layout_width="match_parent" 31 android:layout_height="wrap_content" 32 android:minlines="4" /> 33 </TableRow> 34 <TableRow> 35 <Button 36 android:id="@+id/choosedate" 37 android:layout_width="wrap_content" 38 android:layout_height="wrap_content" 39 android:text="@string/choosedate" /> 40 <EditText 41 android:id="@+id/date" 42 android:layout_width="200dp" 43 android:layout_height="wrap_content" 44 android:editable="false" /> 157

95 </TableRow> 46 <LinearLayout 47 android:layout_width="match_parent" 48 android:layout_height="wrap_content" 49 android:gravity="center_horizontal" > 50 <Button android:layout_width="wrap_content" 53 android:layout_height="wrap_content" 54 /> 55 <Button android:layout_width="wrap_content" 58 android:layout_height="wrap_content" 59 /> 60 </LinearLayout> 61 </TableLayout> 62 <LinearLayout 63 需要动态设置属性, 64 android:layout_width="match_parent" 所以添加 id 65 android:layout_height="wrap_content" 66 android:orientation="horizontal" > 水平线性布局 67 <TextView 68 引用制定好的样式 69 android:layout_width="40dp" 设置 TextView 的宽度 android:textcolor="#000000"/> 设置颜色为黑色 72 <TextView android:layout_width="50dp" android:textcolor="#000000" /> 77 <TextView android:layout_width="110dp" android:textcolor="#000000" /> 82 <TextView android:layout_width="100dp" android:textcolor="#000000" /> 87 </LinearLayout> 88 <ListView android:layout_width="wrap_content" 91 android:layout_height="wrap_content" /> 92 </LinearLayout> 此布局使用到了样式, 样式定义见清单 :codes\07\ Memento\res\values\style.xml:

96 1 <resources xmlns:android=" 2 <style name="textview"> 3 <item name="android:layout_height">wrap_content</item> 设定高度 4 <item name="android:gravity">center_horizontal</item> 设定对齐方式 5 <item name="android:textsize">18sp</item> 设定文本大小 6 <item name="android:textcolor">#0000ff</item> 设定文本颜色 7 </style> 8 </resources> 界面布局设计好后, 现在对相关按钮添加事件监听, 单击选择日期按钮, 能够弹出 日期选择对话框, 选择好日期后, 能够将日期显示在文本编辑框内, 关键代码如下 程序清单 :codes\07\ Memento\src\iet\jxufe\cn\android \MainActivity 1 choosedate.setonclicklistener(new OnClickListener() { 2 public void onclick(view v) { 3 Calendar c = Calendar.getInstance(); 获取当前日期 4 new DatePickerDialog(MainActivity.this, 日期选择器对话框 5 new DatePickerDialog.OnDateSetListener() { 日期改变监听器 6 public void ondateset(datepicker view, int year, 设置文本编辑框的内 7 int month, int day) { 容为设置的日期,month 8 date.settext(year + "-" + (month + 1) + "-"+ day); 需要 从 0 开始, 所以月 9 } 份为 month+1 10 }, c.get(calendar.year), c.get(calendar.month), 11 c.get(calendar.day_of_month)).show(); 12 } 13 }); 下面为添加和查询两个按钮添加事件处理, 所有的数据都已具备, 下面就是如何将 数据写入数据库了, 首先我们需要写一个自己的数据库工具类, 该类继承于 SQLiteOpenHelper, 并重写它的 oncreate() 和 onupdate() 方法, 数据库创建时, 会调用 oncreate() 方法, 因此, 我们将建表语句放在里面, 详细代码如下 程序清单 :codes\07\ Memento\src\iet\jxufe\cn\android \ MyDatabaseHelper.java 1 public class MyDatabaseHelper extends SQLiteOpenHelper { 2 final String CREATE_TABLE_SQL= 3 "create table memento_tb(_id integer primary " + 4 "key autoincrement,subject,body,date)"; 创建 memento 表的 5 public MyDatabaseHelper(Context context, String SQL 语句 name,cursorfactory factory, int version) { 6 super(context, name, factory, version); 构造方法 7 } 8 public void oncreate(sqlitedatabase db) { 9 db.execsql(create_table_sql); 执行建表语句, 创建 10 } memento 表 11 public void onupgrade(sqlitedatabase db, int oldversion, 12 int newversion) { 13 System.out.println(" "+oldVersion+" >"+newVersion); 14 } 15 } 然后通过该工具类, 获取数据库, 并进行添加和查询操作, 关键代码如下 159

97 程序清单 :codes\07\ Memento\src\iet\jxufe\cn\android \MainActivity 1 private class MyOnClickListerner implements OnClickListener { 2 public void onclick(view v) { 3 mydbhelper = new MyDatabaseHelper(MainActivity.this, "memento.db", null, 1); 4 创建数据库辅助类 5 SQLiteDatabase db = mydbhelper.getreadabledatabase(); 获取 SQLite 数据库 6 String substr = subject.gettext().tostring(); 获取主题编辑框的内容 7 String bodystr = body.gettext().tostring(); 获取内容编辑框的内容 8 String datestr = date.gettext().tostring(); 获取时间编辑框的内容 9 switch (v.getid()) { 10 case R.id.add: 单击的是添加按钮 11 title.setvisibility(view.invisible); 设置表头不可见 12 addmemento(db, substr, bodystr, datestr); 调用添加记录方法 13 Toast.makeText(MainActivity.this, " 添加备忘录成功!", 1000).show(); 14 result.setadapter(null); 下拉列表内容为空 15 break; 16 case R.id.query: 单击的是查询按钮 17 title.setvisibility(view.visible); 设置表头可见 18 Cursor cursor = querymemento(db, substr, bodystr, datestr); 调用查询方法 19 SimpleCursorAdapter resultadapter = new SimpleCursorAdapter( 将查询结果显示在下拉 20 MainActivity.this, R.layout.result, cursor, 列表中, 注意一一对应 21 new String[] { "_id", "subject", "body", "date" }, 22 new int[] { R.id.memento_num, R.id.memento_subject, 23 R.id.memento_body, R.id.memento_date }); 24 result.setadapter(resultadapter); 设置下拉列表的内容 25 break; 26 default: 27 break; 28 } 29 } 30 } 向数据库中插入和查询记录的方法如下 1 public void addmemento(sqlitedatabase db, String subject, String body, String date) { 2 db.execsql("insert into memento_tb values(null,?,?,?)", new String[] { 3 subject, body, date }); 执行插入操作 4 this.subject.settext(""); 添加数据后, 将所有的文本编 5 this.body.settext(""); 辑框的内容设为空 6 this.date.settext(""); 7 } 8 public Cursor querymemento(sqlitedatabase db, String subject, String body,string date) { 9 Cursor cursor = db.rawquery("select * from memento_tb where subject like? and body like? and date like?",new String[] { "%" + subject + "%", "%" + body + "%","%" + date 10 + "%" }); 执行查询操作, 提供模糊查询功能 11 return cursor; 12 } 事件监听器写好后, 为按钮注册单击事件处理器, 代码如下 160

98 1 MyOnClickListerner myonclicklisterner = new MyOnClickListerner(); 2 add.setonclicklistener(myonclicklisterner); 3 query.setonclicklistener(myonclicklisterner); 操作完成后, 在结束 Activity 前关闭数据库 代码如下 1 protected void ondestroy() { 2 if(mydbhelper!=null){ 3 mydbhelper.close(); 4 } 5 } 当程序第一调用 getreadabledatabase() 方法后,SQLiteOpenHelper 会缓存已创建的 SQLiteDatabase 实例, 多次调用 getreadabledatabase() 方法得到的都是同一个 SQLitedatabase 实例, 即正常情况下,SQLiteDatabase 实例会维持数据库的打开状态, 因此在结束前应关闭数据库, 否则会占用内存资源 在上面程序中, 我们使用了 SimpleCursorAdapter 封装 Cursor, 从而在下拉列表中显示结果记录信息, 这里需注意,SimpleCursorAdatper 封装 Cursor 时要求底层数据表的主键列名为 _id, 因为 SimpleCursorAdapter 只能识别列名为 _id 的主键, 否则会出现 java.lang.illegalargumentexception: column _id does not exist 错误 程序运行后, 打开 DDMS 视图, 查看 File Explorer 面板, 发现在我们应用程序的包下, 生成了一个 databases 文件夹, 下面有一个 memento.db 文件, 如图 7-14 所示, 该文件即我们在程序中创建的数据库文件 图 7-14 数据库文件的存放位置 数据库文件位于 /data/data/ 应用程序所在包 /databases/ 文件夹下, 可通过 DDMS 工具将该文件夹下的数据库导出来, 然后下载具体的图形化界面进行查看 在 Android SDk 的 tool 目录下提供了一个简单的数据库管理工具, 类似于 MySQL 提供的命令行窗口 如果你将 tool 目录添加到了环境变量, 则只需通过命令行进入到数据库文件所在的目录, 输入如下命令, 即可打开数据库 sqlite3 数据库名 ; 如果你没有将该目录添加到环境变量, 则需要进入 Android SDK 安装目录下的 tool 目录, 然后输入如下命令 : sqlite3 数据库所在目录绝对路径 / 数据库名, 即可打开数据库, 打开数据库后可执行相应的 sql 语句进行增删查改 如图 7-15 所示 注意 : 通过命令行查看数据库内容时, 中文在命令行上会显示乱码 161

99 问题与讨论 162 图 7-15 通过命令行查看 SQLite 数据库内容 (1) 数据库的创建过程是怎么样的? 当不存在数据库时, 直接查找记录会不会出错? 答 :Android 系统在调用 SQLiteOpenHelper 的 getreadabledatabase() 方法时会判断系统中是否已存在数据库, 如果不存在, 系统会创建数据库文件, 因此查找记录时不会出错, 只不过查询结果为空 但若我们在创建数据库时, 没有指定表结构, 添加或查询时会出错. (2) 数据库的后缀名有要求吗? 答 : 后缀名可任意 7.4 使用 ContentProvider 实现数据共享 随着我们手机上应用的增多, 往往在不同的应用之间需要共享数据, 比如现在有一个短信群发的应用, 用户需要选择收件人, 一个个手机号码输入当然可以达到目的, 但是比较麻烦, 并且很少有人会记住所有联系人的号码 这时候就需要获取联系人应用的数据, 然后从中选择收件人即可 对于应用之间数据的共享, 我们可以在一个应用中直接操作另一个应用所记录的数据, 比如前面所学的文件 SharedPreferences 或数据库等, 但这不仅需要应用程序提供相应的权限, 而且还必须知道应用程序中数据存储的细节, 不同应用程序记录数据的方式差别也很大, 不利于数据的交换 针对这种情况, Android 提供了 ContentProvider, 它是不同应用程序间共享数据的标准 API, 用统一的方法访问各类数据 ContentProvider 简单介绍 ContentProvider 是不同应用程序之间进行数据交换的标准 API, 为存储和读取数据提供了统一的接口 ; 通过 ContentProvider, 应用程序可以实现数据共享 ;Android 内置的 许多应用都使用 ContentProvider 向外提供数据, 供开发者调用 ( 如视频 音频 图片 通讯录等 ), 其中最典型的应用就是通讯录 那么 ContentProvider 是如何对外提供数据的呢, 又是如何实现这一机制的呢? ContentProvider 以某种 URI 的形式对外提供数据, 数据以类似数据库中表的方式暴露,

100 允许其他应用访问或修改数据, 其他应用程序使用 ContentResolver 根据 URI 去访问操作指定的数据 URI 是通用资源标识符, 即每个 ContentProvider 都有一个唯一标识的 URI, 其他应用程序的 ContentResolver 根据 URI 就知道具体解析的是哪个 ContentProvider, 然后调用相应的操作方法, 而 ContentResolver 的方法内部实际上是调用该 ContentProvider 的对应方法, 而 ContentProvider 方法内部是如何实现的, 其他应用程序是不知道具体细节的 只是知道有哪一个方法 这就达到了统一接口的目的 对于不同的数据的存储方式, 该方法内部的实现是不同的, 而外部访问方法都是一致的 ContentProvider 也是 Android 四大组件之一, 如果要开发自己的 ContentProvider 必须实现 Android 系统提供的 ContentProvider 基类, 并且需要在 AndroidManifest.xml 文件中进行配置 ContentProvider 基类的常用方法简介 : public abstract boolean oncreate(): 该方法在 ContentProvider 创建后调用, 当其他应用程序第一次访问 ContentProvider 时,ContentProvider 会被创建, 并立即调用该方法 ; public abstract Cursor query(uri uri, String[] projection, String selection, String[] selectionargs, String sortorder): 根据 Uri 查询符合条件的全部记录, 其中 projection 是所需要获取的数据列 ; public abstract int update(uri uri, ContentValues values, String select, String[] selectargs): 根据 Uri 修改 select 条件所匹配的全部记录 ; public abstract int delete(uri uri, String selection, String[] selectionargs): 根据 Uri 删除符合条件的全部记录 ; public abstract Uri insert(uri uri, ContentValues values): 根据 Uri 插入 values 对应的数据,ContentValues 类似于 map, 存放的是键值对 ; public abstract String gettype(uri uri): 该方法返回当前 Uri 所代表的数据的 MIME 类型 如果该 Uri 对应的数据包含多条记录, 则 MIME 类型字符串应该以 vnd.android.curor.dir/ 开头, 如果该 Uri 对应的数据只包含一条记录, 则 MIME 类型字符串应该以 vnd.android.cursor.item/ 开头 上面几个方法都是抽象方法, 开发自己的 ContentProvider 时, 必须重写这些方法, 然后在 AndroidManifest.xml 文件中配置该 ContentProvider, 为了能让其他应用找到该 ContentProvider,ContentProvider 采用了 authorities( 主机名 / 域名 ) 对它进行唯一标志, 你可以把 ContentProvider 看作是一个网站,authorities 就是它的域名, 只需在 <application.../> 元素内添加以下代码即可 1 <provider android:name=".myprovider" 指定 ContentProvider 类 2 android:authorities="iet.jxufe.cn.android.provider.myprovider"> 域名 3 </provider> 注意 :authorities 是必备属性, 如果没有 authorities 属性会报错 一旦某个应用程序通过 ContentProvider 暴露了自己的数据操作接口, 那么不管该应用程序是否启动, 其他应用程序都可通过该接口来操作该应用程序的内部数据 163

101 7.4.2 ContentProvider 操作常用类介绍 上节介绍 ContentProvider 时涉及几个知识点 : Uri ContentResolver ContentValues, 本节将详细介绍这几个类的作用和用法 Uri 代表了要操作的数据,Uri 主要包含了两部分信息 : 164 需要操作的 ContentProvider; 对 ContentProvider 中的什么数据进行操作 一个 Uri 组成如图 7-16 所示 : content://iet.jxufe.cn.android.providers.personprovider/person/10 scheme 主机名或 authority 路径 图 7-16 Uri 的组成部分 scheme:contentprovider( 内容提供者 ) 的 scheme 已经由 Android 所规定为 :content://; 主机名 ( 或 Authority): 用于唯一标志这个 ContentProvider, 外部调用者可以根据这个标志来找到它 路径 ( 或资源 ): 用于确定我们要操作该 ContentProvider 中的什么数据, 一个 ContentProvider 内可能包含多种数据, 路径的构建应根据业务而定, 例如操作通信录应用中的数据, 可构建以下路径 要操作 person 表中 id 为 10 的记录, 可以构建这样的路径 :/person/10; 要操作 person 表中 id 为 10 的记录的 name 字段,person/10/name; 要操作 person 表中的所有记录, 可以构建这样的路径 :/person; 要操作 xxx 表中的记录, 可以构建这样的路径 :/xxx; ID: 该部分是可选的, 用于指定操作的具体是哪条记录, 如果没有设置, 则操作的是所有记录 要操作的数据不一定来自数据库, 也可以是文件 xml 或网络等其他存储方式, 例如要操作 xml 文件中 person 节点下的 name 节点, 可以构建这样的路径 :/person/name 上面我们所构建的都是字符串, 如果要把一个字符串转换成 Uri, 可以使用 Uri 工具类中的 parse() 静态方法, 用法如下 1 Uri uri = Uri.parse("content://iet.jxufe.cn.android.providers.personprovider/person"); 由于 Uri 代表了要操作的数据, 所以我们经常需要解析 Uri, 并从 Uri 中获取数据 Android 系统为我们提供了两个用于操作 Uri 的工具类, 分别为 UriMatcher 和 ContentUris UriMatcher 类用于匹配 Uri, 主要用法如下 (1) 注册所有需要匹配的 Uri 路径, 代码如下 : ID

102 1 UriMatcher myuri= new UriMatcher(UriMatcher.NO_MATCH); 2 // 创建 UriMather 对象, 常量 UriMatcher.NO_MATCH 表示不匹配任何路径的返回码 3 // 该常量值为 myuri.adduri( iet.jxufe.cn.providers.myprovider, person, 1); 5 // 添加需匹配的 Uri, 如果 match() 方法匹配 content://iet.jxufe.cn.providers.myprovider/ 6 //person 路径, 返回匹配码为 1 7 myuri.adduri( iet.jxufe.cn.providers.myprovider, person/#, 2); 8 // 添加需匹配的 Uri,# 号为通配符, 表示匹配任何 ID 的 Uri, 如果匹配则返回 2, 9 // 如如果 match() 方法匹配 content://iet.jxufe.cn.providers.myprovider/person/230 路径, 返回匹配码为 2 (2) 注册完需要匹配的 Uri 后, 就可以使用 myuri.match(uri) 方法对输入的 Uri 进行匹配, 如果匹配就返回匹配码, 匹配码是调用 adduri() 方法传入的第三个参数, 假设匹配 content:// iet.jxufe.cn.providers.myprovider /person 路径, 返回的匹配码为 1 ContentUris 类用于获取 Uri 路径后面的 ID 部分, 它有两个比较实用的方法 : withappendedid(uri, id) 方法用于为路径加上 ID 部分, 用法如下 1 Uri uri = Uri.parse("content://iet.jxufe.cn.providers.myprovider/person") 2 Uri resulturi = ContentUris.withAppendedId(uri, 10); 3 // 生成后的 Uri 为 :content:// iet.jxufe.cn.providers.myprovider/person/10 parseid(uri) 方法用于从路径中获取 ID 部分, 用法如下 1 Uri uri = Uri.parse("content:// iet.jxufe.cn.providers.myprovider/person/10") 2 long personid = ContentUris.parseId(uri); 获取的结果为 :10 ContentProvider 的作用是暴露可供操作的数据, 其他应用程序主要通过 ContentReso- lver 来操作 ContentProvider 所暴露的数据,ContentResolver 相当于我们的客户端 ContentResolver 是一个抽象类, 主要提供了以下几个方法 insert(uri url, ContentValues values): 向 Uri 对应的 ContentProvider 中插入 values 对应的数据 ; delete(uri url, String where, String[] selectionargs) : 删除 Uri 对应的 ContentProvider 中符合条件的记录 ; update(uri uri, ContentValues values, String where, String[] selectionargs): 用 vaules 值更新 Uri 对应的 ContentProvider 中符合条件的记录 ; query(uri uri, String[] projection, String selection, String[] selectionargs, String sortorder): 查询 Uri 对应的 ContentProvider 中符合条件的记录 ; ContentResolver 是一个抽象类, 是不能直接实例化的, 那么我们如何得到 ContentResolver 实例呢?Android 中 Context 类提供了 getcontentresolver() 方法用于获取 ContentResolver 对象 然后即可调用其增删查改方法进行数据操作 一般来说, 当多个应用程序通过 ContentResolver 来操作 ContentProvider 提供的数据 165

103 时,ContentResolver 调用的数据操作将会委托给同一个 ContentProvider 对象 ( 或者实例 ) 处理 这种设计形式, 也被称之为单例模式, 即 ContentProvider 在整个过程中只有一个实例 ContentValues 类和 Java 中的 Hashtable 类比较相似, 都是负责存储一些键值对, 但是它存储的键值对当中的键是一个 String 类型, 往往是数据库的某一字段名, 而值都是一些简单的数据类型 当我们向数据库中插入一条记录时, 可以将这条信息的各个字段值放入 ContentValues, 然后将该 ContentValues 直接插入数据库 而不用拼接 SQL 语句或使用占位符一一赋值 ContentProvider 应用实例 在 Android 系统中, 内置了许多应用, 部分应用也采用了 ContentProvider 向外提供数据, 最典型的就是通讯录应用, 下面我们就来演示如何通过 ContentResolver 获取通讯录应用的联系人信息, 并向其中添加联系人 Android 系统对联系人管理 ContentProvider 的 Uri 如下 ContactsContract.Contacts.CONTENT_URI: 管理联系人的 Uri; ContactsContract.CommonDatakinds.Phone.CONTENT_URI: 管理联系人电话的 Uri 有了这些 Uri 后, 就可以在应用程序中通过 ContentResolver 去操作系统的联系人数据了 程序运行效果如图 7-17 所示, 单击添加按钮后, 将输入的用户名和手机号添加到联系人应用中, 单击显示所有联系人号码, 能够读取所有的联系人信息, 如图 7-18 所示 166 图 7-17 程序运行首界面 图 7-18 单击显示所有联系人按钮后的界面 界面布局相对简单, 在此不再列出, 在此只列出两个按钮的事件处理的关键代码 向通讯录中添加联系人的事件处理代码, 由于通讯录中用户名和号码存放于不同的表中, 是根据联系人 ID 号关联起来的 因此, 我们先向联系人中添加一个空的记录, 产生新的 ID 号, 然后根据 ID 号分别在两张表中插入相应的数据 具体代码如下

104 程序清单 :codes\07\ AccessContacts\src\iet\jxufe\cn\android \MainActivity 1 public void addperson() { 添加联系人 2 String namestr = name.gettext().tostring(); 获取联系人姓名 3 String numstr = num.gettext().tostring(); 获取联系人号码 4 ContentValues values = new ContentValues(); 创建一个空的 5 ContentValues // 向 RawContacts.CONTENT_URI 插入空值, 目的是获取返回的 ID 号 6 Uri rawcontacturi = resolver.insert(rawcontacts.content_uri, values); 7 long contactid = ContentUris.parseId(rawContactUri); 得到新联系人的 ID 号 8 values.clear(); 清空 values 的内容 9 values.put(data.raw_contact_id, contactid); 设置 ID 号 10 values.put(data.mimetype, StructuredName.CONTENT_ITEM_TYPE); 设置类型 11 values.put(structuredname.given_name, namestr); 设置姓名 12 resolver.insert(android.provider.contactscontract.data.content_uri,values); 向联系人 Uri 添加联系人名字 13 values.clear(); 14 values.put(data.raw_contact_id, contactid); 设置 ID 号 15 values.put(data.mimetype, 设置类型 Phone.CONTENT_ITEM_TYPE); 16 values.put(phone.number, numstr); 设置号码 17 values.put(phone.type, Phone.TYPE_MOBILE); 设置电话类型 18 resolver.insert(android.provider.contactscontract.data.content_u 向联系人电话号码 Uri RI,values); 添加电话号码 19 Toast.makeText(MainActivity.this, " 联系人数据添加成功!", 1000).show(); 20 } 获取通讯录中所有联系人的姓名和手机号时, 首先查询出所有的联系人姓名和他的 ID 号, 然后根据 ID 号查询电话号码表中的号码, 再将每个人的信息放在同一个 map 对象 中, 最后将这个 map 对象添加到列表中, 作为结果返回 程序得到列表后将其与下拉列 表控件相关联, 从而将数据有规律的显示在我们的界面上 程序清单 :codes\07\ AccessContacts\src\iet\jxufe\cn\android \MainActivity 1 public ArrayList<Map<String, String>> queryperson() { 2 // 创建一个保存所有联系人信息的列表, 每项是一个 map 对象 3 ArrayList<Map<String, String>> detail = new ArrayList<Map<String, String>>(); 4 Cursor cursor = resolver.query(contactscontract.contacts.content_uri, 5 null, null, null, null); 查询通讯录中所有联系人 6 while (cursor.movetonext()) { 循环遍历每一个联系人 7 Map<String, String> person = new HashMap<String, String>(); 每个联系人信息用一个 map 对象存储 8 String personid = cursor.getstring(cursor 9 获取联系人 ID 号.getColumnIndex(ContactsContract.Contacts._ID)); 10 String name = cursor.getstring(cursor 获取联系人姓名 11.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); 12 person.put("id", personid); 将获取到的信息存入 map 13 person.put("name", name); 14 Cursor nums = resolver.query( 对象中 167

105 ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, 16 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" 17 + personid, null, null); 根据 ID 号, 查询手机号码 18 if(nums.movetonext()){ 19 String num = nums.getstring(nums.getcolumnindex(contactscontract. 20 CommonDataKinds.Phone.NUMBER)); 21 person.put("num",num); 将手机号存入 map 对象中 22 } 23 nums.close(); 关闭资源 24 detail.add(person); 25 } 26 cursor.close(); 关闭资源 27 return detail; 返回查询列表 28 } 方法写好后, 我们需要在相应的事件处理中调用该方法, 代码如下 1 MyOnClickListener myonclicklistener = new MyOnClickListener(); 创建事件监听器 2 add.setonclicklistener(myonclicklistener); 注册事件监听器 3 query.setonclicklistener(myonclicklistener); 注册事件监听器 自定义的事件处理器, 针对不同事件调用不同的方法 代码如下 1 private class MyOnClickListener implements OnClickListener { 2 public void onclick(view v) { 3 switch (v.getid()) { 4 case R.id.add: 5 addperson(); break; 6 case R.id.show: 7 title.setvisibility(view.visible); 8 ArrayList<Map<String,String>> persons=queryperson(); SimpleAdapter adapter=new SimpleAdapter(MainActivity.this,persons, 9 R.layout.result, new String[]{"id","name","num"},new int[]{r.id.personid, R.id.personname,R.id.personnum}); 10 result.setadapter(adapter); break; 11 default: break; 12 } 13 } 14 } 注意 : 本程序需要读取 添加联系人信息, 因此需要在 AndroidManifest.xml 文件中 为该应用程序授权, 授权代码如下 1 <uses permission android:name="android.permission.read_contacts"/> 读的权限 2 <uses permission android:name="android.permission.write_contacts"/> 写的权限 上面程序介绍了如何使用 ContentResolver 来操作系统 ContentProvider 提供的数据, 下面, 我们继续学习如何开发自己的 ContentProvider, 即将自己的应用数据通过 ContentProvider 提供给其他应用 开发自己的 ContentProvider 主要经历两步 : 开发一个 ContentProvider 子类, 该子类需要实现增 删 查 改等方法 ; 在 AndroidManifest.xml 文件中配置该 ContentProvider 下面以一个具体的示例演示如何创建自己的 ContentProvider, 我们为备忘录示例

106 创建 ContentProvider, 使得其他应用程序可以访问和修改它的数据 首先我们定义一个常量类, 把备忘录的相关信息以及 Uri 通过常量的形式进行公 开, 提供访问该 ContentProvider 的一些常用入口, 代码如下 1 public class Mementos { 2 public static final String AUTHORITY = "iet.jxufe.cn.providers.memento"; 3 public static final class Memento implements BaseColumns { 4 public static final String _ID = "_id"; memento_tb 表中 _id 字段 5 public static final String SUBJECT = "subject"; memento_tb 表中 subject 字段 6 public static final String BODY = "body"; memento_tb 表中 body 字段 7 public static final String DATE = "date"; memento_tb 表中 date 字段 8 public static final Uri MEMENTOS_CONTENT_URI= Uri.parse("content://" 9 + AUTHORITY + "/mementos"); 提供操作 mementos 集合 URI 10 public static final Uri MEMENTO_CONTENT_URI= Uri.parse("content://" 11 + AUTHORITY + "/memento"); 提供操作单个 mementouri 12 } 13 } 然后, 为该应用添加 ContentProvider, 继承系统中的 ContentProvider 基类, 重写里 面的抽象方法, 具体代码如下 1 public class MementoProvider extends ContentProvider { 2 private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 3 private static final int MEMENTOS = 1; 定义两个常量, 用 4 private static final int MEMENTO = 2; 于匹配 URI 的返回值 5 MyDatabaseHelper dbhelper; 6 SQLiteDatabase db; 7 static { 8 matcher.adduri(mementos.authority, "mementos", MEMENTOS); 添加 URI 匹配规 9 matcher.adduri(mementos.authority, "memento/#", MEMENTO); 则, 用于判断 URI 的类型 10 } 11 public boolean oncreate() { 12 dbhelper = new MyDatabaseHelper(getContext(), "memento.db", null,1); 13 db = dbhelper.getreadabledatabase(); 创建数据库工具 14 return true; 类, 并获取数据库实例 15 } 16 public Uri insert(uri uri, ContentValues values) { 添加记录 17 long rowid = db.insert("memento_tb", Mementos.Memento._ID, values); 18 if (rowid > 0) { 如果添加成功, 则 19 Uri mementouri = ContentUris.withAppendedId(uri, rowid); 通知数据库记录发 20 getcontext().getcontentresolver().notifychange(mementouri, null); 生更新 21 return mementouri; 22 } 23 return null; 24 } 25 public int update(uri uri, ContentValues values, String selection, 26 String[] selectionargs) { 更新记录 27 int num = 0; 28 switch (matcher.match(uri)) { 169

107 29 case MEMENTOS: 30 num = db.update("memento_tb", values, selection, selectionargs); 31 break; 32 case MEMENTO: 33 long id = ContentUris.parseId(uri); 34 String where = Mementos.Memento._ID + "=" + id; 35 if (selection!= null &&!"".equals(selection)) { 36 where = where + " and " + selection; 37 } 38 num = db.update("memento_tb", values, where, selectionargs); 39 break; 40 default: 41 throw new IllegalArgumentException(" 未知 Uri:" + uri); 42 } 43 getcontext().getcontentresolver().notifychange(uri, null); 44 return num; 45 } 46 public Cursor query(uri uri, String[] projection, String selection, 47 String[] selectionargs, String sortorder) { 48 switch (matcher.match(uri)) { 49 case MEMENTOS: 50 return db.query("memento_tb", projection, selection, selectionargs, 51 null, null, sortorder); 52 case MEMENTO: 53 long id = ContentUris.parseId(uri); 54 String where = Mementos.Memento._ID + "=" + id; 55 if (selection!= null &&!"".equals(selection)) { 56 where = where + " and " + selection; 57 } 58 return db.query("memento_tb", projection, where, selectionargs, null, null, sortorder); default: 61 throw new IllegalArgumentException(" 未知 Uri:" + uri); 62 } 63 } public String gettype(uri uri) { 66 switch (matcher.match(uri)) { 67 case MEMENTOS: 68 return "vnd.android.cursor.dir/mementos"; 69 case MEMENTO: 70 return "vnd.android.cursor.item/memento"; 71 default: 72 throw new IllegalArgumentException(" 未知 Uri:" + uri); 73 } 74 } 75 } 至此, 我们的 ContentProvider 就已经开发好了, 下面将我们的 ContentProvider 在 170 Manifest.xml 文件中进行注册, 代码如下

108 1 <provider android:name=".mementoprovider" 2 android:authorities="iet.jxufe.cn.providers.memento"> 3 </provider> 现在, 我们就可以写一个应用程序来访问我们开发的 ContentProvider 了 应用程序运行界面如图 7-19 图 7-20 所示, 和前面备忘录界面类似, 在此不再详述 图 7-19 程序运行界面图 图 7-20 显示所有记录的效果图 在本应用程序中, 并没有创建自己备忘录数据库, 而是访问 MementoContent 通过 ContentProvider 所提供的数据, 下面只列出事件处理的关键代码, 代码如下 171

109 1 add.setonclicklistener(new OnClickListener() { 2 public void onclick(view v) { 3 ContentValues values = new ContentValues(); 创建一个 4 values.put(mementos.memento.subject, subject.gettext().tostring()); ContentValues 5 values.put(mementos.memento.body, body.gettext().tostring()); 对象 6 values.put(mementos.memento.date, date.gettext().tostring()); values 中存值 7 contentresolver.insert(mementos.memento.mementos_content_uri,values); 8 Toast.makeText(MainActivity.this, " 添加生词成功!", 1000).show(); 9 } 10 }); 11 show.setonclicklistener(new OnClickListener() { 12 public void onclick(view v) { 13 Cursor cursor = contentresolver.query( 14 Mementos.Memento.MEMENTOS_CONTENT_URI, null, null,null, null); 15 SimpleCursorAdapter resultadapter = new SimpleCursorAdapter( 查询所有 16 MainActivity.this, R.layout.result, cursor, 记录 17 new String[] { Mementos.Memento._ID, 18 Mementos.Memento.SUBJECT, 19 Mementos.Memento.BODY, Mementos.Memento.DATE }, 20 new int[] { R.id.memento_num, R.id.memento_subject, 21 R.id.memento_body, R.id.memento_date }); 22 result.setadapter(resultadapter); 设置数据 23 } }); 的显示方式 7.5 获取网络资源 由于手机的计算能力 存储能力都比较有限, 它通常是作为移动终端来使用, 具体的数据处理是交给网络服务器来进行的, 而它主要的优势在于携带方便, 因此, 获取网络资源非常重要 Android 完全支持 JDK 本身的 TCP UDP 网络通信, 也支持 JDK 提供的 URL URLConnection 等通信 API 除此之外,Android 还内置了 HttpClient, 可方便地发送 HTTP 请求, 并获取 HTTP 响应 本节简单介绍通过 URL 如何获取网络资源, 至于 Android 客户端如何与服务器端交互, 将在第 11 章详细阐述 URL(Uniform Resource Locator) 对象代表统一资源定位器, 用于指定网络上某一资源, 该资源既可以是简单的文件或目录, 也可以是对复杂对象的引用 通常 URL 由协议名 主机 端口和资源组成 格式为 :protocol://host:port/resourcename, 如 iet.jxufe.cn/index.html URL 类提供了获取协议 主机名 端口号 资源名等方法, 详细描述可查看 API, 此外还提供了 openstream() 方法, 可以读取该 URL 资源的 InputStream, 通过该方法可以非常方便地读取远程资源 172 下面通过一个简单的例子, 示范如果通过 URL 类读取远程资源 该示例用于获取网络上的一张图片, 并显示在 ImageView 中, 程序运行效果如图 7-21 所示

110 图 7-21 获取网络图片运行效果 程序清单 :codes\07\ AccessURL\src\iet\jxufe\cn\android \MainActivity 1 public class MainActivity extends Activity { 2 private ImageView myimg; 3 private Handler myhandler; 4 private Bitmap bitmap; 5 public void oncreate(bundle savedinstancestate) { 6 super.oncreate(savedinstancestate); 7 setcontentview(r.layout.activity_main); 8 myimg = (ImageView) findviewbyid(r.id.myimg); 9 myhandler=new Handler(){ 10 public void handlemessage(message msg) { 11 if(msg.what==0x1122){ 12 myimg.setimagebitmap(bitmap); 13 } 14 } 15 }; 16 new Thread(){ 17 public void run(){ 18 try{ 19 URL url = new URL(" +"img/baidu_sylogo1.gif"); 20 获取百度首页图片 21 InputStream is = url.openstream(); 22 bitmap = BitmapFactory.decodeStream(is); 23 is.close(); 24 }catch(exception ex){ 25 ex.printstacktrace(); 26 } 27 myhandler.sendemptymessage(0x1122); 28 } 29 }.start(); 30 } 31 } 注意 :Android2.3 以后开始提供了一个新的类 StrictMode, 该类可以用于捕捉发生在应用程序主线程中耗时的磁盘 网络访问或函数调用, 可以帮助开发者改进程序, 使主线程处理 UI 和动画在磁盘读写和网络操作时变得更平滑, 避免主线程被阻塞 而 173

111 2.3 以下版本则不支持该类的 如果直接在主程序中处理网络连接操作, 在 2.3 版本中以后会抛出 NetworkOnMainThreadException 异常, 而在之前的版本是则不会 因此, 本程序采用子线程来处理一些网络连接操作, 这样所有版本都适用 注意 : 想要获取网络资源, 还必须添加访问网络的许可权限, 权限如下 <uses-permission android:name="android.permission.internet"/> 访问 internet 权限 同样的通过这种方式还可以获取网页等其他资源, 都是通过流的方式获取的, 但需注意的是通过这种方式获取的网页是 html 的源代码, 这并不是我们所想要的, 在 Android 中为我们提供了一个 WebView 控件, 可解析 html 源代码 下面就演示如何获取网页资源, 程序运行结果使用 TextView 显示如图 7-22 所示, 使用 WebView 显示如图 7-23 所示 图 7-22 TextView 显示的 Html 源代码 图 7-23 WebView 显示的 Html 网页 程序关键代码如下 1 myhandler = new Handler() { 2 public void handlemessage(message msg) { 3 if (msg.what == 0x1122) { 4 //result_show.settext(result); 使用 TextView 显示结果 5 show.loaddatawithbaseurl(null, result, "text/html", "utf-8",null); 6 } 7 } 8 }; 9 new Thread() { 10 public void run() { 11 try {

112 12 URL httpurl = new URL(" 13 HttpURLConnection conn = (HttpURLConnection) httpurl.openconnection(); 14 conn.setconnecttimeout(5 * 1000); 设置连接超时 15 conn.setrequestmethod("get"); 以 get 方式发起请求,GET 16 if (conn.getresponsecode()!= 200) 一定要大写 17 throw new RuntimeException(" 请求 url 失败 "); 18 InputStream istream = conn.getinputstream(); 得到网络返回的输入流 19 result = readdata(istream, "utf-8"); 20 conn.disconnect(); 21 myhandler.sendemptymessage(0x1122); 22 } catch (Exception ex) { 23 ex.printstacktrace(); 24 } 25 }.start(); 26 public static String readdata(inputstream insream, String charsetname) 27 throws Exception { 获取网络资源 28 ByteArrayOutputStream outstream = new ByteArrayOutputStream(); 29 byte[] buffer = new byte[1024]; 30 int len = -1; 31 while ((len = insream.read(buffer))!= -1) { 32 outstream.write(buffer, 0, len); 33 } 34 byte[] data = outstream.tobytearray(); 将字节输出流转为字节数组 35 outstream.close(); 关闭字节输出流 36 insream.close(); 关闭输入流 37 return new String(data, charsetname); 返回获取的内容, 网页源 38 } 代码 上述程序主要是为了演示 WebView 可以解析 HTML 代码, 实际上要实现上述功能可 直接加载 Url, 而不用先获取源码, 然后再将源码转换成对应的页面, 代码如下 1 show.loadurl(" 注意 : 将字节数组转换成字符串时, 需指定编码格式, 如果网页中包含中文, 而 编码格式不正确则会出现中文乱码 编码格式可通过查看网页中编码方式来指定, 本程 序中, 百度首页采用的是 UTF-8 的编码格式 7.6 本章小结 本章主要讲解了 Android 中数据存储的几种方式, 从简单的通过流的形式读取手机内存以及 SD 卡上的文件, 到 Android 中为我们提供的用于保存用户个性化设置 程序参数的 SharedPreferences 工具类, 再到用于保存比较复杂的, 有一定结构关系的数据库 Android 系统内置了一个小型的关系型数据库 :SQLite, 且为访问 SQLite 数据库提供了 大量方便的工具类 除此之外, 为了方便应用程序之间数据共享, 而又不用知道应用程序内部操作数据的细节,Android 系统提供了不同应用程序之间交换数据的标准 API:ContentProvider ContentProvider 是 Android 四大组件之一, 开发者只需要继承系统的 ContentProvider 基 类, 然后重写里面的部分方法即可开发自己的 ContentProvider, 最后将 ContentProvider 175

113 在 Manifest.xml 文件中进行配置, 其他应用程序即可通过 ContentResolver 来访问或修改该应用的数据 最后, 我们还简单地介绍了如何获取网络上的资源, 一些较为复杂的与服务器端的交互将会在后面章节详细介绍 通过本章的学习, 读者应熟练掌握 SQLite 的操作, 以及 ContentProvider 的原理和开发 课后练习 1.Android 中数据存储主要包含哪五种方式? 2.SQLite 允许把各种类型的数据保存到任何类型字段中, 开发者可以不用关心声明该字段所使用的数据类型 ( 对 / 错 ) 3. 注册 ContentProvider 组件时, 必须要指定 android:authorities 属性的值 ( 对 / 错 ) 4. 通过 openfileoutput(string name, int mode) 读取手机上文件时, 若第二个参数传值为 3, 表示该文件 ( ) A) 是私有数据, 只能被应用本身访问 B) 可以被其他应用读取 C) 可以被其他应用写入 D) 既可以被其他应用读取也能被其他应用写入 5.SharedPreferences 数据以 格式保存在手机上 ( ) A) xml B) txt C) json D) 根据用户自定义 6. 以下数据类型中, 哪个不是 SQLite 内部支持的类型 ( ) A) NULL B) INTEGER C) STRING D) TEXT 7.ContentProvider 的作用是暴露可供操作的数据, 其他应用则通过 来操作 ContentProvider 所暴露的数据 ( ) A)ContentValues B)ContentResolver C)URI D)Context 8. 关于 ContenValues 类说法正确的是 ( ) A) 它和 Hashtable 比较类似, 也是负责存储一些键值对, 但是它存储的名值对当中的名是 String 类型, 而值都是基本类型 B) 它和 Hashtable 比较类似, 也是负责存储一些键值对, 但是它存储的名值对当中的名是任意类型, 而值都是基本类型 C) 它和 Hashtable 比较类似, 也是负责存储一些键值对, 但是它存储的名值对当中的名, 可以为空, 而值都是 String 类型 D) 它和 Hashtable 比较类似, 也是负责存储一些键值对, 但是它存储的名值对当中的名是 String 类型, 而值也是 String 类型 176

114 本章要点 第 12 章综合案例 校园通 校园通案例需求概述 校园通程序结构 案例功能模块分析 界面设计 关键代码解释 注意事项 本章知识结构图 财大通 本章示例 学校生活 出行指南 游玩南昌 号码百事通 校区平面图 校园风景 新生指南 我的位置 线路查询 地点查询 景点列表 添加号码 查询号码 ImageView Gallery Spinner ImageSwitcher 百度地图 位置服务 ListView 下拉列表 SQLite 数据库 ExpandableListView 扩展下拉列表 ListView 下拉列表 257

115 通过前面章节的学习, 我们已经掌握了 android 开发的基础知识, 本章我们将通过一个综合案例将前面所学的知识串联起来, 共同开发一个实用的应用程序 本章将带着大家从零开始开发一个 财大通 应用程序, 该应用主要是为在校学生服务, 方便学生迅速查找信息, 包括学校生活 出行指南 游玩南昌 号码百事通四个模块 本案例所涉及的知识包括 : 基本界面控件的使用, 如 TextView Button ImageView 等 高级界面控件的使用, 如 :Spinner Gallery ListView ExpandableListView 菜单等 Activity 之间的跳转与数据的传递 事件处理, 如单击事件 触摸事件 选择事件等 SQLite 数据库的使用 位置服务与百度地图 通过本章的学习, 读者将对这些知识有更深入的了解, 并且能够自主开发一些小的应用 12.1 校园通 概述 校园通 应用软件主要是为江西财经大学的新生 老生 老师以及对江西财经大学感兴趣的人服务的, 提供一个信息服务平台, 方便他们迅速查找相应信息, 主要包括学校生活 出行信息 游玩南昌 号码百事通四个模块 学校生活主要介绍学校的基本情况, 包括校区平面图 校园风景 新生指南等, 对于即将入学的新生以及校外人士了解财大情况非常有帮助 ; 出行信息包括我的位置 公交线路查询 位置查询等 ; 游玩南昌包括南昌主要景点介绍 ; 号码百事通包括号码的查询和添加 校园通 应用程序的功能模块图如图 12-1 所示 258 校园通 校区平面图 学校生活 校园风景 新生指南 游玩南昌 景点介绍 我的位置 出行指南 线路查询 线路查询 添加号码 号码百事通 查询号码 图 12-1 校园通功能结构图

116 12.2 校园通 应用程序结构 校园通 应用程序涉及的功能和界面较多, 为了方便对代码的管理, 为每个功能模块单独建立一个包, 将功能相关的 Activity 放在同一包下, 校园通 应用程序的程序结构如图 12-2 所示 项目名称 Java 源代码号码百事通模块 Android 版本 运行主 Activity 学校生活模块 出行指南模块数据库操作类 游玩南昌模块 引用的类库 存放生成的中间文件和最后的安装文件 图 12-2 校园通应用程序结构图 12.3 校园通 应用程序功能模块 资源目录 存放图片 存放界面布局文件 存放菜单文件 存放一些常量信息 Android 清单文件 项目属性文件 校园通 应用程序, 主要包含四个模块, 程序运行的主界面及界面分析如图 12-3 所示 单击学校生活 出行指南 游玩南昌 号码百事通等按钮后, 能够跳转到相应的功能模块 259

117 260 按钮, 与父容器左对齐, 单击后跳转到学校生活模块 按钮, 与父容器左对齐, 单击后跳转到游玩南昌模块 主界面的布局文件关键代码如下 图 12-3 校园通应用程序主界面分析 垂直线性布局 ImageView 相对布局 按钮, 与父容器右对齐, 单击后跳转到出行指南模块 TextView, 设置背景图片 相对布局 按钮, 与父容器右对齐, 单击后跳转到出行指南模块 TextView 程序清单 :codes\12\ CampusAssist\res\values\activity_main.xml 1 <LinearLayout...> 总体是垂直线性布局 2 <ImageView.../> 空白 ImageView 3 <ImageView 显示财大标记的 ImageView 4 android:src="@drawable/logo".../> 5 <ImageView.../> 空白 ImageView 6 <RelativeLayout...> 相对布局 7 <Button android:layout_alignparentleft="true".../> 学校生活按钮, 与父容器左对齐 8 <Button android:layout_alignparentright="true".../> 出行指南按钮, 与父容器右对齐 9 </RelativeLayout> 10 <TextView.../> 显示中间的财大通文字 11 <RelativeLayout...> 相对布局 12 <Button android:layout_alignparentleft="true".../> 游玩南昌按钮, 与父容器左对齐 13 <Button android:layout_alignparentright="true"/> 号码百事通按钮与父容器右对齐 14 </RelativeLayout> 15 <TextView.../> 显示校训文字 16 </LinearLayout>

118 单击各个按钮后能够跳转到相应的功能模块, 事件处理的关键代码如下 程序清单 :codes\12\ CampusAssist\src\iet\jxufe\cn\android\MainActivity.java 1 public class MainActivity extends Activity { 2 private Button phoneassist,trafficassist,campuslife,scenery; 声明 Button 类型变量 3 public void oncreate(bundle savedinstancestate) { 4 super.oncreate(savedinstancestate); 5 setcontentview(r.layout.activity_main); 设置界面布局 6 phoneassist=(button)findviewbyid(r.id.phoneassist); 根据 ID 找到控件 7 trafficassist=(button)findviewbyid(r.id.trafficassist); 8 campuslife=(button)findviewbyid(r.id.campuslife); 9 scenery=(button)findviewbyid(r.id.scenery); 10 MyOnClickListener myonclicklistener=new MyOnClickListener(); 创建监听器对象 11 phoneassist.setonclicklistener(myonclicklistener); 为按钮添加事件监听器 12 trafficassist.setonclicklistener(myonclicklistener); 13 campuslife.setonclicklistener(myonclicklistener); 14 scenery.setonclicklistener(myonclicklistener); 15 } 16 public class MyOnClickListener implements OnClickListener{ 内部类实现事件监听器 17 Intent intent=null; 18 public void onclick(view v) { 单击事件处理方法 19 switch (v.getid()) { 判断事件源 20 case R.id.phoneAssist: 21 intent=new Intent(MainActivity.this,PhoneListActivity.class); 跳转到号码百事通 22 break; 23 case R.id.trafficAssist: 24 intent=new Intent(MainActivity.this,ChuxingxinxiActivity.class); 跳转到出行指南 25 break; 26 case R.id.campusLife: 27 intent =new Intent(MainActivity.this,CampusLifeActivity.class); 跳转到校园生活 28 break; 29 case R.id.scenery: 30 intent=new Intent(MainActivity.this,SceneryActivity.class); 跳转到游玩南昌 31 break; 32 default:break; 33 } 34 startactivity(intent); 35 } 36 } 37 } 在主界面中, 有四个按钮都需要进行单击事件处理, 因此可创建一个内部类来实现事件监听器, 所有的按钮注册到同一个事件监听器上, 单击事件发生后, 通过判断事件源从而进行不同的处理 下面分别对各个模块的功能进行详细分析与介绍 261

119 学校生活模块 学校生活主要介绍学校的基本情况, 包括校区平面图 校园风景以及新生指南三部分 程序结构图以及各个 Activity 之间的跳转关系如图 12-4 所示 262 校区平面图 Activity 学校生活主 Activity 校园风景 Activity 新生指南信息 Activity 新生指南 Activity CampusBuildActivity 跳转 FreshAssistActivity 图 12-4 学校生活模块程序结构 学校生活模块运行主界面及分析如图 12-5 所示 图 12-5 学校生活主界面分析 CampusLifeActivity 总体垂直线性布局, 设置背景图片, 内容垂直居中并右对齐, 设置右边距 20dp 按钮, 单击后跳转到校区平面图 按钮, 单击后跳转到校园风景 按钮, 单击后跳转新生指南 CampusSceneryActivity 跳转 DetailInfoActivity 按钮, 单击后返回到财大通首界面 界面布局相对简单, 总体使用垂直线性布局, 设置背景图片, 对齐方式为垂直居中并右对齐 android:gravity="center_vertical right", 并设置右边距为 20dp, 即 paddingright = 20 dp 按钮的事件处理与前面主界面上的按钮事件处理类似, 在此不给出相应代码 可查看光盘中 codes\12\ CampusAssist\src\iet\jxufe\cn\android\ CampusLifeActivity.java 单击校区平面图按钮后, 跳转到 CampusBuildActivity, 界面运行效果如图 所示 该界面中包含一个 Spinner 下拉列表 一个 ImageView 以及一个返回按钮, 选择 Spinner 中的某一项后能在 ImageView 中显示对应的图片, 由于图片比较大, 因此为图片 跳转

120 添加了触摸事件, 能够拖动以查看图片其他部分 图 12-6 校区平面图运行界面 图 12-7 麦庐校区显示效果 界面整体采用垂直线性布局, 相对比较简单, 在此不列出代码, 在此界面中, 存在 三种事件处理, 一种是下拉列表的选择事件, 一种是图片的触摸事件还有一种是返回按 钮的单击事件 关键代码如下所示 下拉列表选择事件关键代码如下 程序清单 :codes\12\ CampusAssist\src\iet\jxufe\cn\ android\ CampusBuildActivity.java 1 int [] imageids=new int[]{r.drawable.jiaotong,r.drawable.jiaoqiaoxiaoqu, 2 R.drawable.mailuxiaoqu,R.drawable.fenglinxiaoqu}; 图片 ID 数组 3 String[] xiaoqu=new String[]{" 交通示意图 "," 蛟桥校区 "," 麦庐校区 "," 列表内容枫林校区 "}; 4 protected void oncreate(bundle savedinstancestate) { 5 super.oncreate(savedinstancestate); 6 setcontentview(r.layout.campus_build); 7 myspinner = (Spinner)findViewById(R.id.spinner); 根据 ID 找到下拉列表 8 myimage=(imageview)findviewbyid(r.id.myimage); 根据 ID 找到 ImageView 9 ArrayAdapter<String> adapter=new ArrayAdapter<String>(this, 10 android.r.layout.simple_dropdown_item_1line,xiaoqu); 设置样式和内容 11 myspinner.setadapter(adapter); 为 Spinner 设置 Adapter 12 myspinner.setonitemselectedlistener(new 选中事件监听器 OnItemSelectedListener() { 13 public void onitemselected(adapterview<?> arg0, View arg1,int position, long id) { 14 myimage.setimageresource(imageids[position]); 根据选择显示图片 15 } 16 public void onnothingselected(adapterview<?> arg0) { 17 myimage.setimageresource(imageids[0]); 默认显示第一张图片 18 } 19 }); 263

121 264 图片的触摸事件处理代码如下 1 myimage.setontouchlistener(new OnTouchListener() { 匿名内部类实现触摸事件 2 public boolean ontouch(view v, MotionEvent event) { 监听器 3 float curx,cury; 触摸事件发生的坐标 4 switch (event.getaction()) { 5 case MotionEvent.ACTION_DOWN: 6 mx=event.getx(); 7 my=event.gety(); 8 break; 9 case MotionEvent.ACTION_MOVE: 10 curx=event.getx(); 11 cury=event.gety(); 12 myimage.scrollby((int)(mx-curx), (int)(my-cury)); 13 mx=curx; 14 my=cury; 15 break; 16 case MotionEvent.ACTION_UP: 17 curx=event.getx(); 18 cury=event.gety(); 19 myimage.scrollby((int)(mx-curx), (int)(my-cury)); 20 break; 21 default: 22 break; 23 } 24 return true; 25 } 26 }); 触摸事件的原理是 : 记录按下时的坐标以及移动后的坐标, 根据这两个坐标的距离来移动整张图片 在触摸事件中存在三种状态 : 按下 移动 松开, 因此需对触摸事件的状态进行判断, 然后再做具体的操作 单击校园风景按钮后, 跳转到 CampusSceneryActivity, 界面运行效果如图 12-8 所示 在此界面中包含三种控件 :Gallery ImageSwitcher 以及按钮, 整体采用垂直线性布局, 布局中对齐方式为水平居中 ImageSwitcher 图片选择器, 主要用于显示图片, 相对于 ImageView 而言, 它在图片切换时能添加一些动态效果 Gallery 是画廊, 是存放图片的列表, 选择某一张图片是时, 该图片能够突出显示, 而其他为选择的图片则以半透明的形式显示, 开发者可自由设置未选中图片的透明度以及图片间的间距等 由于 Gallery 没选中一张图片时, 会单独创建一个 ImageView 对象, 内存消耗比较大, 因此逐渐被淘汰了, 不推荐使用, 可用 HorizontalScrollView 或 PageView 代替 在此只是为了显示效果, 对内存要求不高, 所以选择 Gallery

122 图 12-8 校园风景运行效果图 总体垂直线性布局, 设置背景图片, 内容水平居中 ImageSwitcher Gallery, 选择某一图片后 ImageSwitcher 中会显示该图片 按钮, 单击后返回到财大通首界面 该界面中单击 Gallery 中的某一张图片时,ImageSwitcher 中的图片就会相应的进行变 化 关键代码如下 codes\12\campusassist\src\iet\jxufe\cn\android\campussceneryacti- vity.java 1 switcher.setfactory(new ViewFactory(){ 为 Switcher 创建 2 public View makeview(){ 图片, 并设置效果 3 ImageView imageview = new ImageView(CampusSceneryActivity.this); 4 imageview.setscaletype(imageview.scaletype.fit_center); 5 imageview.setlayoutparams(new ImageSwitcher.LayoutParams( 6 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 7 return imageview; 8 } }); 9 switcher.setinanimation(animationutils.loadanimation(this,android.r 设置图片切换的.anim.fade_in)); 动画 : 淡入淡出 10 switcher.setoutanimation(animationutils.loadanimation(this,android.r.anim.fade_out)); 11 BaseAdapter adapter = new BaseAdapter(){ 下拉列表对应的内容信息 12 public int getcount(){ 获取下拉列表的选项数 13 return Integer.MAX_VALUE; 设置最大值, 从 14 } 而循环显示图片 15 public Object getitem(int position){ 16 return position; 返回选项所对应 17 } 18 public long getitemid(int position){ 19 return position; 的位置 265

123 } 21 public View getview(int position, View convertview, ViewGroup parent){ 22 ImageView imageview = new ImageView(CampusSceneryActivity.this); 23 imageview.setimageresource(images[position % images.length]); 24 imageview.setscaletype(imageview.scaletype.fit_xy); 25 imageview.setlayoutparams(new Gallery.LayoutParams(75, 100)); 26 TypedArray typedarray = obtainstyledattributes(r.styleable.gallery); 27 imageview.setbackgroundresource(typedarray.getresourceid( 28 R.styleable.Gallery_android_galleryItemBackground, 0)); 29 return imageview; 30 } 31 }; 32 gallery.setadapter(adapter); 33 gallery.setonitemselectedlistener(new OnItemSelectedListener() { 34 public void onitemselected(adapterview<?> arg0, View arg1,int position, long id) { 35 switcher.setimageresource(images[position%images.length]); 选择事件处理 36 } 37 public void onnothingselected(adapterview<?> arg0) { 38 } 39 }); 单击新生指南按钮后, 跳转到 FreshAssistActivity, 界面运行效果如图 12-9 所示 该界面采用垂直线性布局, 包含八个按钮, 居中显示, 单击某一按钮后跳转到 DetailInfoActivity, 显示详细信息, 如图 所示 图 12-9 新生指南主界面 图 新生指南详细信息 新生指南主界面中包含 8 个按钮, 每个按钮都需要添加事件处理, 一个个添加比较麻烦, 此处采用数组存储每个按钮的 ID, 然后循环遍历数组, 取出 ID, 并根据 ID 找到对应的按钮, 并为其注册事件监听器 事件处理后显示的信息虽然不同, 但是结构一

124 致, 因此, 此处采取数组存储数据, 将需要显示的内容存储到 Intent 中, 从而达到动态改变的效果, 而不用为每部分单独建立一个 Activity 显示内容, 大大减少了 Activity 的数量, 关键代码如下 程序清单 :codes\12\ CampusAssist\src\iet\jxufe\cn\android\ FreshAssistActivity.java 1 protected void oncreate(bundle savedinstancestate) { 2 super.oncreate(savedinstancestate); 3 setcontentview(r.layout.fresh_assist); 4 int[] btnids = new int[] { R.id.woshi, R.id.xuezhang, R.id.zhengli,R.id.dida, 5 R.id.jiejiao, R.id.qinlian, R.id.shenghuo,R.id.ruxiao }; 定义按钮对应的 ID 数组 6 Button[] btns = new Button[btnIds.length]; 创建对应长度的按钮数组 7 myonclicklistener mylistener = new myonclicklistener(); 创建事件监听器对象 8 for (int i = 0; i < btns.length; i++) { 9 btns[i] = (Button) findviewbyid(btnids[i]); 遍历数组根据 ID 得到按钮 10 btns[i].setonclicklistener(mylistener); 为每个按钮注册事件监听 11 } 器 12 } 注意 : 上面代码中的 btnids 定义必须放在 setcontentview(r.layout.fresh_assist); 之后, 因为只有加载了 fresh_assist.xml 文件之后, 才会有对应的按钮 ID 单击事件监听器的实现类关键代码如下 1 private class myonclicklistener implements OnClickListener { 2 Intent intent = new Intent(FreshAssistActivity.this,DetailInfoActivity.class); 3 public void onclick(view v) { 4 switch (v.getid()) { 5 case R.id.zhengli: 6 intent.putextra("info", info[0]); 7 break; 8 case R.id.dida: 9 intent.putextra("info", info[1]); 10 break; 11 其他匹配项, 在此不再列出 12 default: 13 break; 14 } 15 startactivity(intent); 16 } 代码中 Info 是一个字符串数组, 用于存放需要传递的字符串信息 对于不同的按 钮, 传递的数据不同, 但接收数据的 Activity 是一致的, 所以在 switch 语句外面创建 Intent 对象 出行指南模块 出行指南主要包括获取当前的位置信息 查找公交路线信息, 以及搜索一些关键地点的位置 程序结构图以及各个 Activity 之间的跳转关系如图 所示 267

125 268 出行指南 Activity 线路查询 Activity 位置查询 Activity 我的位置 Activity WozainaActivity 图 出行指南模块程序结构 跳转 GuanjiandianActivity GongjiaoluxianActivity 跳转跳转 ChuxingxinxiActivity 出行指南模块运行主界面如图 所示 单击线路查询按钮后, 跳转到 GongjiaoluxianActivity, 调用百度地图 API, 在界面中输入某一公交路线, 会在地图上显示出该公交路线的站点信息, 如图 所示, 显示南昌的 232 路公交路线, 单击缩小和放大可以缩放地图 ; 单击我的位置按钮后, 调用百度地图 API, 并能够在地图上用一个图片标记当前位置, 如图 所示 ; 单击位置查询后, 调用百度地图, 并在地图上标记出与之相关的位置信息, 如图 所示, 标记的是南昌与财大相关的位置信息 图 出行指南模块程序结构图 图 公交路线查询结果

126 图 我的位置显示图 图 百度地图搜索财大的结果 以上功能模块中都涉及百度地图, 先对其进行简要介绍 首先在网上下载百度地图的相关 API, 并申请使用 API 的 Key 百度地图网址: index.php, 进入网页后选择 android, 如图 所示 图 百度地图首页截图 选择 Android 平台后, 可下载相关 jar 包 技术文档, 申请使用 API 的 Key, 查看开发 269

127 流程等, 如图 所示 270 开发流程介绍 申请使用 API 的 Key 常见问题解答 图 android 版百度地图 百度地图相关类介绍 API 包 技术文档下载 单击 Key 申请进入百度地图 API Key 申请页面 如图 所示 应用名称, 可任意 对你应用的简单描述 图 申请百度地图 API 的 Key 单击按钮生成 API Key 生成的 API Key 注意 : 获取 API 密钥时, 前提是你已经登录, 所以若没有百度账号, 需注册一个账号, 然后再进行申请 生成的 API Key 需要进行保存, 在后面开发百度地图相关应用中需要用到

128 有了百度地图的 API Key 和相关 Jar 包, 我们就可以开发自己的应用了, 开发步骤如下 : (1) 在项目中添加相关的 Jar 包, 将下载的 jar 包中的 baidumapapi.jar 放在项目中的 libs 目录下, 然后在 libs 目录下, 建立一个 armeabi 文件夹, 然后将 libbmapapiengine_v1_3_3.so 复制到该工程目录下 ; (2) 由于要调用百度地图的相关数据, 因此需要添加相应的权限, 那么究竟需要添加哪些权限? 我们可以通过查看下载的百度地图的示例文件, 从它的 AndroidManifest. xml 中进行拷贝即可, 或者运行时根据提示信息一个个进行添加 ; (3) 在布局文件中添加地图控件 ; 1 <com.baidu.mapapi.mapview 百度地图提供的地图控件, 完整的包名 + 类名 2 android:id="@+id/bmapview" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:clickable="true" /> 这样我们的准备工作就完成了, 下面就需要针对具体要实现的功能调用相应的 API 先简要介绍下, 百度地图中几个比较核心的类库 类的名称与 Google 地图的相似 MapActivity: 该类是用于显示地图的 Activity 类, 是一个抽象类, 任何想要显示 MapView 的 Activity 都需要派生自 MapActivity, 并且在 oncreate() 中, 都要创建一个 MapView 实例 MapView: 用于显示地图的 View 组件 它派生自 ViewGroup, 它必须和 MapActivity 配合使用, 而且只能被 MapActivity 创建, 这是因为 MapView 需要通过后台的线程来连接网络或文件系统, 这些线程要由 MapActivity 来管理 当 MapView 获取的焦点时, 它将捕捉按键和触摸手势, 自动地平移和缩放地图, 还可以在地图上绘制许多 Overlay 类型标记 BMapManager: 地图引擎管理类, 用于初始化 开启和停止地图 MapController: 用于控制地图的移动 缩放等的工具类 Overlay: 是一个可显示在地图之上的可绘制的对象, 常用于绘制标记 如果需要在地图上标注一些图标文字等信息, 就需要使用 Overlay 添加一个 overlay 时, 从这个基类派生出一个子类, 创建一个实例, 然后把它加入到一个列表中 这个列表通过调用 MapView.getOverlays() 得到 GeoPoint: 表示一个地理坐标点, 存放经度和纬度, 以微度的整数形式存储 (1 微度 =10-6 度 ) MKSearch: 用于位置检索 周边检索 范围检索 公交检索 驾乘检索 步行检索 开发百度地图应用的一些基本步骤如下 271

129 272 程序清单 :codes\12\ CampusAssist\src\iet\jxufe\cn\android\ FreshAssistActivity.java 1 public class GongjiaoluxianActivity extends MapActivity { 2 private MapView mapview; MapView 用于显示地图 3 private BMapManager bmapmanager; 地图管理类 4 private MapController mc; 地图控制类 5 private MKSearch mksearch; 用于位置检索 周边检索 范围检索 公交检索 步行检索等 6 private String keystring = " "EE93B5F3ECE5E3E38DE04547AED6DBDA D"; 申请的 API Key( 字符串中的 Key 根据各人申请的代码修改 ) 7 private EditText bus,city; 城市和线路信息 8 private Button search; 搜索按钮 9 public void oncreate(bundle savedinstancestate) { 10 super.oncreate(savedinstancestate); 11 setcontentview(r.layout.gongjiaoluxian); 布局文件中只有一个 MapView 控件 12 bus=(edittext)findviewbyid(r.id.bus); 获取要查询的线路 13 search=(button)findviewbyid(r.id.search); 14 mapview = (MapView) findviewbyid(r.id.bmapview); 15 city=(edittext)findviewbyid(r.id.city); 获取查询的城市 16 bmapmanager = new BMapManager(this); 创建地图管理类的对象 17 bmapmanager.init(keystring, new MKGeneralListener() { 初始化地图管理类 18 public void ongetpermissionstate(int arg0) { 19 if (result == 300) { 返回授权验证错误,300 表示验证失败 20 Toast.makeText(GongjiaoluxianActivity.this, " 验证不通过, 请重新输入!", 21 Toast.LENGTH_SHORT).show(); 22 } 23 } 24 public void ongetnetworkstate(int result) { 返回网络错误 25 } 26 }); 27 initmapactivity(bmapmanager); 初始化 MapActivity 28 mapview.setbuiltinzoomcontrols(true); 设置地图可放大 缩小 29 mc=mapview.getcontroller(); 获取地图控制对象 30 mc.setzoom(15); 设置缩放级别为 15 在上述代码中,bMapManager 对象初始化时, 需要传递两个参数, 第一个参数为申请的授权验证码, 即我们申请的 API Key, 第二个参数为注册回调事件, 用于获取验证 错误信息或网络错误信息 通过上述这段代码, 我们才可以在模拟器中显示地图信息了, 并可以进行缩放 移动, 默认是以北京天安门为中心显示信息 需要注意的是, 还必须要在 androidmanifest.xml 文件中添加相应的权限信息

130 1 <uses-permission android:name="android.permission.internet" /> 2 <uses-permission android:name="android.permission.access_network_state" /> 3 <uses-permission android:name="android.permission.access_fine_location" /> 4 <uses-permission android:name="android.permission.write_external_storage" /> 5 <uses-permission android:name="android.permission.access_wifi_state" /> 6 <uses-permission android:name="android.permission.change_wifi_state" /> 7 <uses-permission android:name="android.permission.read_phone_state" /> 下面我们实现查询公交路线功能, 为搜索按钮添加事件处理 主要是调用 MKSearch 类来实现公交检索, 关键代码如下 1 search.setonclicklistener(new OnClickListener() { 为搜索按钮添加事件 2 public void onclick(view v) { 监听器 3 mksearch = new MKSearch(); 创建 MKSearch 对象 4 mksearch.init(bmapmanager,new MyMKSearchListener()); 初始化 MKSearch 对象 5 mksearch.poisearchincity(city.gettext().tostring().trim(), 传入两个参数, 城市和 6 bus.gettext().tostring().trim()); 公交路线 7 } 8 }); MkSearch 类对象初始化时传入两个参数, 地图管理对象以及 MKSearchListener 监听 器, 该监听器用于返回 poi 搜索 ( 位置 关键点搜索 ) 公交搜索 驾乘路线和步行路线 结果 poisearchincity() 方法用于城市 poi 搜索, 将会自动调用 MKSearchListener 中的 ongetpoiresult(), 在该方法中会返回所有的 poi 信息, 然后我们获取公交路线的 poi 信 息, 再调用 MKSearch 的 buslinesearch() 根据 poi 信息来搜索公交的详细信息, 并绘制路 线标记, 关键代码如下所示 1 public class MyMKSearchListener implements MKSearchListener{ 2 public void ongetwalkingrouteresult(mkwalkingrouteresult result, 3 int arg1) { 返回步行路线搜索结 4 } 果 5 public void ongettransitrouteresult(mktransitrouteresult result, 6 int arg1) { 返回公交搜索结果 7 } 8 public void ongetsuggestionresult(mksuggestionresult arg0, int arg1) { 返回搜索结果 9 } 10 public void ongetpoiresult(mkpoiresult result, int type, int ierror) { 返回 poi 搜索结果 11 if (result == null ierror!= 0) { result - 搜索结果 ierror 12 Toast.makeText(GongjiaoluxianActivity.this, - 错误号,0 表示正确返 13 " 对不起, 没有相应结果 ", 回 1000).show(); 14 return; 15 } 16 MKPoiInfo mkpoiinfo=null; 搜索的 Poi 信息 17 int mkpoinum=result.getnumpois(); 得到的 Poi 信息个数 18 for(int i=0;i<mkpoinum;i++){ 循环获取所有的 Poi 19 mkpoiinfo=result.getpoi(i); 信息 20 if(mkpoiinfo.epoitype==2){ 2 表示公交路线 21 break; 22 } 23 } 273

131 24 mksearch.buslinesearch(city.gettext().tostring().trim(), mkpoiinfo.uid); 搜索公交详细信息 25 } 26 public void ongetdrivingrouteresult(mkdrivingrouteresult result,int arg1) { 27 } 返回驾乘路线搜索结果 28 public void ongetbusdetailresult(mkbuslineresult result, 返回公交车详情信 int ierror) { 息搜索结果 29 if (result == null ierror!= 0) { 30 Toast.makeText(GongjiaoluxianActivity.this, 31 " 对不起, 没有相应结果 ",1000).show(); 32 return; 33 } 34 RouteOverlay routeoverlay=new RouteOverlay(GongjiaoluxianActivity.this, mapview); 35 routeoverlay.setdata(result.getbusroute()); 为标记设置数据 36 mapview.getoverlays().clear(); 清空原有标记 37 mapview.getoverlays().add(routeoverlay); 添加路线标记 38 mapview.invalidate(); 刷新地图 39 mapview.getcontroller().animateto(result.getbusroute().getstart()); 定位到起点 40 } 41 public void ongetaddrresult(mkaddrinfo arg0, int arg1) { 返回地址信息搜索结 42 } 果 43 } 主要流程是 : 查询城市的所有 poi 信息, 然后对 poi 信息进行遍历, 获取公交路线 poi 信息, 最后根据搜索公交路线的详细信息 整个流程如图 所示 274 开发地图的一般步骤 提示验证错误信息并退出 继承 MapActivity 获取 MapView 对象 创建 BMapManager 对象 初始化 BMapManager 否 网络和验证是否通过 是 初始化 MapActivity 获取 MapController 对象 设置缩放等级 刷新地图 绘制路线标记 buslinesearch() 搜索路线详细信息 获取公交路线 poi 信息 poisearchincity() 搜索城市 poi 信息 初始化 MKSearch 对象, 需要指定监听器 创建 MKSearch 对象 为搜索按钮添加事件处理 图 百度地图开发的一般流程

132 302 附录 附录 1:Android 开发工具及书中所用代码 下述编程环境及教材中所涉及代码均可从网站下载 ( ) 一 文件夹 : AndroidEnv 含编程需要环境和开发工具 文件夹名称 文件夹位置 作用 JavaJDK1.6 AndroidEnv\javaJDK java 运行环境 adt-bundle-windows-x zip AndroidEnv\Android4.4 Android4.4 版的三合一安装包 ADT AndroidEnv\ADT Eclipse 上开发 Android 的插件 AndroidSDK4.1 AndroidEnv\AndrodSDK Android 开发工具 Eclipse4.2 AndroidEnv\ Eclipse4.2 集成开发工具 二 文件夹 :Codes 含书本中的详细代码 工程名称 功能简介 文件夹位置 HelloAndroid 安卓第一个应用 : 输出 HelloAndroid Codes\01\HelloAndroid TextViewTest 登录界面 ( 三种基本组件 :TextView Codes\02\TextViewTest EditText Button) 的综合应用 Calculate 计算器 布局的综合应用 Codes\02\ Calculate ImageTest 图片按钮动态改变图标 Codes\03\ ImageTest GalleryTest 画廊视图和图片切换器的综合应用 Codes\03\ GalleryTest ListViewTest 自动列表提示框 简单下拉列表 Codes\03\ ListViewTest ListTest 简单的列表展示 Codes\03\ ListTestTest SimpleAdapterTest 模拟 QQ 列表显示举例 Codes\03\SimpleAdapterTest ExpandableListView QQ 好友分组, 扩展下拉列表 Codes\03\ ExpandableListView DialogTest 常见对话框举例 Codes\03\ DialogTest MenuTest 菜单使用的综合案例 Codes\03\MenuTest TextEditor 文本编辑编辑器 事件监听的使用 Codes\04\ TextEditor CallbackEventTest 基于回调的事件处理机制 Codes\04\ CallbackEventTest EventTransferTest 事件传播机制介绍 Codes\04\ EventTransferTest EventBinding 布局文件中为组件添加单击事件的处 Codes\04\ EventBinding 理方法 HandlerTest 动态生成随机数 Handler 消息传 Codes\04\ HandlerTest 递举例 AsyncTaskTest 下载进度条 异步消息处理机制 Codes\04\ AsyncTaskTest ActivityLifeCycleTest 模拟 Activity 的生命周期 Codes\05\ ActivityLifeCycleTest RegisterTest 注册案例 Activity 间的数据传递 Codes\05\ RegisterTest DailTest 电话拨号器 Intent 调用系统功能 Codes\05\ DailTest SendMessage 短信发送器 Codes\05\ SendMessage Bitmap_AnimationTest 范简单图片和逐帧动画 ( 骏马奔腾效 Codes\06\ Bitmap_AnimationTest 果 ) AnimationTest ImageView 用于显示背景和逐帧动画 Codes\06\ AnimationTest

133 工程名称 功能简介 文件夹位置 CanvasTest 自定义绘图的程序 Codes\06\ CanvasTest FileTest 读取手机文件 Codes\07\ FileTest SaveLoginInfo 保存用户登录信息 ( 保存密码 自动 Codes\07\ SaveLoginInfo 登录 ) SharedPreferences 使用方法 SDCardFileTest SD 卡文件的写入与读取 Codes\07\ SDCardFileTest ReadOtherSharedPreferences 访问程序中的 SharedPreferences 数据 Codes\07\ ReadOtherSharedPreferences Memento 备忘录 数据库的写入与读取 Codes\07\ Memento MementoContent ContentProvider 共享备忘录数据 Codes\07\ MementoContent MementoResolver 访问备忘录共享的数据 Codes\07\ MementoResolver AccessContacts 使用 ContentProvider 访问系统通讯录 Codes\07\ AccessContacts AccessURL 获取网络资源 Codes\07\ AccessURL AccessHtml WebView 显示的 Html 网页 Codes\07\ AccessHtml FirstService Service 中常用方法简介 Codes\08\ FirstService AIDLClient 跨进程调用数据的客户端 Codes\08\ AIDLClient ADILServer 提供数据的服务端 Codes\08\ ADILServer SendMessage 短信发送器 ( 调用系统服务 ) Codes\08\ SendMessage MusicPlayer 音乐播放器 Codes\09\ MusicPlayer OrderedBroadcastTest 有序广播示例 Codes\09\ OrderedBroadcastTest LocationService 实时提示位置信息 Codes\10\ LocationService GoogleMapTest GoogleMapAPI 地图的应用 Codes\10\GoogleMapTest NavigationTest GPS 定位, 动态获取当前位置信息 Codes\10\NavigationTest InfoSearch 高校信息查询平台 Android 客户端 Codes\11\ InfoSearch InfoSearchServer 高校信息查询平台服务器端 Codes\11\ InfoSearchServer CampusAssist 财大 校园通 应用程序综合实例 Codes\12\CampusAssist 303

134 326 附录 6:Android 中部分常见错误与程序调试方法 Android 是基于 Java 语言的, 因此一些简单的语法错误在编译时会自动提示, 开发 者根据提示信息就能很快的修正 然而编译时正常, 并不能表示程序能够正常运行, 在 运行时可能会出现运行时异常导致程序强制退出, 还有一种隐蔽性错误即程序能够正常 运行, 但结果却和我们期望的不一致, 也就是所谓的逻辑错误 下面我们主要针对后两 种情景的解决方案进行简单介绍 一 程序调试的工具 1. LogCat 工具介绍 在 Android 中, 为开发者提供了一个记录日志的 Log 类, 使用 Log 类可以在程序代 码中加入一些 记录点, 并可以通过 Eclipse 中的 LogCat 工具来查看记录 当程序每 次执行到 记录点 时, 相应的 记录点 就会在 LogCat 中输出一条信息 开发者通过 分析这些记录, 就可以检查程序执行的过程是否与我们期望的相符合 依此来判断程序 代码中可能出错的区域, 以便准确定位 在默认的 Eclipse 编辑窗口中, 并没有显示提供 Logcat 工具, 需要开发者从 Eclipse 的窗口中调出来, 具体操作为 : Eclipse 菜单选择 Windows Show View Other Android LogCat, 在控制台窗口出现 LogCat 工具 LogCat 工具各部分含义如 附图 6-1 所示 保存的过滤器 输出的日志信息 信息级别 清空日志 级别时间进程 ID 线程 ID 应用名标记文本内容 导出日志 显示保存的过滤器 锁定滚动条 附图 6-1 LogCat 工具各部分含义 默认情况下,LogCat 中显示的信息比较多, 为了显示自己所需要的信息, 可以对信

135 息进行过滤 添加过滤条件的操作如附图 6-2 附图 6-3 所示 添加 编辑删除 附图 6-2 过滤器操作面板 过滤条件 附图 6-3 添加过滤器的面板 过滤器名称 通过日志标记 通过日志消息 通过进程 ID 通过应用名称 通过信息级别 android.util.log 中常见的方法有 :Log.v() Log.d() Log.i() Log.w() and Log.e() 根据 首字母分别对应于 VERBOSE,DEBUG,INFO,WARN,ERROR 信息内容从 ERROR, WARN, INFO, DEBUG, VERBOSE 依次递增, 即 VERBOSE 包含所有的信息,DEBUG 包含 ERROR WARN INFO DEBUG 等信息, 而 ERROR 仅仅包含 ERROR 级别的信息 不同类型的信息 在 LogCat 中显示的颜色也会有所不同, 具体如附表 6-1 所示 附表 6-1 信息级别及对应颜色表 方法颜色消息 Log.v() 黑色任何信息 verbose Log.d() 蓝色调试信息 debug Log.i() 绿色提示信息 information Log.w() 橙色警告信息 warning Log.e() 红色错误信息 error 通常 Log 类中相关的方法需要传递两个参数 : 一个是信息的标记, 即 Tag; 一个是 信息的内容 我们可以通过 Tag 标记进行过滤, 快速的定位到日志信息 注意 : 有时 LogCat 中会不显示任何信息 解决方法 : 在 DDMS devices 视图中选择运行的设备, 或重新打开 LogCat, 或重启 Eclipse 简单示例 :( 控制台打印的日志信息顺序 ) 定义两个类 :Person.java 和 Student.java 327

136 328 Person.java 1 import android.util.log; 2 public class Person { 3 public Person(){// 构造方法 4 Log.i(MainActivity.TAG, "Person Construtor invoked!"); 5 } 6 public void say(){// 自定义方法 7 Log.i(MainActivity.TAG,"Person say() invoked!"); 8 System.out.println("I'm a super class!"); 9 } 10 } Student.java 1 public class Student extends Person { 2 private String name; 3 public Student(){// 午餐构造方法 4 this(" 姓名未知 "); 5 Log.i(MainActivity.TAG, "Student Constructor without argument invoked!"); 6 } 7 public Student(String name){// 带一个参数的构造方法 8 this.name=name; 9 Log.i(MainActivity.TAG, "Student Constructor with a argument invoked!"); 10 } 11 public void say(){// 自定义的方法 12 Log.i(MainActivity.TAG,"Student say() invoked!"); 13 System.out.println("I'm a subclass of Person! My name is "+name); 14 } 15 } 在 MainActivity 类中定义 TAG 常量, 并在 oncreate() 方法中调用相应方法 MainActivity.java 1 public class MainActivity extends Activity { 2 public static final String TAG="LogCatInfoTest"; 3 protected void oncreate(bundle savedinstancestate) { 4 super.oncreate(savedinstancestate); 5 setcontentview(r.layout.activity_main); 6 Person person=new Student();// 多态, 父类引用指向子类对象 7 person.say();// 调用对象方法 8 } 9 } 控制台中日志信息的输出顺序是什么?( 选择可能输出的信息, 并对其进行排序 ) 1Person Construtor invoked! 2Person say() invoked! 3I'm a super class! 4Student Constructor without argument invoked!

137 5Student Constructor with a argument invoked! 6Student say() invoked! 7I'm a subclass of Person! My name is Xxx 结果 : Eclipse 提供的 Debug 功能 和 Java 编程一样, 在 Eclipse 中也可以对 Android 程序进行调试 首先在代码中设置 断点, 当程序执行到断点时, 将会停下来 设置断点的方法有如下几种 1) 双击左边代码所在行的行号, 生成断点标志 ; 2) 鼠标放在代码所在行, 单击右键, 选择第一个 Toggle Breakpoint, 生成断点标志 3) 将光标放在需添加断点的行, 然后按 ctrl+shift+b, 即可生成断点标志 如果想取消相应的断点, 只需重复以上的操作即可 设置好断点后, 运行程序, 此时不再是选择 Run As 而是选择 Debug As 程序会执行 到断点处停止, 并且跳转到 Debug 视图, 如附图 6-4 所示 调试信息 代码区域 变量和断点 附图 6-4 Eclipse 中调试窗口 接下来即可通过调试按钮或快捷键跟踪程序执行过程,Debug 调试的一些快捷键 F11 启动 Debug F5 Step into ( 进入内部执行 ) F6 Step over ( 执行下一步 ) F7 Step Retrun ( 返回 ) F8 执行到最后 329

138 二运行时常见的错误 空指针异常 a 引用类型的变量只有声明 定义, 没有初始化, 默认值为 null 1 public class MainActivity extends Activity { 2 private Button login; 3 protected void oncreate(bundle savedinstancestate) { 4 super.oncreate(savedinstancestate); 5 setcontentview(r.layout.activity_main); 6 login.setonclicklistener(new OnClickListener() { 7 public void onclick(view v) { 8 System.out.println(" 登录按钮被单击了!"); 9 } 10 }); 11 } 12 } 此时, 编译没有任何错误, 但运行时, 会抛出空指针异常! 因为 login 并没有具体为 它赋值 它默认为 null 程序运行结果如附图 6-5 或附图 6-6 所示 附图 6-5 中文状态下强制退出提示 附图 6-6 英文状态下强制退出提示 此时就需要查看控制台中对错误信息的描述, 一般来说, 首先查看错误的开始, 对错误的描述, 例如 : 然后查找 Caused by 语句, 查看是由什么造成的 发现原因后就需要对其进行分析, 为什么会为 null 值, 从而进行相应的修改 下面的修改行不行呢? 为什么? 1 public class MainActivity extends Activity { 2 private Button login=(button)findviewbyid(r.id.login); 3 protected void oncreate(bundle savedinstancestate) {

139 4 super.oncreate(savedinstancestate); 5 setcontentview(r.layout.activity_main); 6 //login=(button)findviewbyid(r.id.login); 7 login.setonclicklistener(new OnClickListener() { 8 public void onclick(view v) { 9 System.out.println(" 登录按钮被单击了!"); 10 } 11 }); 12 } 13 } 此时, 系统仍然会抛出空指针异常, 这是因为 findviewbyid() 方法的作用是通过 Id 从某个布局文件中查找相应的控件, 它的前提是该布局文件已加载 而布局文件的加载 是在 oncreate() 方法中, 而 login 作为成员变量, 是在类加载的时候就执行的, 而 oncreate() 方法是在创建了该类的对象后才会执行 正确做法如下 1 public class MainActivity extends Activity { 2 private Button login; 3 protected void oncreate(bundle savedinstancestate) { 4 super.oncreate(savedinstancestate); 5 setcontentview(r.layout.activity_main); 6 login=(button)findviewbyid(r.id.login); 7 login.setonclicklistener(new OnClickListener() { 8 public void onclick(view v) { 9 System.out.println(" 登录按钮被单击了!"); 10 } 11 }); 12 } 13 } b 根据 findviewbyid() 方法未能找到相应控件 ( 主要针对多个布局文件 ) 1 public class MainActivity extends Activity { 2 private Button login; 3 private Button reset; 4 private EditText name,psd; 5 protected void oncreate(bundle savedinstancestate) { 6 super.oncreate(savedinstancestate); 7 setcontentview(r.layout.activity_main); 8 login=(button)findviewbyid(r.id.login); 9 login.setonclicklistener(new OnClickListener() { 10 public void onclick(view v) { 11 Builder builder=new AlertDialog.Builder(MainActivity.this); 12 builder.settitle(" 欢迎登录 "); 13 View view=getlayoutinflater().inflate(r.layout.login, null); 14 reset=(button)findviewbyid(r.id.reset); 15 name=(edittext)findviewbyid(r.id.name); 16 psd=(edittext)findviewbyid(r.id.psd); 17 reset.setonclicklistener(new OnClickListener() { 18 public void onclick(view v) { 19 name.settext(""); 331

140 psd.settext(""); 21 } 22 }); 23 builder.setview(view); 24 builder.create().show(); 25 } 26 }); 27 } 28 } 默认情况下 Activity 的 findviewbyid() 方法, 会从 setcontentview 方法设置的布局文 件中去查找控件, 但上面的代码中这些控件并不是在 R.layout.activity_main 中, 而是在 R.layout.login 中 上面代码中已经将 R.layout.login 转换成了 View 对象, 此时应调用 View 类的 findviewbyid() 因此只需将上面加粗部分用下面代码替换即可 reset=(button)view.findviewbyid(r.id.reset); name=(edittext)view.findviewbyid(r.id.name); psd=(edittext)view.findviewbyid(r.id.psd); 2. 类型转换异常 Android 中 Activity 类的 findviewbyid() 方法返回值为 View 类型, 在实际应用中我们 经常需要调用具体控件的一些特殊方法, 例如 ImageView 的设置图片,TextView 设置文 本内容等, 而 View 类并没有提供相关的方法, 因此, 需要把 View 对象转化成具体的子 类对象 由父类对象强制转换为子类对象, 在编译时是不会出错的, 但是当程序运行时, 如果具体的对象与你所转换的对象类型不一致, 也不存在父子关系时, 则会抛出类型转 换异常 例如将 ImageView 强制转换成 TextView, 将 TextView 转换为 Button 等 而将 Button 强制转换成 TextView 则不会出错, 因为 TextView 是 Button 的父类, 子类对象可 以赋给父类引用 3. 数组越界异常 数组越界异常也是开发中经常会遇到的异常, 访问时数组的下标从 0 开始, 因此最 大的下标为数组的长度减 1, 如果访问的下标不在这个范围之内, 则抛出数组越界异常 例如循环浏览图片时, 当访问到最后一张时, 如果继续递增则会导致数组越界 对于数 组越界一个比较好的处理方式, 即将数组的下标设置为当前访问的数对数组的长度取模, 这样结果一定在 0~ 数组长度 -1 之间, 不会越界 4. 重复运行程序出现警告

141 当前程序已经运行在前台, 并且程序没有任何更新, 此时重复运行会提示如下警告 Warning: Activity not started, its current task has been brought to the front 即 Activity 没有启动, 因为当前任务已经运行在前台 解决方案 :a 退出程序再运行 ;b 修改程序再运行, 如添加一个空格 5. XML 文件中标签拼写错误 在 Android 开发中, 还会经常遇到 XML 文件中单词拼写错误, 该错误编译时不是提 示 程序运行时, 则会强制退出, 并且 LogCat 中会打印出 android.view.inflateexception: Binary XML file line # : Error inflating class Xxxx. 信息 错误原因 : (1) 引用类名问题即标签的名称写错, 这时候系统根据反射机制找不到相应的类 ; (2) 如果是自定义标签, 那么自定义的类必须实现包含属性的构造方法 ; View(Context context): 仅包含 Context 类型参数的构造方法, 通过这种方式自定义 的控件, 只能通过 Java 代码来创建 View(Context context, AttributeSet attrs): 通过这种方式自定义的控件既可以在 Java 代码中创建, 也可以在 XML 文件中使用, 在 XML 文件中使用时, 使用完整的包名 + 类名 作为标签的名称, 如下所示 1 public class MyButton extends Button { 2 public MyButton(Context context) { 3 super(context); 4 } 5 // public MyButton(Context context, AttributeSet attrs) { 6 // super(context, attrs); 7 // } 8 } 6. 使用 ListActivity 时, 调用 setcontentview() 方法出错 当使用 ListActivity 时, 可以不包含任何布局文件, 即不调用 setcontentview() 方法, 如果使用 setcontentview() 方法设置显示的界面, 则在布局文件中必须包含一个 ListView, 并且 ListView 的 id 为 :@android:id/list 否则会抛出运行时异常 (Fatal Exception 致命的异 常 ):Your content must hava a ListView whose id attribute is android.r.id.list 为什么? ListActivity has a default layout that consists of a single, full-screen list in the center of the screen. However, if you desire, you can customize the screen layout by setting your own view layout with setcontentview() in oncreate(). To do this, your own view MUST contain a ListView object with the id "@android:id/list" 因为 ListActivity 中有一个默认的布局文件, 333

142 该文件中仅包含一个占满整个屏幕的 ListView, 并且该 ListView 的 id 系统会根据这个 id 来获取 ListActivity 中的 ListView Eclipse 中导入项目时错误 1) 几乎所有的 Java 类都报错 出现这种现象, 通常是由 Android 的版本造成的, 原来项目所使用的版本在本机上 不存在, 此时我们可以看到在项目的文档结构中不存在 Android 开发包 解决方案 : 为该项目引入 Android 开发包, 不一定要和原版本一致, 可以引入比原 版本更高的开发包 操作过程 : 选中该项目右键 选择 properties 弹出对话框 选择 Android, 然后在右边选择一个已有的 Android 开发包 Apply OK 2) 提示 Java 编译器错误 Eclipse 中导入 Android 项目时时常出现 :Android requires compiler compliance level 5.0 or 6.0. Found'1.4' instead. Please use Android Tools > Fix Project Properties. 解决方案 : (1) 按提示在工程文件上右键 Android Tools Fix Project Properties 即可 ; (2) 若 (1) 无效, 则手动打开 Project Properties javacompiler 选上 Enable project specific setting 再选择 Compiler Compliance Level( 选择任意一个非默认的值 ) OK ; (3) 重复第 (2) 步, 将 Compiler Compliance Leave 选为正确的值 ( 该值一般是当前安装 的 JDK 版本值, 如 jdk 5 对应 1.5,jdk 6 对应 1.6),OK 3) 报错 有时候导入 android 工程的时候, 明明是刚刚用过的没有问题的工程, 但重新导入 的时候就报错 提示 The method... must override a spuerclass method, 然后 eclipse 给我们提示让我 删除 这个错误源于 java compiler, Java1.5 的,1.6 中才有 解决方案 : 让 eclipse 使用 java1.6 而不是 1.5 操作过程如下 : Eclipse 中选择 Window Preferences Java Compiler 虽然这个时候我们可能在右边看到的 Compiler compiance level 选择的是 1.6, 但 是细分到每个项目的时候则不一定, 因此我们继续选择 Configure Project Specific Setings..., 于是我们可以看到我们的工程了, 选择报错的工程 OK 这时我们看到这里的 JDK Compliance 并不是 1.6, 将其修改为 1.6 OK

143 参考文献 [1] Android 开发者指南. [EB/OL],2012. [2] 李刚编著. 疯狂 Android 讲义 [M]. 北京 : 电子工业出版社, [3] 杨丰盛著.Android 应用开发揭秘 [M]. 北京 : 机械工业出版社, [4] 李宁编著.Android 开发权威指南 [M]. 北京 : 人民邮电出版社, [5] 李兴华编著. 名师讲坛 Android 开发实战经典 [M]. 北京 : 清华大学出版社, [6] 吴亚峰, 苏亚光编著. Android 应用案例开发大全 [M]. 北京 : 人民邮电出版社,

144 336 Android 编程 课部分优秀学生 ( 经任课教师推荐, 最大比例为教学班人数的 4%; 或省级竞赛一等奖得主 ) 黄坚坚 杨斐 梁艳婷 邓璨荣 揭萍 曾宇辉 QQ: QQ: QQ: QQ: QQ: QQ: 江西财经大学软件江西财经大学软件江西财经大学软件江西财经大 2012 级江西财经大学软件工江西财经大学软件 工程 10 级 工程 105 班 工程 115 班 计算机科学与技术 1 程 112 班, 保送华侨大工程 11 级, 广州海 北京发现角科技有深圳市天方达科技上海微汇金融信息班, 芬兰奥卢大学学计算机应用技术硕格通信集团网络通 限公司 发展有限公司 服务有限公司 GS3D 研究生 士 信研究院 熊智亮王俊峰胡哲李兵兵廖钟民何清辉 QQ: QQ: QQ: QQ: QQ: QQ: 江西财经大学软件江西财经大学软件江西财经大学软件江西财经大学软件江西财经大学软件工江西财经大学软件与通信工程学院软工程 113 班, 杭州世工程 113 班, 广州亚工程 114 班程 115 班工程 115 班件工程 111 班纳科技有限公司信智能终端部门 黄康琴 彭龙 皮怀雨 陈恭斌 万鑫宇 罗来亮 QQ: QQ: QQ: QQ: QQ: QQ: 江西科技师范大学 - 江西财经大学软件江西财经大学软件江西财经大学软件江西财经大学 新余学院 12 级计算 教育技术学 2011 级工程 114 班 工程 2012 级 工程 2012 级 软件工程 121 班 机科学与技术 吴慧诗 张仁礼 陈楷元 陈 芳 毛侠 王新 QQ: QQ: QQ: QQ: QQ: QQ: 赣南师范学 2012 级赣南师范学 2012 级赣南师范学院 2012 赣南师范学院 2012 江西财经大学软件工江西财经大学软件 计算机科学与技术 计算机科学与技术 级计算机科学与技级计算机科学与技程 12 级 工程 115 班, 深圳市 术 术 云软信息技术有限 公司

145 仇夏夏周芳鑫陈小俊王星旺童然陆军涛 QQ: QQ: QQ: QQ: QQ: QQ: 江西机电职业技术九江职业技术学院, 江西应用技术职业学江西应用技术职业学江西应用技术职业江西应用技术职业学院手机软件班 13 3G 移动软件开发院 2010 级软件技术, 院 2011 级软件技术, 学院 2011 级软件技学院 2012 级软件技级上海陆宝网络技术有广州科韵股份有限公术, 上海丁丁网术, 昂盛智能股份有限公司司限公司 (.CN) 为 Android 初学者提供可信的第三方水 平测试和企业招聘服务 不管是否使用本书, 只要通过测试的优秀程序员, 都可在网 站免费展示 任何需招聘移动开发程序员的用人单位, 都欢迎在免费张贴 招聘启事 企业 Enterprise E1. 发布招聘启事 是 是否自选题 T1. 随机生成客观题测试卷 求职者 Job Seeker J1. 填写简历 J2. 添加客观题试卷 E2. 企业选题 E4. 查阅求职者测试报告 否 E3. 系统出题 主观题试题库 主观题需求库 代测平台 Test Platform T2. 系统自动评阅客观题 是否合格 是 T3. 生成主观题测试卷 T4. 选聘评审员 客观题试卷库 客观题答卷库 客观题成绩库 主观题试卷库 主观题答卷库 J4. 选择企业投简历 是否合格 J5. 主观题答题 客观题成绩库 T5. 评审员评审 T6. 生成测试报告 测试报告库 否 客观题试题库 主观题成绩库 J6. 查阅自身测试报告 Android 程序员代招代测平台 工作流程图 J3. 客观题答题 是 否 337

146

res/layout 目录下的 main.xml 源码 : <?xml version="1.0" encoding="utf 8"?> <TabHost android:layout_height="fill_parent" xml

res/layout 目录下的 main.xml 源码 : <?xml version=1.0 encoding=utf 8?> <TabHost android:layout_height=fill_parent xml 拓展训练 1- 界面布局 1. 界面布局的重要性做应用程序, 界面是最基本的 Andorid 的界面, 需要写在 res/layout 的 xml 里面, 一般情况下一个 xml 对应一个界面 Android 界面布局有点像写 html( 连注释代码的方式都一样 ), 要先给 Android 定框架, 然后再在框架里面放控件,Android 提供了几种框架,AbsoluteLayout,LinearLayout,

More information

Microsoft Word - 第1章 Android基本概念.docx

Microsoft Word - 第1章 Android基本概念.docx Android 系 统 下 Java 编 程 详 解 作 者 : 华 清 远 见 第 1 章 Android 基 本 概 念 本 章 简 介 本 章 主 要 介 绍 Android 基 本 概 念 方 面 的 内 容, 包 括 Android 平 台 特 性 Android 系 统 架 构 Android 开 发 框 架 和 Android 开 发 环 境 搭 建 1.1 Android 简 介 Android

More information

Microsoft Word zw

Microsoft Word zw 第 1 章 Android 概述 学习目标 : Android Android Android Studio Android Android APK 1.1 1. 智能手机的定义 Smartphone 2. 智能手机的发展 1973 4 3 PC IBM 1994 IBM Simon PDA PDA Zaurus OS 1996 Nokia 9000 Communicator Nokia 9000

More information

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new ListView 自訂排版 主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new int[]{r.drawable.dog1, R.drawable.dog2,

More information

一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页

一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页 第 1 页共 32 页 crm Mobile V1.0 for IOS 用户手册 一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页 二 crm Mobile 界面介绍 : 第 3 页共 32 页 三 新建 (New) 功能使用说明 1 选择产品 第 4 页共 32 页 2 填写问题的简要描述和详细描述 第 5 页共

More information

第一章 Android 简介与开发环境搭建

第一章 Android 简介与开发环境搭建 安卓开发环境与常用布局 第一章 Android 简介与开发环境搭建 本章目标 3G 的概念 Android 的改变 Android 开发环境的搭建编写第一个 Android 程序 Android 程序的目录结构 手机发展史 1. 3G 的概念 3G 全称为 :3rd Generation, 中文含义即为第三代数字通信, 是指将无线通信与国际互联网等多媒体通信结合的新一代移动通信系统 2. 符合 3G

More information

手册 doc

手册 doc 1. 2. 3. 3.1 3.2 3.3 SD 3.4 3.5 SD 3.6 3.7 4. 4.1 4.2 4.3 SD 4.4 5. 5.1 5.2 5.3 SD 6. 1. 1~3 ( ) 320x240~704x288 66 (2G SD 320x2401FPS ) 32M~2G SD SD SD SD 24V DC 3W( ) -10~70 10~90% 154x44x144mm 2. DVR106

More information

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP: ******************* * 关于 Java 测试试题 ******

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP:  ******************* * 关于 Java 测试试题 ****** ******************* * 关于 Java 测试试题 ******************* 問 1 运行下面的程序, 选出一个正确的运行结果 public class Sample { public static void main(string[] args) { int[] test = { 1, 2, 3, 4, 5 ; for(int i = 1 ; i System.out.print(test[i]);

More information

Microsoft Word - 实验一Android开发环境搭建.doc

Microsoft Word - 实验一Android开发环境搭建.doc 实验 1:Android 开发环境搭建 实验目的 : 搭建 Android 开发环境, 为后续的开发做好充分准备 实验内容 : n 搭建 Android 开发环境 n 创建 HelloWorld 程序实验步骤一 下载与安装 JDK n JDK 下载地址 :http://www.oracle.com/technetwork/java/javase/downloads/index.html 考虑到稳定性,

More information

FJXBQ

FJXBQ 高等医学院校选用教材 ( 供成人教育中医药专业 中西医结合专业使用 ) 方剂学 闫润红 主编 2 0 0 1 内容简介本书是供成人教育中医药专业 中西医结合专业使用的教材 全书分总论和各论两部分, 总论部分对中医方剂的基本理论, 如治法 君臣佐使 剂型 剂量等及其现代研究进展进行了介绍 各论部分对常用方剂的主治病证 配伍意义 临床应用 加减变化规律及现代研究概况等内容, 按分类进行了系统阐述 在保证方剂学学科知识结构完整性的前提下,

More information

Android Robert C.C. Huang Oscar F.Y. Liu Peter C.L. Hsieh 2011/03/21

Android Robert C.C. Huang Oscar F.Y. Liu Peter C.L. Hsieh 2011/03/21 Android Robert C.C. Huang Oscar F.Y. Liu Peter C.L. Hsieh 2011/03/21 Outlines for Today Future Planning Review System Architecture Dev. Tools & Making the First App Project Structure & File Details Application

More information

Microsoft Word - 第3章.doc

Microsoft Word - 第3章.doc 第 章.1 Android UI 基本概念 用户界面 (User Interface,UI) 是用户与设备之间进行信息交流的直接媒介, 是决定用户体验最重要的部分 相比于早期的计算机的主要交互界面 批处理界面和命令行界面, 现在更为流行的是更简单直接的用户图形界面 (Graphical User Interface, GUI) GUI 简单易用, 受众面广, 直接推动了个人计算机的发展 目前, 主流大众的操作系统都采用了

More information

<4D6963726F736F667420576F7264202D20BBF9D3DA416E64726F6964C6BDCCA8B5C4B5E7D7D3C5C4C2F4CFB5CDB32E646F63>

<4D6963726F736F667420576F7264202D20BBF9D3DA416E64726F6964C6BDCCA8B5C4B5E7D7D3C5C4C2F4CFB5CDB32E646F63> 基 于 Android 平 台 的 电 子 拍 卖 系 统 摘 要 本 电 子 拍 卖 系 统 其 实 就 是 一 个 电 子 商 务 平 台, 只 要 将 该 系 统 部 署 到 互 联 网 上, 客 户 都 可 以 在 该 系 统 上 发 布 想 出 售 的 商 品, 也 可 以 对 拍 卖 中 的 商 品 参 与 竞 价 整 个 过 程 无 须 人 工 干 预, 由 系 统 自 动 完 成 本

More information

题目

题目 开发 Android 应用 目的 : 帮助大家了解 Android 平台开发 作者 : 王威威 技术爱好 : linux,qt, 嵌入式开发 MSN : wangweiweicdma@hotmail.com Email : wang.weiwei1@ztenc.com.cn wangjiecdma@126.com 下载开发资源 1 下载最新的 Android SDK http://code.google.com/android/download.html

More information

01_Service

01_Service 移动平台应用软件开发 Service 主讲 : 张齐勋 zhangqx@ss.pku.edu.cn 移动平台应用软件开发 课程建设小组 北京大学 二零一七年 什么是 Service 与 Activity 一样, 同属 Android 基本组件 后台运行, 不与用户交互, 没有可视化界面 最常见的 Service 如 : 在后台播放歌曲 后台执行文件的下载 同样需在 AndroidManifest.xml

More information

<C8EBC3C5C6AAA3A8B5DA31D5C2A3A92E696E6464>

<C8EBC3C5C6AAA3A8B5DA31D5C2A3A92E696E6464> 第 1 章 进入 Photoshop 的全新世界 本章导读 Photoshop 1 1.1 Photoshop CS6 Photoshop Photoshop 1.1.1 Photoshop POP 1-1 图 1-1 平面广告效果 1.1.2 Photoshop 1-2 Photoshop CS6 Photoshop CS6 Photoshop CS6 Extended 3D 3 Photoshop

More information

Microsoft Word - 在VMWare-5.5+RedHat-9下建立本机QTopia-2.1.1虚拟平台a.doc

Microsoft Word - 在VMWare-5.5+RedHat-9下建立本机QTopia-2.1.1虚拟平台a.doc 在 VMWare-5.5+RedHat-9 下建立 本机 QTopia-2.1.1 虚拟平台 张大海 2008-5-9 一 资源下载 1. 需要以下安装包 : tmake-1.13.tar.gz qtopia-free-source-2.1.1.tar.gz qt-embedded-2.3.10-free.tar.gz qt-x11-2.3.2.tar.gz qt-x11-free-3.3.4.tar.gz

More information

<4D F736F F D20B5DA32D5C2A1A2416E64726F6964BFAAB7A2BBB7BEB3B4EEBDA8>

<4D F736F F D20B5DA32D5C2A1A2416E64726F6964BFAAB7A2BBB7BEB3B4EEBDA8> 2 Android Windows Android JDK Java Eclipse Android SDK ADT Android 2.1 Android Android 应用软件开发需要的开发环境如表 2-1 所示 表 2-1 所需项 版本需求 说明 备注 操作系统 Windows XP/Vista/7 Mac OS X10..8+ 选择自己最熟悉的操作系统 Linux Ubuntu Drapper

More information

Lecture01_Android介绍

Lecture01_Android介绍 移动平台应用软件开发 Android 介绍 主讲 : 张齐勋 zhangqx@ss.pku.edu.cn 移动平台应用软件开发 课程建设小组 北京大学 二零一七年秋北京 Android是什么 Android不仅仅是一个操作系统 它更是一个完整的软件框 架 Android基于Linux内核 2005年Google公司收购了Android公司 Google公司选择使用Apache许可证开放Android源码

More information

图书在版编目穴 CIP 雪数据做事细节全书 / 赵彦锋编著郾 北京 : 企业管理出版社, ISBN Ⅰ 郾做... Ⅱ 郾赵... Ⅲ 郾工作方法 通俗读物 Ⅳ 郾 B 中国版本图书馆 CIP 数据核字 (2005) 第 号 书

图书在版编目穴 CIP 雪数据做事细节全书 / 赵彦锋编著郾 北京 : 企业管理出版社, ISBN Ⅰ 郾做... Ⅱ 郾赵... Ⅲ 郾工作方法 通俗读物 Ⅳ 郾 B 中国版本图书馆 CIP 数据核字 (2005) 第 号 书 做事细节全书 赵彦锋著 企业管理出版社 图书在版编目穴 CIP 雪数据做事细节全书 / 赵彦锋编著郾 北京 : 企业管理出版社, 2005.11 ISBN 7-80197-338-0 Ⅰ 郾做... Ⅱ 郾赵... Ⅲ 郾工作方法 通俗读物 Ⅳ 郾 B026-49 中国版本图书馆 CIP 数据核字 (2005) 第 136676 号 书 名 : 做事细节全书 作 者 : 赵彦锋 责任编辑 : 吴太刚

More information

内 容 提 要 将 JAVA 开 发 环 境 迁 移 到 Linux 系 统 上 是 现 在 很 多 公 司 的 现 实 想 法, 而 在 Linux 上 配 置 JAVA 开 发 环 境 是 步 入 Linux 下 JAVA 程 序 开 发 的 第 一 步, 本 文 图 文 并 茂 地 全 程 指

内 容 提 要 将 JAVA 开 发 环 境 迁 移 到 Linux 系 统 上 是 现 在 很 多 公 司 的 现 实 想 法, 而 在 Linux 上 配 置 JAVA 开 发 环 境 是 步 入 Linux 下 JAVA 程 序 开 发 的 第 一 步, 本 文 图 文 并 茂 地 全 程 指 内 容 提 要 将 JAVA 开 发 环 境 迁 移 到 Linux 系 统 上 是 现 在 很 多 公 司 的 现 实 想 法, 而 在 Linux 上 配 置 JAVA 开 发 环 境 是 步 入 Linux 下 JAVA 程 序 开 发 的 第 一 步, 本 文 图 文 并 茂 地 全 程 指 导 你 搭 建 Linux 平 台 下 的 JAVA 开 发 环 境, 包 括 JDK 以 及 集

More information

预览图 : (2) 在 SelectCity.java 中增加控件, 用于绑定 select_city 文件的 ListView, TextView,EditTest 等控件 代码和注释如下 :

预览图 : (2) 在 SelectCity.java 中增加控件, 用于绑定 select_city 文件的 ListView, TextView,EditTest 等控件 代码和注释如下 : EditText 实现城市搜索 1801210778 邹宇航 摘要 : 使用 EditText 实现搜索城市的功能, 以此为依据更新 ListView 1. 效果图 : 2. 主要步骤 (1) 在 select-city.xml 布局文件中中添加 EditText 控件用作搜索框, 然后添加 ListView 控件用来显示城市名字内容 代码如下 : 预览图 : (2) 在 SelectCity.java

More information

untitled

untitled JavaEE+Android - 6 1.5-2 JavaEE web MIS OA ERP BOSS Android Android Google Map office HTML CSS,java Android + SQL Sever JavaWeb JavaScript/AJAX jquery Java Oracle SSH SSH EJB+JBOSS Android + 1. 2. IDE

More information

目 錄 版 次 變 更 記 錄... 2 原 始 程 式 碼 類 型 之 使 用 手 冊... 3 一 安 裝 軟 體 套 件 事 前 準 備... 3 二 編 譯 流 程 說 明... 25 1

目 錄 版 次 變 更 記 錄... 2 原 始 程 式 碼 類 型 之 使 用 手 冊... 3 一 安 裝 軟 體 套 件 事 前 準 備... 3 二 編 譯 流 程 說 明... 25 1 科 技 部 自 由 軟 體 專 案 原 始 程 式 碼 使 用 手 冊 Source Code Manual of NSC Open Source Project 可 信 賴 的 App 安 全 應 用 框 架 -App 應 用 服 務 可 移 轉 性 驗 證 Trusted App Framework -Transferability Verification on App MOST 102-2218-E-011-012

More information

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 odps-sdk 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基 开放数据处理服务 ODPS SDK SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基础功能的主体接口, 搜索关键词 "odpssdk-core" 一些

More information

2 第 章 绪 论 Internet 2.0 使 得 消 费 型 电 子 产 品 用 户 可 以 通 过 多 种 不 同 的 数 据 网 络 访 问 互 联 网 内 容 用 户 可 以 使 用 便 携 式 消 费 型 电 子 设 备, 如 智 能 手 机 触 屏 平 板 电 脑 电 子 书, 甚 至

2 第 章 绪 论 Internet 2.0 使 得 消 费 型 电 子 产 品 用 户 可 以 通 过 多 种 不 同 的 数 据 网 络 访 问 互 联 网 内 容 用 户 可 以 使 用 便 携 式 消 费 型 电 子 设 备, 如 智 能 手 机 触 屏 平 板 电 脑 电 子 书, 甚 至 . Android 是 什 么 第 章 绪 论 2 3 本 章 将 主 要 介 绍 Android 操 作 系 统, 这 些 背 景 知 识 可 以 帮 你 更 好 地 理 解 本 书 的 内 容 你 将 了 解 到, 这 一 平 台 在 如 今 以 便 携 式 消 费 型 电 子 设 备 为 基 础 的 Internet 2.0 环 境 下 是 如 何 大 显 身 手 的 这 里 所 说 的 Internet

More information

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 ->

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 -> 目录 1 大概思路... 1 2 创建 WebAPI... 1 3 创建 CrossMainController 并编写... 1 4 Nuget 安装 microsoft.aspnet.webapi.cors... 4 5 跨域设置路由... 4 6 编写 Jquery EasyUI 界面... 5 7 运行效果... 7 8 总结... 7 1 1 大概思路 创建 WebAPI 创建 CrossMainController

More information

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double

More information

Ioncube Php Encoder 8 3 Crack 4. llamaba octobre traslado General Search colony

Ioncube Php Encoder 8 3 Crack 4. llamaba octobre traslado General Search colony Ioncube Php Encoder 8 3 Crack 4 ->>->>->> DOWNLOAD 1 / 5 2 / 5 Press..the..General..Tools..category4Encrypt..and..protect..files..with..PHP..encoding,..encryption,..ob fuscation..and..licensing... 2016

More information

03 开发入门.key

03 开发入门.key #3 手机应用开发入门 刘宁 Email:liuning2@mail.sysu.edu.cn 大纲» Android 基本概念» 开发包及 工具安装» 创建 HelloWorld» Android 程序设计基础 2 Android 开发基本概念» Activities» Intents» 视图与控件 ( 界 面元素 )» 异步调 用 ( 多线程 支持 )» 后台服务 3 Activities Android

More information

Guava学习之Resources

Guava学习之Resources Resources 提供提供操作 classpath 路径下所有资源的方法 除非另有说明, 否则类中所有方法的参数都不能为 null 虽然有些方法的参数是 URL 类型的, 但是这些方法实现通常不是以 HTTP 完成的 ; 同时这些资源也非 classpath 路径下的 下面两个函数都是根据资源的名称得到其绝对路径, 从函数里面可以看出,Resources 类中的 getresource 函数都是基于

More information

Dynamic Layout in Android

Dynamic Layout in Android Dynamic Layout in Android 建國科技大學資管系 饒瑞佶 2013/5 V1 Layout 多半都透過 res/layout/xml 格式設定來達成 Android 是 OOP, 所以可以動態產生 Layout 重點是 Layout 的階層關係 (Hierarchy) 需要處理對應事件 最後一樣用 setcontentview 加入 Layout 一 加入現有 Layout 中

More information

第二章 Android 界面编程基础 一 学情分析 本章课程主要讲述界面控件和布局管理器, 学生可以结合前面学习课程 HTML 前端界面设计和 Java 程序设计中图形界面设计 swing 组件学习 本章学习难点因为未接触 XML 页面开发, 在界面设计中需要重新认识, 尤其组件名及组件属性需要 着

第二章 Android 界面编程基础 一 学情分析 本章课程主要讲述界面控件和布局管理器, 学生可以结合前面学习课程 HTML 前端界面设计和 Java 程序设计中图形界面设计 swing 组件学习 本章学习难点因为未接触 XML 页面开发, 在界面设计中需要重新认识, 尤其组件名及组件属性需要 着 第二章 Android 界面编程基础 一 学情分析 本章课程主要讲述界面控件和布局管理器, 学生可以结合前面学习课程 HTML 前端界面设计和 Java 程序设计中图形界面设计 swing 组件学习 本章学习难点因为未接触 XML 页面开发, 在界面设计中需要重新认识, 尤其组件名及组件属性需要 着重掌握 本章属于 Android 开发初级阶段, 本章应该着重培养学生学习成就感, 让学生踏入 Android

More information

第四章 Android 事件处理 一 学情分析前面我们学习了 Android 提供的一些强大的界面组件, 这些组件主要是用来进行数据显示, 如何用户想进行交互, 实现具体的功能, 则还需要相应事件处理进行辅助 当用户在程序上执行各种操作是, 如单机一个按钮, 应用程序必须为用户提供相应动作, 这种响

第四章 Android 事件处理 一 学情分析前面我们学习了 Android 提供的一些强大的界面组件, 这些组件主要是用来进行数据显示, 如何用户想进行交互, 实现具体的功能, 则还需要相应事件处理进行辅助 当用户在程序上执行各种操作是, 如单机一个按钮, 应用程序必须为用户提供相应动作, 这种响 第四章 Android 事件处理 一 学情分析前面我们学习了 Android 提供的一些强大的界面组件, 这些组件主要是用来进行数据显示, 如何用户想进行交互, 实现具体的功能, 则还需要相应事件处理进行辅助 当用户在程序上执行各种操作是, 如单机一个按钮, 应用程序必须为用户提供相应动作, 这种响应动作通过事件处理来进行完成 Android 中, 用户界面属于主线程, 而子线程无法更新主线程的界面状态,

More information

Java 1 Java String Date

Java 1 Java String Date JAVA SCJP Java 1 Java String Date 1Java 01 Java Java 1995 Java Java 21 Java Java 5 1-1 Java Java 1990 12 Patrick Naughton C++ C (Application Programming Interface API Library) Patrick Naughton NeXT Stealth

More information

// HDevelopTemplateWPF projects located under %HALCONEXAMPLES%\c# using System; using HalconDotNet; public partial class HDevelopExport public HTuple

// HDevelopTemplateWPF projects located under %HALCONEXAMPLES%\c# using System; using HalconDotNet; public partial class HDevelopExport public HTuple halcon 与 C# 混合编程之 Halcon 代码调用 写在前面 完成 halcon 与 C# 混合编程的环境配置后, 进行界面布局设计构思每一个按钮所需要实现 的功能, 将 Halcon 导出的代码复制至相应的 C# 模块下即可 halcon 源程序 : dev_open_window(0, 0, 512, 512, 'black', WindowHandle) read_image (Image,

More information

F515_CS_Book.book

F515_CS_Book.book /USB , ( ) / L R 1 > > > 2, / 3 L 1 > > > 2 + - 3, 4 L 1 了解显示屏上显示的图标 Wap 信箱收到一条 Wap push 信息 ( ) GSM 手机已连接到 GSM 网络 指示条越多, 接收质量越好 2 ...........................4.............................. 4 Micro SD (

More information

人民邮电

人民邮电 第 2 章 布 局 布 局 是 所 有 带 界 面 的 Android 程 序 的 开 端 布 局 应 用 得 好 坏 直 接 决 定 了 程 序 的 用 户 体 验 虽 然 布 局 看 似 没 有 组 件 复 杂, 但 也 涉 及 到 了 很 多 技 巧 在 各 大 公 司 的 面 试 题 中 也 会 经 常 遇 到 关 于 布 局 的 一 些 问 题 通 过 这 些 问 题 可 以 考 查 应

More information

图书在版编目 (CIP) 数据 满堂花醉 / 沈胜衣著. 南京 : 江苏教育出版社, ( 沈郎文字 ) ISBN Ⅰ. 满... Ⅱ. 沈... Ⅲ. 作家 - 人物研究 - 世界 Ⅳ.K815.6 中国版本图书馆 CIP 数据核字 (2005) 第 041

图书在版编目 (CIP) 数据 满堂花醉 / 沈胜衣著. 南京 : 江苏教育出版社, ( 沈郎文字 ) ISBN Ⅰ. 满... Ⅱ. 沈... Ⅲ. 作家 - 人物研究 - 世界 Ⅳ.K815.6 中国版本图书馆 CIP 数据核字 (2005) 第 041 图书在版编目 (CIP) 数据 满堂花醉 / 沈胜衣著. 南京 : 江苏教育出版社, 2005.4 ( 沈郎文字 ) ISBN 7-5343-6512-0 Ⅰ. 满... Ⅱ. 沈... Ⅲ. 作家 - 人物研究 - 世界 Ⅳ.K815.6 中国版本图书馆 CIP 数据核字 (2005) 第 041843 号 出版者社址网址出版人 南京市马家街 31 号邮编 :210009 http://www.1088.com.cn

More information

Android Android Android SDK iv

Android Android Android SDK iv Android Market Google Android SDK Apple Google Microsoft b2c b 2010 Internet Android how why iii Android 240... Android Android SDK iv Android Market Google Android SDK Visual C++ Java N-tier J2EE Unix/Linux

More information

图书在版编目 (CIP) 数据 文学与现代性批判 / 邵建著. 南京 : 江苏教育出版社, ISBN Ⅰ. 文... Ⅱ. 邵... Ⅲ. 当代文学 - 文学研究 - 中国 Ⅳ.I206.7 中国版本图书馆 CIP 数据核字 ( 2005 ) 第 04185

图书在版编目 (CIP) 数据 文学与现代性批判 / 邵建著. 南京 : 江苏教育出版社, ISBN Ⅰ. 文... Ⅱ. 邵... Ⅲ. 当代文学 - 文学研究 - 中国 Ⅳ.I206.7 中国版本图书馆 CIP 数据核字 ( 2005 ) 第 04185 图书在版编目 (CIP) 数据 文学与现代性批判 / 邵建著. 南京 : 江苏教育出版社, 2005.4 ISBN 7-5343-6528-7 Ⅰ. 文... Ⅱ. 邵... Ⅲ. 当代文学 - 文学研究 - 中国 Ⅳ.I206.7 中国版本图书馆 CIP 数据核字 ( 2005 ) 第 041850 号 出版者社址网址出版人 南京市马家街 31 号邮编 :210009 http://www.1088.com.cn

More information

xforce keygen microsoft office 2013

xforce keygen microsoft office 2013 Xforce Keygen Microsoft Office 2013 ->->->-> http://shurll.com/78610 1 / 5 2 / 5 Generally, Autodesk,,Vault,,Office,,2016,,555H1,,Autodesk,,Vault,,Professional,,2016,,569H1,,Autode sk,,vault,,workgroup,,2016,,559h1,,autodesk,,vehicle,,tracking,,2016,,955h1,,autodesk,,vred...

More information

第四章 102 图 4唱16 基于图像渲染的理论基础 三张拍摄图像以及它们投影到球面上生成的球面图像 拼图的圆心是相同的 而拼图是由球面图像上的弧线图像组成的 因此我 们称之为同心球拼图 如图 4唱18 所示 这些拼图中半径最大的是圆 Ck 最小的是圆 C0 设圆 Ck 的半径为 r 虚拟相机水平视域为 θ 有 r R sin θ 2 4畅11 由此可见 构造同心球拼图的过程实际上就是对投影图像中的弧线图像

More information

Android 开发快速入门 第 章.1 开发环境的搭建 在开始 Android 开发之旅启动之前, 首先要搭建环境,Android 开发环境的安装和配置是 开发 Android 应用程序的第一步, 也是深入 Android 平台的一个非常好的入口.1.1 开发准备工作 配置 Android 开发环

Android 开发快速入门 第 章.1 开发环境的搭建 在开始 Android 开发之旅启动之前, 首先要搭建环境,Android 开发环境的安装和配置是 开发 Android 应用程序的第一步, 也是深入 Android 平台的一个非常好的入口.1.1 开发准备工作 配置 Android 开发环 Android 开发快速入门 学习目标 : 对 Android 快速入门, 能够开发运行简单 Android 应用, 为以后深入学习打下坚实基础 知识目标 理解 Android 相关的基本概念 熟练搭建 Android 开发运行环境 编写一个 Android 应用程序 了解 Android 应用四个主要组件 技能目标 能熟练搭建 Android 开发环境 编写运行一个 Android 应用 Android

More information

小应用 Magic8

小应用 Magic8 胡家威 计研135班 http://hujiaweibujidao.github.io/ 小应用 Magic8 Android 系统简介 Android 应用结构 Android 四大组件 Activity 生命周期 Android 资源管理 UI 组件和容器组件 内容概要 ( 上 ) Android 系统简介 Android 市场份额 Android 是如何诞生的? Android 之父 :Andy

More information

Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7.

Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7. Kubernetes 包管理理 工具 Helm 蔺礼强 Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7. Kubernetes

More information

Microsoft PowerPoint - 03 开发入门.ppt [兼容模式]

Microsoft PowerPoint - 03 开发入门.ppt [兼容模式] 手 机 应 用 平 台 软 件 开 发 3 开 发 入 门 刘 宁 Email:liuning2@mail.sysu.edu.cn 课 程 简 介 课 程 目 标 Android 开 发 准 备 工 作 开 发 包 及 工 具 安 装 创 建 HelloWorld Android 程 序 设 计 基 础 创 意 移 动 应 用 创 意 移 动 应 用 创 意 移 动 应 用 创 意 移 动 应 用

More information

X713_CS_Book.book

X713_CS_Book.book / / /USB ) ; ; C D ; ; B B 1 >> 2 3 B 1 ( > > ) 了解显示屏上显示的图标 Wap 信箱收到一条 Wap push 信息 GSM GPS ( ) 手机已连接到 GSM 网络 指示条越多, 接收质量越好 GPS 2 ...........................4.............................. 4 Micro SD (

More information

使用 Eclipse 开发 Java EE 应用 (Web 应用 ) 这里以开发一个简单的 Web 应用为例, 介绍使用 Eclipse 开发 Java EE 应用的一般步 骤 此处使用的 Eclipse 是 Eclipse IDE for Java EE Developers; 如果是使用的其他

使用 Eclipse 开发 Java EE 应用 (Web 应用 ) 这里以开发一个简单的 Web 应用为例, 介绍使用 Eclipse 开发 Java EE 应用的一般步 骤 此处使用的 Eclipse 是 Eclipse IDE for Java EE Developers; 如果是使用的其他 使用 Eclipse 开发 Java EE 应用 (Web 应用 ) 这里以开发一个简单的 Web 应用为例, 介绍使用 Eclipse 开发 Java EE 应用的一般步 骤 此处使用的 Eclipse 是 Eclipse IDE for Java EE Developers; 如果是使用的其他 Eclipse 插件 ( 比如 MyEclipse 插件 ), 其开发方式和步骤可能略有差异和不同 在该例中,

More information

untitled

untitled 图书在版编目 (CIP) 数据 家居美化中的巧 / 陈赞等编著. 北京 : 中国林业出版社,2003.4 ISBN 7-5038-3399-8 I. 家 II. 陈 III. 住宅 室内装饰 基本知识 IV.TU241 中国版本图书馆 CIP 数据核字 (2003) 第 022376 号 版权所有翻印必究 1 2002.10 1 ...1...1...2...2...3...4...5...6...7...8...8...10...10...11...12...12...13...13...15...15...16...17...18...19...20...20...20...21...22

More information

在Windows上安装Hadoop

在Windows上安装Hadoop 一见 2010.1.6 www.hadoopor.com/hadoopor@foxmail.com 1. 安装 JDK 不建议只安装 JRE, 而是建议直接安装 JDK, 因为安装 JDK 时, 可以同时安装 JRE MapReduce 程序的编写和 Hadoop 的编译都依赖于 JDK, 光 JRE 是不够的 JRE 下载地址 :http://www.java.com/zh_cn/download/manual.jsp

More information

Lecture01_Android介绍

Lecture01_Android介绍 移动平台应用软件开发 Android 介绍 主讲 : 张齐勋 zhangqx@ss.pku.edu.cn 移动平台应用软件开发 课程建设小组 北京大学 二零一八年秋北京 Android 是什么 Android 不仅仅是一个操作系统, 它更是一个完整的软件框架 Android 基于 Linux 内核 2005 年 Google 公司收购了 Android 公司 Google 公司选择使用 Apache

More information

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例 帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例 这篇文章主要介绍了帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例, 本文还详细介绍了帝国 CMS 数据库类中的一些常用方法, 需要的朋友可以参考下 例 1: 连接 MYSQL 数据库例子 (a.php)

More information

Microsoft Word - install_manual-V _CN.docx

Microsoft Word - install_manual-V _CN.docx NO TASK Q-Sign Install Manual PAGE 1/28 Q-Sign INSTALL MANUAL Version 3.0 Server Manager Client Codec NO TASK Q-Sign Install Manual PAGE 2/28 History DATE Contents Name Ver. Remark 2009-02-11 Q-Sign Ver.

More information

Microsoft Word - Hibernate与Struts2和Spring组合指导.doc

Microsoft Word - Hibernate与Struts2和Spring组合指导.doc 1.1 组合 Hibernate 与 Spring 1. 在 Eclipse 中, 新建一个 Web project 2. 给该项目增加 Hibernate 开发能力, 增加 Hibernate 相关类库到当前项目的 Build Path, 同时也提供了 hibernate.cfg.xml 这个配置文件 3. 给该项目增加 Spring 开发能力, 增加 spring 相关类库到当前项目的 Build

More information

ChinaBI企业会员服务- BI企业

ChinaBI企业会员服务- BI企业 商业智能 (BI) 开源工具 Pentaho BisDemo 介绍及操作说明 联系人 : 杜号权苏州百咨信息技术有限公司电话 : 0512-62861389 手机 :18616571230 QQ:37971343 E-mail:du.haoquan@bizintelsolutions.com 权限控制管理 : 权限控制管理包括 : 浏览权限和数据权限 ( 权限部分两个角色 :ceo,usa; 两个用户

More information

六域链联盟 SDChain-Matrix 节点搭建指南 2018/07/26 Version : 1.0.0

六域链联盟 SDChain-Matrix 节点搭建指南 2018/07/26 Version : 1.0.0 SDChain-Matrix 节点搭建指南 目录 1 环境要求... 3 2 软件下载... 4 3 安装部署... 4 3.1 部署可执行程序目录... 4 3.2 部署配置文件目录... 4 3.3 部署数据库文件目录... 4 3.4 部署日志文件目录... 4 3.5 部署依赖库文件目录... 4 4 配置参数... 5 5 启动运行... 7 5.1 普通模式启动... 7 5.2 加载启动模式...

More information

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double x) { d

More information

本章学习目标 小风 Java 实战系列教程 SpringMVC 简介 SpringMVC 的入门案例 SpringMVC 流程分析 配置注解映射器和适配器 注解的使用 使用不同方式的跳转页面 1. SpringMVC 简介 Spring web mvc

本章学习目标 小风 Java 实战系列教程 SpringMVC 简介 SpringMVC 的入门案例 SpringMVC 流程分析 配置注解映射器和适配器 注解的使用 使用不同方式的跳转页面 1. SpringMVC 简介 Spring web mvc 本章学习目标 SpringMVC 简介 SpringMVC 的入门案例 SpringMVC 流程分析 配置注解映射器和适配器 配置视图解析器 @RequestMapping 注解的使用 使用不同方式的跳转页面 1. SpringMVC 简介 Spring web mvc 和 Struts2 都属于表现层的框架, 它是 Spring 框架的一部分, 我们可 以从 Spring 的整体结构中看得出来 :

More information

图书在版编目 (CIP) 数据程序员的数学. 3, 线性代数 /( 日 ) 平冈和幸, ( 日 ) 堀玄著 ; 卢晓南译. 北京 : 人民邮电出版社, ( 图灵程序设计丛书 ) ISBN Ⅰ. 1 程 Ⅱ. 1 平 2 堀 3 卢 Ⅲ. 1 电子计算

图书在版编目 (CIP) 数据程序员的数学. 3, 线性代数 /( 日 ) 平冈和幸, ( 日 ) 堀玄著 ; 卢晓南译. 北京 : 人民邮电出版社, ( 图灵程序设计丛书 ) ISBN Ⅰ. 1 程 Ⅱ. 1 平 2 堀 3 卢 Ⅲ. 1 电子计算 图灵程序设计丛书 程序员的数学 3: 线性代数 [ 日 ] 平冈和幸堀玄著 卢晓南译 图书在版编目 (CIP) 数据程序员的数学. 3, 线性代数 /( 日 ) 平冈和幸, ( 日 ) 堀玄著 ; 卢晓南译. 北京 : 人民邮电出版社, 2016.3 ( 图灵程序设计丛书 ) ISBN 978-7-115-41774-9 Ⅰ. 1 程 Ⅱ. 1 平 2 堀 3 卢 Ⅲ. 1 电子计算机 数学基础 2

More information

Office Office Office Microsoft Word Office Office Azure Office One Drive 2 app 3 : [5] 3, :, [6]; [5], ; [8], [1], ICTCLAS(Institute of Computing Tech

Office Office Office Microsoft Word Office Office Azure Office One Drive 2 app 3 : [5] 3, :, [6]; [5], ; [8], [1], ICTCLAS(Institute of Computing Tech - OfficeCoder 1 2 3 4 1,2,3,4 xingjiarong@mail.sdu.edu.cn 1 xuchongyang@mail.sdu.edu.cn 2 sun.mc@outlook.com 3 luoyuanhang@mail.sdu.edu.cn 4 Abstract. Microsoft Word 2013 Word 2013 Office Keywords:,, HTML5,

More information

无类继承.key

无类继承.key 无类继承 JavaScript 面向对象的根基 周爱 民 / aimingoo aiming@gmail.com https://aimingoo.github.io https://github.com/aimingoo rand = new Person("Rand McKinnon",... https://docs.oracle.com/cd/e19957-01/816-6408-10/object.htm#1193255

More information

FPGAs in Next Generation Wireless Networks WPChinese

FPGAs in Next Generation Wireless Networks WPChinese FPGA 2010 3 Lattice Semiconductor 5555 Northeast Moore Ct. Hillsboro, Oregon 97124 USA Telephone: (503) 268-8000 www.latticesemi.com 1 FPGAs in Next Generation Wireless Networks GSM GSM-EDGE 384kbps CDMA2000

More information

Microsoft Word - 第3章.doc

Microsoft Word - 第3章.doc 第 3 章 Android 应用程序剖析 用户要想编写出复杂的应用程序, 首先要对 Android 应用程序的构成及程序的内部执行流程有一个清晰的了解 本章通过对一个简单的应用程序的深入剖析, 使读者对 Android 应用程序的构成及执行流程有个清晰的了解 3.1 Android 应用程序目录结构 之前我们已经开发了一个项目名称为 HelloWorld 的 Android 应用程序, 也许你很疑惑,

More information

教案模板4-2

教案模板4-2 移动终端开发技术 电子教案 第三单元 ListView 的功能和用法 章节名称 : 第二章 软件要美观 UI 开发的点点滴滴 所属专业 ( 教研室 ): 计算机软件技术 制定人 : 陈媛媛 合作人 : 制定时间 : 2018 年 2 月 日照职业技术学院 单元标题 ListView 的功能和用法 单元教学学时 在整体设计中的位置 4 课时 第 7 次 授课班级上课地点一体化教室 上课时间周月日第节

More information

Android 编程基础 Android 开发教程 & 笔记 1

Android 编程基础 Android 开发教程 & 笔记 1 Android 开发教程 & 笔记 1 多式样 ProgressBar 撰写 : 地狱怒兽 联系 :zyf19870302@126.com 普通圆形 ProgressBar 该类型进度条也就是一个表示运转的过程, 例如发送短信, 连接网络等等, 表示一个过程正 在执行中 一般只要在 XML 布局中定义就可以了

More information

Eclipse C C++, or

Eclipse C C++,  or Eclipse C C++, Emailctchen@pl.csie.ntut.edu.tw or s1669021@ntut.edu.tw, s2598003@ntut.edu.tw http://pl.csie.ntut.edu.tw/~ctchen, http://www.ntut.edu.tw/~s2598003/ 2004/9/10 (0.02 ) Eclipse http://www.eclipse.org

More information

软件工程文档编制

软件工程文档编制 实训抽象类 一 实训目标 掌握抽象类的定义 使用 掌握运行时多态 二 知识点 抽象类的语法格式如下 : public abstract class ClassName abstract void 方法名称 ( 参数 ); // 非抽象方法的实现代码 在使用抽象类时需要注意如下几点 : 1 抽象类不能被实例化, 实例化的工作应该交由它的子类来完成 2 抽象方法必须由子类来进行重写 3 只要包含一个抽象方法的抽象类,

More information

C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1

C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1 C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 月 3 日 1 1 INPUTOUTPUT 1 InputOutput 题目描述 用 cin 输入你的姓名 ( 没有空格 ) 和年龄 ( 整数 ), 并用 cout 输出 输入输出符合以下范例 输入 master 999 输出 I am master, 999 years old. 注意 "," 后面有一个空格,"." 结束,

More information

宋守信 教授 杨书宏 教授级高工 傅 贵 教授 许开立 教授 高等工程教育专业认证委员会安全工程专业试点工作组 北京交通大学经济管理学院 北京 中国职业安全健康协会培训部 北京 中国矿业大学 北京 资源与安全工程学院 北京 东北大学资源与土木工程学院 沈阳 简要介绍我国高等工程教育专业认证试点的发展状况和主要做法 详细阐述安全工程专业认证以学生为本的指导思想和以质量保证及质量改进为出发点的基本特点

More information

(京)新登字063号

(京)新登字063号 教 育 部 职 业 教 育 与 成 人 教 育 司 推 荐 教 材 Java 程 序 设 计 教 程 ( 第 二 版 ) 沈 大 林 主 编 沈 昕 肖 柠 朴 曾 昊 等 编 著 内 容 简 介 Java 是 由 美 国 SUN 公 司 开 发 的 一 种 功 能 强 大 的, 具 有 简 单 面 向 对 象 分 布 式 可 移 植 等 性 能 的 多 线 程 动 态 计 算 机 编 程 语 言

More information

2 Java 语 言 程 序 设 计 教 程 1.2.1 简 单 性 Java 语 言 的 语 法 与 C 语 言 和 C++ 语 言 很 接 近, 使 得 大 多 数 程 序 员 很 容 易 学 习 和 使 用 Java 另 一 方 面,Java 丢 弃 了 C++ 中 很 少 使 用 的 很 难

2 Java 语 言 程 序 设 计 教 程 1.2.1 简 单 性 Java 语 言 的 语 法 与 C 语 言 和 C++ 语 言 很 接 近, 使 得 大 多 数 程 序 员 很 容 易 学 习 和 使 用 Java 另 一 方 面,Java 丢 弃 了 C++ 中 很 少 使 用 的 很 难 第 1 章 Java 概 述 Java 的 诞 生 Java 的 特 点 Java 开 发 环 境 安 装 与 配 置 创 建 并 运 行 一 个 简 单 的 Java 程 序 Java 语 言 是 当 今 计 算 机 软 件 行 业 中 最 热 门 的 网 络 编 程 语 言, 以 Java 为 核 心 的 芯 片 技 术 编 译 技 术 数 据 库 连 接 技 术, 以 及 基 于 企 业 级

More information

教学输入与学习者的语言输出 温晓虹 本文从三个方面探讨了语言的输入与输出的关系 首先从理论研究的角度讨 论了从语言输入到语言输出的习得过程 实验研究表明 输入的语言素材必须被学习者所接收 即使接收了的内容也并不会自动进入中介语的体系 而是需要进一步对输入语言进行 分解 归类等分析性与综合性的处理 在语言 内化 的基础上 学习者的中介语系统才能 够不断地得到重新组合 趋于目的语 另外 学习者在语言输出前和输出时需要调节

More information

Microsoft Word 杨超-spinner实现省市县的三级联动.docx

Microsoft Word 杨超-spinner实现省市县的三级联动.docx Spinner 实现省市县的三级联动 1801210908- 杨超 下拉框 Spinner 控件 : 常用属性 属性 说明 android:prompt 下拉提示信息 android:spinnermode 下拉显示方法 android:entries 配置下拉框数据源 android:dropdownwidth 下拉框显示模式下的显示项宽度 常用方法 方法 说明 getdropdownwidth()

More information

1. 2. Flex Adobe 3.

1. 2. Flex Adobe 3. 1. 2. Flex Adobe 3. Flex Adobe Flex Flex Web Flex Flex Flex Adobe Flash Player 9 /rich Internet applications/ria Flex 1. 2. 3. 4. 5. 6. SWF Flash Player Flex 1. Flex framework Adobe Flex 2 framework RIA

More information

<4D F736F F D20B5DA32D5C220416E64726F6964BFAAB7A2BBB7BEB3B4EEBDA82E646F6378>

<4D F736F F D20B5DA32D5C220416E64726F6964BFAAB7A2BBB7BEB3B4EEBDA82E646F6378> Android 应用程序开发与典型案例 作者 : 华清远见 第 2 章 Android 开发环境搭建 本章简介 本章主要介绍在 Windows 环境下,Android 开发环境的搭建步骤及注意事项, 包括 JDK 和 Java 开发环境的安装和配置 Eclipse 的安装 Android SDK 和 ADT 的安装和配置等 ; 同时介绍了 Android 开发的基本步骤 2.1 Android 开发环境的安装与配置

More information

* 4 6 R P r p . 1 2 3 4 7 89bk 6 5 1 2 3 4 5 6 7 8 9 0 bk r bl bm bn^ bo bl br bq bpbo bn bm [ ] [ ] [ ] bp 8 2 4 6 bq p [ ] [SET] br clckbt bs bs bt ck cl. 1 2 1 2+- 3 3 . 1 2 3 4 5 6 7 8 9 bk bl bm

More information

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6 www.brainysoft.net 1.JasperReport ireport...4 1.1 JasperReport...4 1.2 ireport...4 2....4 2.1 JDK...4 2.1.1 JDK...4 2.1.2 JDK...5 2.1.3 JDK...5 2.2 ant...6 2.2.1 ant...6 2.2.2 ant...6 2.3 JasperReport...7

More information

!"# $ %&'!"#$

!# $ %&'!#$ !"# $ %&'!"#$ 内容简介本书是在作者已经出版的 XilinxAlProgrammableZynq-7000SoC 设计指南 一书的基础上进行大幅度修订而成的 本书的一大特色就是更加突出 ARMCortex-A9 双核处理器的使用 此外, 在修订本书时采用了 Xilinx 最新的 Vivado2015 4 集成开发环境 通过本书的修订, 能反映最新的 ARM 嵌入式设计技术和实现方法, 同时也能更加凸显采用异构架构的

More information

DU Ad Platform_SDK 安卓接入指南 DU Ad Platform_SDK for Android 接入手册 ( 触发式广告 ) DUAd_SDK_Trigger v1.0 百度在线网络技术 ( 北京 ) 有限公司 百度在线网络技术 ( 北京 ) 有限公司 - 1 -

DU Ad Platform_SDK 安卓接入指南 DU Ad Platform_SDK for Android 接入手册 ( 触发式广告 ) DUAd_SDK_Trigger v1.0 百度在线网络技术 ( 北京 ) 有限公司 百度在线网络技术 ( 北京 ) 有限公司 - 1 - DU Ad Platform_SDK for Android 接入手册 ( 触发式广告 ) DUAd_SDK_Trigger v1.0-1 - 目录 1. 获取身份... 1 2. 加载与配置... 2 2.1 加载 SDK 文件... 2 2.2 配置 AndroidManifest.xml... 2 2.3 混淆代码... 3 3. 初始化... 4 4. 功能使用... 4 第 1 页共 1

More information

Linux服务器构建与运维管理

Linux服务器构建与运维管理 1 Linux 服务器构建与运维管理 第 2 章 :Linux 基本命令 阮晓龙 13938213680 / rxl@hactcm.edu.cn http://linux.xg.hactcm.edu.cn http://www.51xueweb.cn 河南中医药大学管理科学与工程学科 2018.3 2 提纲 目录与文件的操作 mkdir touch mv cp rm rmdir file tree

More information

Android Fragment

Android Fragment Android Fragment 建國科技大學資管系饒瑞佶 2017/10 V1 Android 3.0 後才支援 Fragment 解決部分 App 適應螢幕大小的問題 它類似於 Activity, 可以像 Activity 可以擁有自己的版面設計, 也和 Activity 一樣有自己的生命週期 ( 具備 oncreate() oncreateview() 與 onpause() 方法 ) LifeCycle

More information

序号:001

序号:001 第 一 组 选 题 简 介 序 号 :001 题 目 : 基 于 BPEL 的 网 上 订 餐 系 统 的 设 计 与 实 现 网 上 订 餐 系 统 是 在 互 联 网 上 进 行 菜 单 信 息 发 布 网 上 订 餐 以 及 维 护 客 户 关 系 的 电 子 商 务 系 统, 餐 饮 企 业 可 以 通 过 这 个 电 子 商 务 系 统 发 布 自 己 的 菜 单 信 息 以 供 客 户

More information

郑杭生等 一 杭州市 社会复合主体 的组织创新

郑杭生等 一 杭州市 社会复合主体 的组织创新 年第 期 从社会复合主体到城市品牌网群 以组织创新推进社会管理创新的 杭州经验 郑杭生 杨 敏 多年来 以 让我们生活得更好 的价值共识作为共同行动的基础 杭州经验 通过连续不断的系列创新 对 中国经验 的内核 构建 国家 社会 新型关系 促进 政府 企业 社会 的三维合作 使社会资源和社会机会形成优化配置 给予了独特的探索 最近几年中 杭州经验 从社会复合主体到城市品牌网群的新跨越 对 政府 企业

More information

PowerPoint 簡報

PowerPoint 簡報 UI 設計 Android 專案目錄架構 Android 專案建立後會自動產生 3 個主要目錄 src:java 程式檔案 res: 資源 ( 文字 圖形 聲音檔案等 ) 與 UI 設定有關的 layout 檔 此目錄內檔案名稱只能為小寫字母 數字 _. gen:r.java 根據 res 目錄內容自動產生 不要去修改 R.java Android 中所有的資源檔案 ( 圖片 XML 等 ) 命名都必須使用英文小寫,

More information

0511-Android程式之GPS應用_專題週記4

0511-Android程式之GPS應用_專題週記4 逢甲大學通訊工程學系專題研究 Android 程式之 GPS 應用 專題週記 0511 學生姓名 陳彥儒 D0035131 廖元譽 D0077791 指導老師 楊豐瑞老師繳交日期 2014.05.11 1 匯入 GoogleMap 1.1 取得授權步驟 目前進度 取得 Google 授權鑰匙 實作程式尚未成功 1.1.1 建立個人的 keystore 1.1.2 由個人的 keystore 查詢 SHA1

More information

图书在版编目渊 CIP 冤数据速成财富课院成就富翁的圆缘条法则 / 石向前著援北京院蓝天出版社袁 2005 援员园 ISBN 愿怨 -1 玉援速... 域援石... 芋援商业经营要通俗读物郁援 F71 缘原源怨中国版本图书馆 CIP 数据核字渊 2005 冤第 0 愿怨猿猿员号

图书在版编目渊 CIP 冤数据速成财富课院成就富翁的圆缘条法则 / 石向前著援北京院蓝天出版社袁 2005 援员园 ISBN 愿怨 -1 玉援速... 域援石... 芋援商业经营要通俗读物郁援 F71 缘原源怨中国版本图书馆 CIP 数据核字渊 2005 冤第 0 愿怨猿猿员号 25 图书在版编目渊 CIP 冤数据速成财富课院成就富翁的圆缘条法则 / 石向前著援北京院蓝天出版社袁 2005 援员园 ISBN 7-80158-6 愿怨 -1 玉援速... 域援石... 芋援商业经营要通俗读物郁援 F71 缘原源怨中国版本图书馆 CIP 数据核字渊 2005 冤第 0 愿怨猿猿员号 蓝天出版社出版发行渊北京复兴路 14 号冤渊邮政编码院 100843 冤电话院 66983715

More information

_banneradview.settest(true); _banneradview.setuserkeywords("swimming"); _banneradview.setusercategories("1,3,4"); _banneradview.setusergender(jdbanner

_banneradview.settest(true); _banneradview.setuserkeywords(swimming); _banneradview.setusercategories(1,3,4); _banneradview.setusergender(jdbanner 京东 APP 联盟 SDK Android 版接口说明文档 1.0 1. 嵌入 SDK 1.1 添加 SDK Android Studio 环境 : 菜单 New->New Module->Import.jar or.aar package, 然后选中 App 联盟 SDK 所带的 jar 文件 Eclipse+ADT 环境 : 将 App 联盟 SDK 所带的 jar 文件拷贝到 libs/ 目录下

More information

PowerPoint 演示文稿

PowerPoint 演示文稿 The BitCoin Scripting Language 交易实例 交易结构 "result": { "txid": "921a dd24", "hash": "921a dd24", "version": 1, "size": 226, "locktime": 0, "vin": [ ], "vout": [ ], "blockhash": "0000000000000000002c510d

More information

PowerPoint 演示文稿

PowerPoint 演示文稿 按钮对象 (button) 当 JavaScript 读到 标记中的 type 属性值为 button 时, 自动建立一个按钮对象, 并将该对象放到表单对象的 elements 数组当中 按钮对象包括 : 普通按钮 (button) 提交按钮 (submit) 重置按钮 (reset) 1. 使用按钮对象的格式 document.forms[ 索引值 ].elements[ 索引值

More information

01

01 ZEBRA 技术白皮书 条码编码 101 相关知识介绍 引言 20 70 数据 80 20 90 (JIT) AIAG EIA HIBCC HAZMAT 条码的优势提高数据准确性 99% 85% / / 提升效率 / 2 Zebra Technologies 保持一致性 ID 改进库存和资产管理 成本 / 效益分析 ID ID ID (ERP) RFID Zebra Technologies 3 ID

More information

APP 103 學 年 度 嶺 東 科 技 大 學 資 訊 網 路 系 專 題 研 究 報 告 嶺 東 中 華 民 國 一 四 年 五 月 1

APP 103 學 年 度 嶺 東 科 技 大 學 資 訊 網 路 系 專 題 研 究 報 告 嶺 東 中 華 民 國 一 四 年 五 月 1 嶺 東 科 技 大 學 資 訊 網 路 系 專 題 研 究 報 告 嶺 東 APP 指 導 老 師 : 陳 志 樺 教 授 組 員 : 陳 俊 瑋 陳 崇 緣 江 健 民 張 宏 銘 駱 佳 琪 中 華 民 國 一 四 年 五 月 1 APP 103 學 年 度 嶺 東 科 技 大 學 資 訊 網 路 系 專 題 研 究 報 告 嶺 東 中 華 民 國 一 四 年 五 月 1 誌 謝 本 專 題 報

More information

新美大酒店开放平台SDK(.NET版)使用说明.pages

新美大酒店开放平台SDK(.NET版)使用说明.pages SDK(.NET 版 ) 使 用说明 1 SDK 包说明 1.1 获取 SDK SDK 可以在数据平台下载, 也可直接通过下载地址获得 下载地址 : http://s3.meituan.net/v1/mss_de81c933e113413ea913a772b707b9c9/open-platform-sdk/mthotelopenplatform-sdk-1.0-net.zip 下载成功后, 解压后可获得

More information

單步除錯 (1/10) 打開 Android Studio, 點選 Start a new Android Studio project 建立專案 Application name 輸入 BMI 點下 Next 2 P a g e

單步除錯 (1/10) 打開 Android Studio, 點選 Start a new Android Studio project 建立專案 Application name 輸入 BMI 點下 Next 2 P a g e Android Studio Debugging 本篇教學除了最基本的中斷點教學之外, 還有條件式中斷的教學 條件式中斷是進階的除錯技巧, 在某些特定情況中, 我們有一個函數可能會被呼叫數次, 但是我們只希望在某種條件成立時才進行中斷, 進而觀察變數的狀態 而條件式中斷這項技巧正是符合這項需求 本教學分兩部分 單步除錯 (Page2~11, 共 10) 條件式中斷點 (Page12~17, 共 6)

More information

单击以编辑母片 Content 标题样式 LinearLayout 排版模式 TableLayout 排版模式 RelativeLayout 排版模式 AbsoluteLayout 排版模式 FrameLayout 排版模式 GridLayout 排版模式 TabWidget 切換卡 Lab 5 2

单击以编辑母片 Content 标题样式 LinearLayout 排版模式 TableLayout 排版模式 RelativeLayout 排版模式 AbsoluteLayout 排版模式 FrameLayout 排版模式 GridLayout 排版模式 TabWidget 切換卡 Lab 5 2 单击以编辑母片标题样式 安卓系统 Android 的排版 授课老师 : 谢兆贤 2016/4/18 1 单击以编辑母片 Content 标题样式 LinearLayout 排版模式 TableLayout 排版模式 RelativeLayout 排版模式 AbsoluteLayout 排版模式 FrameLayout 排版模式 GridLayout 排版模式 TabWidget 切換卡 Lab 5

More information

Photoshop CS6 艺术设计案例教程 ( 第二版 ) 1.1 Photoshop 的应用领域 Photoshop,,, Photoshop Photoshop 的用途 Photoshop CIS ( ) ( ) 案例展现 ~ 1

Photoshop CS6 艺术设计案例教程 ( 第二版 ) 1.1 Photoshop 的应用领域 Photoshop,,, Photoshop Photoshop 的用途 Photoshop CIS ( ) ( ) 案例展现 ~ 1 Chapter 01 Photoshop CS6 的基本操作 本章内容 1.1 Photoshop 的应用领域 1.6 控制面板的显示与隐藏 1.2 位图和矢量图的特性 1.7 新建 打开与保存文件 1.3 像素和分辨率的关系 1.8 图像的缩放 1.4 色彩模式 1.9 屏幕显示模式 1.5 Photoshop CS6 界面 1.10 计算机图形图像常用的色彩模式 Photoshop CS6 艺术设计案例教程

More information

* r p . 4 6 12 3 5 7 8 9bk bm btbsbrbqbp bo bn bl [ ] [ ] [ ] [ ] [SET] 1 2 3 4 5 6 7. cmcl ck 8 9 0 bk bl bm bn bo 1 2 1 2+ - bp bq 8 2 4 6 br r bs p bt ck cl cm 3 3 . 1 2 3 4 5 6 7 8 9 bk bl bm

More information

云数据库 RDS SDK

云数据库 RDS SDK 云数据库 RDS SDK SDK SDK 下载 SDK 下载 最新版本 java_sdk.zip python_sdk.zip php_sdk.zip c#_sdk.zip 历史版本 2015-11-3 java_sdk.zip python_sdk.zip php_sdk.zip c#_sdk.zip JAVA 教程 JAVA 创建 Access Key 登陆阿里云账号 打开 我的 Access

More information

工程项目进度管理 西北工业大学管理学院 黄柯鑫博士 甘特图 A B C D E F G 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 甘特图的优点 : 直观明了 ( 图形化概要 ); 简单易懂 ( 易于理解 ); 应用广泛 ( 技术通用 ) 甘特图的缺点 : 不能清晰表示活动间的逻辑关系 WBS 责任分配矩阵 ( 负责〇审批

More information