2009年3月22日星期日

UCON---使用控制方法

使用控制方法


在以上引用的研究包含许多针对特定目标问题的重要文章。同时,特定的焦点导致基本问题的广泛的系统的讨论的缺乏。访问控制、信任管理、DRM的研究有自身的轨迹,相互之间有很少的影响。尽管传统的访问控制应用到现在数字环境时有局限性,但是,在控制数字资源的安全策略和模型上也存在有价值的工作。同样地,尽管DRM已经打破了封闭系统限制,仍旧缺乏策略和模型良好定义的规范。而且,目前的权限表达语言不能表达事务级别的控制,包括易变性和连续性方面。今天的DRM技术要求良好定义的策略和模型,能更广泛地表达使用决策,能覆盖敏感信息保护,比如IPR保护。访问控制和信任管理要求放大他们的范围使之更丰富,更出色,持久地控制数字对象而和其位置无关。此外,在这些方法中没有能充分访问秘密发布的信息的方法。

UCON的目标是提供新的针对访问控制的智能化基础。正如以上讨论的,访问矩阵三十年的框架已经扩展到各种不同的方向,而研究者已经发现他不够他们的需要。纯粹的结果是表面上的过剩,特别是没有基础的智能的统一框架。在此篇论文中,我们打算提出一个崭新的观点,针对UCON本身的基础框架。因为我们从根本上重新考虑和扩展访问控制的基本特性,我们铸造UCON术语来传达我们正在讨论的广阔的观点。UCON的概念在我们前一篇论文 [Park and Sandhu 2002]中介绍了。UCON的概念是全面的,充分围绕传统的访问控制、信任管理、DRM展开。UCON系统地统一了这些领域到单个统一框架中,在自身的作用域中进行了超越。图1战士了UCON的覆盖范围和与其他研究领域的关系。根据目标,敏感信息保护已经是传统访问控制的重要目标之一。近来针对控制数字资源的使用的研究也集中在其他目标,诸如IPR保护、隐私保护。

图1


控制敏感信息的使用要求保护数字信息,数字信息可能是国家和组织关键资源。智能通讯和B2B食物是这个目标的良好例子。IPR保护或者数字版权保护是相对较新的目标。内容提供者的兴趣很大程度上属于这方面,因为,他们能实现最大收入。在控制数字信息使用的环境中,隐私是研究的很少的领域,但是,隐私逐渐开始受到更多的公众关注。W3C的最近的P3P项目是Web service的隐私支持的一个例子。健康保健信息系统是另外一个好例子,健康保健信息系统应该作为主要关注点来考虑隐私。UCON是中性的对象,系统性地覆盖所有的这些目标。

UCON属于有一对含义。在DRM环境中,UCON传达这个意义:在最终用户的系统中数字内容被提供来使用,但是提供者希望保留一些控制以限制用户对这些数字资源的操作。在隐私环境中,情况相反,最终用户经常提供个人信息给服务提供者,而希望控制服务提供者怎样使用这些个人信息。有时,个人信息被第三方起始者提供,例如健康保健提供者,然而,个人——叫做被指认人(UCON适合针对个人),希望行使一些个人信息使用的控制。使用也有持续时间的含义,因为,访问可以持续一段时间。在经典的访问控制中,通常的视点是:访问在访问被许可之前被强制实施,然后访问持续到一段时间而没有考虑将来检查。这对传统访问控制系统是适当的,但是没有反映许多现代电子商务系统的案例。

在UCON中,目标客体和使用者、提供者以及被识别主体有关系。使用者主体搜寻访问目标客体,目标客体由提供者主体提供。目标客体可能包含主体的隐私信息,这些主体叫做被识别主体,在客体中包含一定权限。使用判定针对目标资源基于这些不同的客体群中的关系进行判定。理想地,这些关系可以不再是一种方式控制判定,这种方式是当前通常的例子,提供者决定使用者的访问。在判定处理过程中,有多种方式的控制要求积极地包含这三方的每一方。而这可能是UCON的一种理想途径,这篇论文是舞台的第一步,而仅仅覆盖UCON核心方面,不考虑不同群或者多种方式的控制的关系。UCON的核心部分处理使用者主体使用的判定方面。在这篇论文中,我们介绍UCON ABC模型,作为UCON的核心模型。UCON ABC模型主要讨论基本控制问题,使用者主体基于目标客体的使用控制,而不覆盖任何不同主体群体的关系问题,也不涉及相关的管理问题。



传统上,访问控制处理授权作为判定过程的基础。在UCON ABC模型中,基于判定过程的授权利用主体属性和客体属性。属性能被识别,安全标签,特征,能力等。UCON ABC模型包含义务和条件,和授权一样,作为使用判定过程的一个部分,提供更加丰富和更加出色的判定能力。义务和条件的必要性被认识为现代业务系统诸如B2C大型分布式系统,以及,在业务伙伴之间的B2B事务和交互。义务是要求,针对使用许可,必须履行。条件是环境或者系统要求,独立于单个主体和客体。这些判定能被评估,在请求实行之前或者之中。另外,目标客体的使用可能请求一定的修改,这种变更针对主体或者客体属性,在使用实行之前、之中、之后(例如,通过电子书的价值减少请求者的账户余额)。UCON覆盖这些问题,在UCON ABC核心模型里,使用系统化方式。



从架构观点看,传统访问控制仅仅集中在服务器端控制,因此,几乎不考虑客户端控制,客户端控制甚至在数字资源发布后也提供持续地控制使用的能力。UCON ABC模型可以使用服务端和客户端控制架构,尽管有的功能细节可能不同。客户端控制要求存在客户端可信计算基础和引用监视器。最近的针对客户端控制的研究包括Palladum和TCPA都是前面提及的。值得信赖的客户端引用监视是一个相对问题,巨大地依赖包括隐私问题的业务模型需求。客户端引用监视器的细节不再这篇论文中讨论。这篇论文的主要焦点集中在模型级讨论,不考虑架构和实现细节。

2009年3月16日星期一

WORD文档结构混乱的解决办法

经常有同事抱怨我的文档结构混乱,无法索引,经过我的大力研究~~~~~

问题:
1、在word文档中使用自动编号,文档结构图会混乱,本来是正文的地方出现一级大纲
2、一个较长的文档,调整好格式并保存,以后重新打开,有时会发生正文莫名地改变为1级大纲级别的情况。
3、如果手动换页,换页后首行为标题,则在文档结构图中只能显示标题内容,不能显示编号。

实验:
经过观察发现在重新打开文档的过程中按esc键取消word对文档格式进行设置就可以正常,但是如果不按esc键取消的话,文档结构图就会出现混乱。

继续分析:
在打开word的时候左下角会有提示word自动更新文档样式(按esc键可以取消),只要这时候按esc键就可以看到正常的文档结构图了。由此推断是由于word自动更新文档样式造成的。因为word中加载了自定义模板,但是这个文档又从其它地方复制了许多乱七八糟的样式来,导致word自动更新样式的时候把正文的一些文字也加到了一级大纲视图中了。因此只要不让word自动更新文档样式就可以。解决办法可以是在文档中使用正确的样式,但是这很麻烦,而且有时候哪些不正确的样式会自动产生。

解决:
1、OFFICE2003
菜单“工具”——菜单“模板和加载项”——复选框“自动更新文档样式”,去掉勾
2、OFFICE2007
在打开word的时候左下角会有提示word自动更新文档样式,按esc键取消,然后在大纲模式下任意增加一行,保存,重新打开就解决了

2009年1月13日星期二

应用程序模型:应用、任务、进程、线程

在多数操作系统中,在应用程序驻留的可执行映像文件、运行的进程和用户交互的图标和应用之间有强1对1的关系。在Android操作系统中,这些关联更多的是不固定,重要的是理解不同的部分怎么整合在一起。
因为Android应用成需的灵活的特性,当实现程序的各个部分的时候,有一些基本术语需要理解。
1、android包(简称.apk)是包容程序代码和资源的文件。这也是发布应用程序的文件,当用户在其设备上安装应用程序时,由用户下载这个文件。
2、一般一个任务是用户觉察出的作为一个能运行的应用程序的东西:通常一个任务在HOME屏幕有一个图标,通过这个图标可以被用户访问,作为顶层条目,任务可获得,能在其他任务前面放到前台。
3、进程是低级核心处理过程,用于运行应用程序代码。通常,在.apk中的所有代码只运行在一个进程中,每个.apk都有专用的进程。但是,进程标签能用于修改代码运行的地方,要么针对整个.apk,要么针对单个活动、接收器、服务、提供者、组件。

任务

这里关键点:当用户看到认为他们是一个应用程序时,实际上处理的东西就是一个任务。如果你仅仅创建一个带有大量活动的.apk,其中一个活动是顶级入口点(通过一个intent过滤器针对操作android.intent.action.MAIN和分类 android.intent.category.LAUNCHER),那么的确有一个任务针对你的.apk创建起来,从这里启动的任何活动也作为那个任务的一部分运行。
一个任务,来自用户的眼中看,是应用程序;从程序开发者眼中看,是一到多个活动,用户已经越过进入那个任务,但是还没有关闭,或者,一个活动堆栈。一个新任务是通过使用Intent.FLAG_ACTIVITY_NEW_TASK标志启动一个活动Intent来创建的;这个Intent会用于作为任务的根Intent,定义是什么任务。任何没有使用这个标志启动的活动会运行在和正在运行的那个活动所在的相同的任务中(除非活动已经请求一个特殊的启动模式,稍后讨论)。任务能被排序:如果你使用FLAG_ACTIVITY_NEW_TASK但是已经有一个任务运行于那个Intent,当前任务的活动堆栈会送到前台而不是启动一个新的任务。
FLAG_ACTIVITY_NEW_TASK必须小心地单独使用:使用这个标志就说明,从用户的角度看,一个新的应用程序在这个点启动。如果这不是你喜欢的行为,你应该不创建一个新任务。另外,如果用户可能从HOME屏幕回退到原来的位置,你应该仅仅使用新任务标志,以运行和新任务同样的Intent。否则,如果用户从正在运行的任务中按下HOME键而不是BACK键,你的任务机器活动将会在home屏幕之后被排序,而没有办法返回。

任务亲和力

在一些情况下,Android需要知道活动属于哪一个任务,即使当活动没有运行在指定的任务中时。这是通过任务亲缘关系完成的,任务亲缘关系提供一个唯一的任务静态名称,一到多个活动打算运行在具有静态名称的任务中。一个活动默认的任务亲和力是实现活动的.apk包的名称。这就提供了通常的可期望的行为,其中,在指定.apk文件中所有活动对用户来说是单个应用程序的一个部分。
当启动一个新活动而不是Intent.FLAG_ACTIVITY_NEW_TASK标志的时候,任务亲缘关系没有影响新活动运行的任务:总是运行在触发任务的活动的任务中。但是,如果NEW_TASK被使用,那么亲缘关系会用于决定一个任务是否和一个相同的亲缘关系已经存在。如果是已经存在,那么任务将会被提到前台,新活动运行在那个任务的顶层。
这种行为对于必须使用NEW_TASK标志的情况是非常有用的,特别是从状态栏通知或者home屏幕快捷方式运行活动时。结果是:当用户使用这种方式运行你的程序时,当前任务状态会拉到前台,用户现在想看到的活动被放到顶层。
你能在manifest的应用程序标签中指定你自己的任务亲缘关系,针对.apk的所有活动,或者针对单个活动的活动标签。怎么使用亲缘关系的例子如下:
1、如果你的.apk包含多个顶层的用户能运行的应用程序,那么你会可能想分派不同的亲和力到用户从你的.apk中找到的每个活动。使用不同的名称的好的惯例是添加你的.apk包名称,使用克隆的独立的字符串。例如,"com.android.contacts".apk文件可能有亲缘关系"com.android.contacts:Dialer" 和 "com.android.contacts:ContactsList"。
2、如果你正在替换通知、快捷方式、其他内嵌的在内部运行的活动,那么你可能需要显式设置你替换的活动的任务亲缘关系为与你替换的应用程序相同。例如,如果你正在替换通讯录详细视图(用户可以创建和调用快捷方式),那么你会想设置任务亲缘关系到"com.android.contacts"。

运行模式和运行标志

控制活动和任务交互的主要方法是通过活动的launchMode属性和与Intent关联的标志。这两个参数能一同工作在控制活动运行的结果的各种方法之下,正如在关联文档中描述的那样。在这里,我们将会看到一些常用的用例和这些参数的组合。
最常用的运行模式(除了默认的标准模式外)是singleTop。这不会对任务有影响,这种模式恰好避免不同时间启动同一个活动(在堆栈顶层)
singleTask运行模式对任务有一个主要的影响:导致活动总是启动在新任务里(或者,已存在的任务被拉到前台)。使用这种模式要求关心怎么和系统的其他部分交互,因为它影响到活动的每个路径。这种模式应该仅被用于作为应用程序前门的活动(即,支持MAIN操作和LAUNCHER分类)
singleInstance运行模式更多地是专用的,应该仅仅用于完全作为一个活动来实现应用程序的情况下。
你经常会运行进入的一种情况是另外一个实体(比如:SearchManager、NotificationManager)启动你的其中一个活动。在这种情况下,Intent.FLAG_ACTIVITY_NEW_TASK标志必须使用,因为活动在任务的外部启动(应用程序和任务甚至可以不存在)。正如前面描述的,这种情况下的标准行为是,把当前任务提到前台,当前任务匹配新活动的affinity,并在顶层启动新活动。但是,有您能实现的其他类型的行为。
一个普通的方法是,与NEW_TASK一起使用Intent.FLAG_ACTIVITY_CLEAR_TOP标志。通过这样做,如果你的任务已经在运行,那么将会被提到前台,堆栈中的所有活动将被清除,除了根活动,根活动的onNewIntent(Intent)在Intent被启动的时候被调用。注意,当使用这种方法的时候,活动经常使用singleTop或者singleTask运行模式,因此,当前实例给予新Intent而不是请求销毁和产生新实例。
你能采用的另外一个办法是,设置通知活动的任务affinity为空串(表示无affinity),并设置finishOnBackground属性。这种办法是有用的,如果你将会希望通知把用户带到一个分离的活动,胜于返回到应用程序的任务。通过指定这个属性,不论用户是否使用BACK或者HOME离开活动,活动都会完成。如果属性没有被指定,按HOME将会导致进入活动,其任务保持在系统中,可能没有办法返回。
保证阅读launchMode属性和Intent标志相关文档,以了解这些选项。

进程

在Android操作系统中,进程完全是应用程序的具体实现,不是用户通常认为的那种东西。他们的主要用途是简单的:
1、改善稳定性或者安全性,通过把未信任或者不稳定的代码放入独立的进程的方法来解决
2、简化在同一进程中多个.apk文件的代码的运行
3、有助于系统管理资源,通过吧重量级代码放入独立的进程,可以被杀掉,而且和程序的其他部分无关。
正如前面描述的那样,进程属性用于控制细小程序组件运行的进程。注意,这些进程属性不能用于违反系统安全规则:如果没有共享同一个用户ID的两个.apk文件试图运行在同一个进程中,那么,这是不允许的,不同的进程将会分别针对每个.apk文件被创建。
参见安全相关的文件了解更多的关于安全规则的信息。

线程

每个进程有一到多个线程运行在其中。在多数情况下,Android避免在进程中创建额外的线程,保证应用程序是单线程的,除非应用程序自己又创建了线程。重要印证是,注意,新线程不会针对每个Activity/BroadcastReceiver/Servie/ContentProvider实例:这些程序组件在对应的进程中实例化(除非另作说明所有的组件都位于同一个进程),在那个进程的主线程中。这意味着,当被系统调用的时候,没有组件(包括服务)应该在进程中执行长的或者阻塞的组件(例如网络调用或者计算循环),因为这将会阻塞所有进程中的其他组件。你可以使用标准库线程类或者Android的HandleThread类来在另外一个线程中执行长操作。
There are a few important exceptions to this threading rule:
线程规则有几处例外:
1、到IBinder的访问或者在IBinder上实现的接口从调用它们的线程被调度,或者如果来自其他进程则从本地进程中的线程池被调度,不是从他们的进程的主线程被调度。特别地,到服务的IBinder的访问将会以这种方式调用。(虽然到在服务自身的调用来自主线程。)这意味着,IBinder接口的实现必须总是在线程安全模式下编写,因为他们能在同一时间从许多随机线程中被调用。
2、到ContentProvider的调用从调用线程或者IBinder所在的主线程被调度。在ContentProvider类的相关文档中这个方法有描述。这意味着,这些方法的实现必须总是以线程安全方式编写,因此,他们能在同一时刻从许多随机线程被调用。
3、在View和它的子类上调用来自视图窗口运行的线程。正常情况下,这将会是进程的主线程,但是如果你创建一个线程,然后从线程中显示一个窗口,那么这个窗口的视图层次将会从那个线程被调用。

2009年1月4日星期日

开发工具

Android SDK包含多种自定义的工具,用来帮组你开发移动程序。最重要的是模拟器以及ADT插件,但是SDK还包括其他的工具用于调试、打包、安装。

模拟器

虚拟手机设备,运行在你的电脑上。你使用模拟器来设计、调试、测试你的应用程序。

层次阅读器

该工具允许你调试和优化你的用户接口。提供可视化表现视图的多层次布局,使用像素栅格的当前界面的放大检查器,因此你能获得正确的界面。

Draw 9-patch

Draw 9-patch工具允许你轻易地创建NinePatch图形,使用WYSIWYG编辑器。预览图片的扩展版本,高亮化选择的区域。

ADT插件

ADT插件增加了强力的扩展功能,集成Eclipse环境,使创建和调试程序更加容易和快速,在开发Android程序时,ADT插件给你难以置信的促进
1、允许你在Eclipse IDE中访问其他的Android开发工具。例如,ADT提供了DDMS工具的许多能力——屏幕截图、管理端口转发、设置断点、观察线程和进程信息。
2、提供新项目向导,帮助你快速创建和设置新程序需要的基本文件。
3、自动化和简化建立你的Android程序的过程;
4、提供Android代码编辑器,有助于编写有效的manifest和资源XML文件。

Dalvik调试监视服务(ddms)

和Dalvik(Android平台的自定义虚拟机)集成,这个工具让你管理模拟器或者设备上的进程,辅助调试。你能使用这个工具来销毁进程,选择指定的进程调试,产生跟踪数据,查看内存堆和线程信息,对模拟器或者设备进行屏幕截图。

调试桥

adb工具让你在模拟器或者设备上安装程序的.apk文件,从命令行访问模拟器或者设备。你也能使用这个工具来连接标准的调试器到运行在模拟器或者设备上的程序代码。

资产打包工具(aapt)

aapt工具让你创建.apk文件,包含程序的二进制和资源文件。

接口描述语言(aidl)

让你产生进程间接口的代码,比如服务可能使用的代码。

sqlite3

顺便包括这个工具,该工具让你访问SQLite数据文件,数据文件被程序创建和使用。

Traceview

这个工具产生图形化的分析视图,该视图跟踪你在程序中产生的日志数据。

mksdcard

帮组你创建在模拟器中能使用的磁盘图片,来模拟外部存储卡(比如SD卡)的表现。

dx

dx工具重写.class字节代码进入Android字节代码(存储在.dex文件)中。


UI/程序练习猴子

Monkey是运行在你的模拟器或者设备上的一段程序,产生用户事件的伪随机流,比如点击、触摸、手势,还有系统级的事件。你能使用Monkey来进行强度测试,使用随机的但是重复的方式来进行。

activitycreator

产生Ant建立文件的描述脚本,Ant建立文件用于编译Android程序。如果你正在使用ADT插件开发,那么你不会需要使用这个描述脚本。

2008年12月30日星期二

教程:额外的收益

本练习中,你将会使用调试器来查看你在练习3中做的工作。本练习证明:
1、怎么设置断点来观察执行情况
2.怎么运行你的应用程序在调试模式下。

第一步

使用加工过的Notepadv3,在NoteEdit类的onCreate(), onPause(), onSaveInstanceState() 和 onResume()方法的开头处,设置断点。(如果不熟悉Eclipse,在你想设置断点的行上,对应编辑窗口的左边的窄窄的灰色边框里面右击,选择Toggle Breakpoint,你应该会看到蓝点显示)

第二步

现在,在调试模式下启动notepad示例程序
1、在Notepadv3项目上右击,从调试菜单选择Debug As -> Android Application
2.Android模拟器应该简单地说“waiting for debugger to connect(等待调试器链接)”,然后运行应用程序
3、如果一直停留在“waiting...”屏幕下,退出模拟器和Eclipse,从命令行使用adb销毁服务器,然后重新启动。

第三步

当你编辑或者创建新标签的时候,你应该看到断点命中,执行停止。

第四步

点击Resume按钮让执行继续(在顶部的Eclipse工具栏右边带有绿色三角形的黄色矩形)

第五步

实验确认和回退按钮,试着按Home键,改为其他模式。观察生命周期时间产生了什么,什么时候触发。
ADT插件不仅提供优良的调试支持,而且卓越的剖析能力支持。你能试着使用TraceView来剖析你的应用程序。如果你的应用成运行很慢,这能帮助你发现瓶颈,并纠正问题。

教程:Notepad练习3

教程:Notepad练习3

这个练习中,你将会使用生命周期事件回调来保存和取回应用程序状态数据。这个练习说明:
1、生命周期事件和你的程序怎么能使用生命周期事件
2、维持应用程序状态的技术

第一步

导入Notepadv3进入Eclipse。这个练习的开始点正好是Notepadv2最后停止的地方。
目前这个程序有一些问题——当编辑导致崩溃时点击回退按钮,其他在编辑期间发生的任何事情将会导致编辑数据丢失。
为了修正这个问题,我们将会把创建和编辑便签的大部分功能移到NoteEdit类,针对编辑便签功能介绍一个完整的生命周期。
1、删除NoteEdit中从额外Bundle分析标题和内容的代码
作为替换,我们打算使用DBHelper类来从数据库中直接访问便签。我们需要传入到NoteEdit活动的参数只有mRowId(但是仅仅在编辑时,创建时我们不传入任何参数)。删除下面的行:
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);

2、我们也会除去在额外Bundle中传入的属性,这些属性用于设置UI中标题和内容文本编辑框的值。因此删除:
if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}

第二步

在NoteEdit类的顶部,创建NotesDbAdapter的私有属性:
private NotesDbAdapter mDbHelper;

也在onCreate()方法中增加NotesDbAdapter的实例(在super.onCreate()调用的右下面)
mDbHelper = new NotesDbAdapter(this);

mDbHelper.open();

第三步

在NoteEdit中,我们需要为了mRowId检查savedInstanceState,万一正在编辑的便签包含Bundle的一个已经保存的状态,我们应该恢复这个状态(如果Activity失去焦点然后重新启动,恢复动作将会发生)
1、替换目前的初始化mRowId的代码:
mRowId = null;

Bundle extras = getIntent().getExtras();
if (extras != null) {
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
}

为:

mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
: null;
if (mRowId == null) {
Bundle extras = getIntent().getExtras();
mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
: null;
}


2、注意检查savedInstanceState的null值,我们仍然需要从额外Bundle中加载mRowID,如果mRowID没有被savedInstanceState提供。这是一个三元操作符的简写,以便保证不是使用这个值就是null,不管它是否满足条件。

第四步

下面,我们需要填充基于mRowID的字段:
populateFields();

这行代码在confirmButton.setOnClickListener()之前运行。我们会待会儿定义这个方法。

第五步

从onClick()处理函数中除去Bundle创建和Bundle值设置.Activity不再需要返回任何附加信息到调用者。因为我们不再有返回的Intent,我们会使用setResult()的更简版本:
public void onClick(View view) {
setResult(RESULT_OK);
finish();
}

我们会实现功能:保存修改或者新建的便签到数据库中,使用生命周期方法。
onCreate()的代码如下:
super.onCreate(savedInstanceState);

mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();

setContentView(R.layout.note_edit);

mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);

Button confirmButton = (Button) findViewById(R.id.confirm);

mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
: null;
if (mRowId == null) {
Bundle extras = getIntent().getExtras();
mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
: null;
}

populateFields();

confirmButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View view) {
setResult(RESULT_OK);
finish();
}

});


第六步

定义populateFields()方法
private void populateFields() {
if (mRowId != null) {
Cursor note = mDbHelper.fetchNote(mRowId);
startManagingCursor(note);
mTitleText.setText(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
mBodyText.setText(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
}
}

这个方法使用NotesDbAdapter.fetchNote()方法来找到编辑的便签,然后调用startManagingCursor(),这个方法是Android共用程序,负责Cursor的生命周期。在Activity生命周期指示下这个方法会释放和重建资源,因此,我们不必担心这些,做自己该做的事情。之后,我们只从游标中查找标题和内容的值,并用来填充View元素。

第七步


处理生命周期事件为什么是重要的?
如果你过去习惯于总是控制你的程序,那么你可能不理解生命周期所有事情为什么必须的。原因是,在Android中,你不能控制你的Activity,而是操作系统控制。
正如我们已经看到的那样,Android模型基于可以互相调用的活动。当一个活动调用另外一个时,目前的活动暂停,如果系统运行在低资源环境下,可能被遗弃杀掉。如果被杀掉,你的活动将不得不保存充足的状态,以便将来恢复,我们希望恢复时的状态和被杀掉的状态相同。
Android有一个定义明确的生命周期。即使你不显式控制另外一个活动,生命周期时间也能发生。例如,也许一个电话到达到话筒,如果电话到达,你的Activity正在运行,那么这个活动会被换出,而由呼叫活动替换。
仍旧在NoteEdit类中,我们重载onSaveInstanceState()、onPause() 和 onResume()方法。这些是我们自己的生命周期方法(连同我们已经有的onCreate())。
如果Activity被停止,onSaveInstanceState()被Android自动调用,在重新开始以前可以被杀掉。这意味着,应该保存任何必须的状态以保证Activity重新启动时重新初始化到同样的环境。这是到onCreate()方法的副本,实际上,传入到onCreate()的savedInstanceState Bundle是和onSaveInstanceState()的outState相同的Bundle.
onPause() 和 onResume()是免费赠送的方法。当Activity结束的时候,onPause()总是被调用,及时我们主动(例如调用finish())。我们会使用来保存当前的标签到数据库。最佳办法是释放在onPause()内部能不释放的任何资源,在被动状态占用更少的资源。基于这个原因,我们将会关闭DBHelper类,设置属性到空,以便能进行垃圾回收。另外一个方面,onResume()会重建mDbHelper实例,因此我们能使用它,然后又读取数据库的便签数据,重新填充这些字段。
因此,在populateFields()方法后增加一些空间,并且增加下面的生命周期方法:
1. onSaveInstanceState():

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}

2. onPause():

@Override
protected void onPause() {
super.onPause();
saveState();
}

We'll define saveState() next.
3. onResume():

@Override
protected void onResume() {
super.onResume();
populateFields();
}

第八步

定义saveState()方法,从数据库中取出数据
private void saveState() {
String title = mTitleText.getText().toString();
String body = mBodyText.getText().toString();

if (mRowId == null) {
long id = mDbHelper.createNote(title, body);
if (id > 0) {
mRowId = id;
}
} else {
mDbHelper.updateNote(mRowId, title, body);
}
}

注意:我们从creaeNote()捕获返回值,如果返回了一个有效的行号,我们保存在mRowID属性中,以便我们将来能修改便签,而不是创建一个新的便签(另外,如果生命周期时间被触发,那么创建可能发生)

第九步

现在,在Notepadv3类中从onActivityResult()中剥离出先前的处理代码
在NoteEdit生命周期内发生所有便签查询和修改操作,因此,onActivityResult()方法需要做的事情是修改数据视图,其他工作不需要。最终的代码如下
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
fillData();
}

因为其他类做这个工作,必须做的事情是刷新数据。

第十步


也要从onListItemClick()中移去设置标题和内容的行(他们不再需要,仅仅需要mRowID):
Cursor c = mNotesCursor;
c.moveToPosition(position);

同样要移去:
i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
c.getColumnIndex(NotesDbAdapter.KEY_TITLE)));
i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
c.getColumnIndex(NotesDbAdapter.KEY_BODY)));


因此剩下的代码应该是:
super.onListItemClick(l, v, position, id);
Intent i = new Intent(this, NoteEdit.class);
i.putExtra(NotesDbAdapter.KEY_ROWID, id);
startActivityForResult(i, ACTIVITY_EDIT);

现在,你也能移去mNotesCursor属性,使用fillData()方法中的本地变量:
Cursor notesCursor = mDbHelper.fetchAllNotes();

注意:mNotesCursor中的m表示一个成员变量,因此当我们产生本地变量notesCursor的时候,我们销毁m。
运行程序!(右键菜单中,点击Run As -> Android Application)

2008年12月28日星期日

教程:Notepad练习2

Tutorial: Notepad Exercise 2
教程:Notepad练习2

In this exercise, you will add a second Activity to your notepad application, to let the user create, edit, and delete notes. The new Activity assumes responsibility for creating new notes by collecting user input and packing it into a return Bundle provided by the intent. This exercise demonstrates:
在这个练习之中,你会增加第二个活动进入你的notepad程序,让用户创建、编辑、删除便签。新的Activity呈现的职责是创建新的便签来收集用户输入、打包进入一个返回的由intent提供的Bundle,这个练习证明了:
* Constructing a new Activity and adding it to the Android manifest
1、构造新Activity,增加进入Android manifext
* Invoking another Activity asynchronously using startActivityForResult()
2、使用startActivityForResult()异步调用另外一个Activity
* Passing data between Activity in Bundle objects
3、在Activity间使用Bundle对象传递数据
* How to use a more advanced screen layout
4、怎么使用更多的高级界面布局

Step 1
第一步

Create a new Android project using the sources from Notepadv2 under the NotepadCodeLab folder, just like you did for the first exercise. If you see an error about AndroidManifest.xml, or some problems related to an android.zip file, right click on the project and select Android Tools > Fix Project Properties.
创建新Android项目,使用NotepadCodeLab文件夹的Notepadv2源代码,和练习1的做法相同。如果你看到AndroidManifest.xml的错误,或者android.zip的问题,在project上右击,选择Android Tools > Fix Project Properties
Open the Notepadv2 project and take a look around:
打开Notepadv2项目,全部看一看:
* Open and look at the strings.xml file under res/values — there are several new strings which we will use for our new functionality
1、打开strings.xml 并查看——新功能会用到几个新的字符串
* Also, open and take a look at the top of the Notepadv2 class, you will notice several new constants have been defined along with a new mNotesCursor field used to hold the cursor we are using.
2、打开 Notepadv2类,看看顶部的代码,你会注意到定义了几个新常量,和新的mNotesCursor属性一起用于存储我们正在使用的游标。
* Note also that the fillData() method has a few more comments and now uses the new field to store the notes Cursor. The onCreate() method is unchanged from the first exercise. Also notice that the member field used to store the notes Cursor is now called mNotesCursor. The m denotes a member field and is part of the Android coding style standards.
3、fillData()方法有更多的注释,使用新的字段来保存便签游标。onCreate()方法没有改变。用于保存便签游标的成员变量现在称为mNotesCursor。m指示一个成员属性,是Android代码风格标准的一部分。
* There are also a couple of new overridden methods (onListItemClick() and onActivityResult()) which we will be filling in below.
4、还有一对新的重载方法:onListItemClick() 和onActivityResult(),下面我们会填充它们。

Step 2
第二步

Add an entry to the menu for deleting a note:
增加删除便签的菜单入口:
1. In the onCreateOptionsMenu() method, add a new line:
1、在方法onCreateOptionsMenu()中增加新行:
menu.add(0, DELETE_ID, 0, R.string.menu_delete);

2. The whole method should now look like this:
2、整个方法的代码如下:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
return true;
}

Step 3
第三步

In the onMenuItemSelected() method, add a new case for DELETE_ID:
onMenuItemSelected()增加新代码,关联DELETE_ID:
mDbHelper.deleteNote(getListView().getSelectedItemId());
fillData();
return true;

1. Here, we use the deleteNote method to remove the note specified by ID. In order to get the ID, we call getListView().getSelectedItemId().
1、使用deleteNote方法,通过ID移除便签。为了获取ID,我们调用getListView().getSelectedItemId()
2. Then we fill the data to keep everything up to date.
2、我们填充数据以保证每件事情最新
The whole method should now look like this:
整个方法的代码:
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
case INSERT_ID:
createNote();
return true;
case DELETE_ID:
mDbHelper.deleteNote(getListView().getSelectedItemId());
fillData();
return true;
}

return super.onMenuItemSelected(featureId, item);
}

Step 4
第四步

Starting Other Activities
启动其他的Activity
In this example our Intent uses a class name specifically. As well as starting intents in classes we already know about, be they in our own application or another application, we can also create Intents without knowing exactly which application will handle it.
这个例子中,Intent使用一个特定的类名。又在类中启动intent,正如我们知道的那样,是他们在我们自己的程序或者其他程序中,我们也能创建Intent而不知道哪个程序处理它。
For example, we might want to open a page in a browser, and for this we still use an Intent. But instead of specifying a class to handle it, we use a predefined Intent constant, and a content URI that describes what we want to do. See android.content.Intent for more information.
例如,我们可能想在浏览器中打开一个页面,为了这个目的,我们人就使用Intent。但不是指定一个类来处理它,我们使用一个预定义的Intent常量,一个内容URI(描述我们想做什么)。欲知更多信息,请看“android.content.Intent ”。

Fill in the body of the createNote() method:
填写createNode()方法的代码:
Create a new Intent to create a note (ACTIVITY_CREATE) using the NoteEdit class. Then fire the Intent using the startActivityForResult() method call:
创建新的Intent来创建一个便签(ACTIVITY_CREATE),使用NoteEdit类。然后,调用startActivityForResult(),触发Intent:
Intent i = new Intent(this, NoteEdit.class);
startActivityForResult(i, ACTIVITY_CREATE);

This form of the Intent call targets a specific class in our Activity, in this case NoteEdit. Since the Intent class will need to communicate with the Android operating system to route requests, we also have to provide a Context (this).
这种Intent调用的形式瞄准Activity中的特定类,这个例子中指NoteEdit。因为Intent类会需要与Android操作系统通讯,目的是路由请求消息,我们也不得不提供Context(this)。
The startActivityForResult() method fires the Intent in a way that causes a method in our Activity to be called when the new Activity is completed. The method in our Activity that receives the callback is called onActivityResult() and we will implement it in a later step. The other way to call an Activity is using startActivity() but this is a "fire-and-forget" way of calling it — in this manner, our Activity is not informed when the Activity is completed, and there is no way to return result information from the called Activity with startActivity().
startActivityForResult()方法触发Intent,这种方法促使Activity中的方法在新Activity完成时被调用。Activity中接受回调的方法是onActivityResult(),在稍后步骤中会实现它。调用Activity的其他方式是使用startActivity(),但是这种方法是"fire-and-forget"(发射后忘记)——使用这种方法,Activity完成时Activity消息并不灵通,startActivity()调用的Activity没有办法返回结果信息。
Don't worry about the fact that NoteEdit doesn't exist yet, we will fix that soon.
不要担心NoteEdit,我们会校正这个问题。

Step 5
第五步

Fill in the body of the onListItemClick() override.
填写onListItemClick()重载代码。
onListItemClick() is a callback method that we'll override. It is called when the user selects an item from the list. It is passed four parameters: the ListView object it was invoked from, the View inside the ListView that was clicked on, the position in the list that was clicked, and the mRowId of the item that was clicked. In this instance we can ignore the first two parameters (we only have one ListView it could be), and we ignore the mRowId as well. All we are interested in is the position that the user selected. We use this to get the data from the correct row, and bundle it up to send to the NoteEdit Activity.
onListItemClick()是一个回调函数,我们将会重载它。当用户从列表中选择一项时该函数自动被系统调用。四个参数:ListView对象、View(点击的ListView里面)、点击的列表位置、nRowId(点击的条目的记录ID)。这个例子中,我们忽略前面两个参数,也忽略mRowId。我们感兴趣的是用户选择的位置。我们使用这个方法获得正确行的数据,捆扎在一起送到NoteEdit活动。
In our implementation of the callback, the method creates an Intent to edit the note using the NoteEdit class. It then adds data into the extras Bundle of the Intent, which will be passed to the called Activity. We use it to pass in the title and body text, and the mRowId for the note we are editing. Finally, it will fire the Intent using the startActivityForResult() method call. Here's the code that belongs in onListItemClick():
在回调函数的实现中,创建Intent,以便使用NoteEdit类编辑便签。然后,增加数据进入Intent的附加Bundle中。新建的Intent对象传送给被调用的活动。我们使用它传入标题和内容文本,以及我们编辑的便签的mRowId。最后,使用startActivityForResult()方法发射Intent。下面的代码是onListItemClick()的实现:
super.onListItemClick(l, v, position, id);
Cursor c = mNotesCursor;
c.moveToPosition(position);
Intent i = new Intent(this, NoteEdit.class);
i.putExtra(NotesDbAdapter.KEY_ROWID, id);
i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
startActivityForResult(i, ACTIVITY_EDIT);

* putExtra() is the method to add items into the extras Bundle to pass in to intent invocations. Here, we are using the Bundle to pass in the title, body and mRowId of the note we want to edit.
1、putExtra()方法增加条目到附加的Bundle中,传入参数intent的引用。此处,我们使用Bundle来传入标题、内容、我们想编辑的便签的记录ID
* The details of the note are pulled out from our query Cursor, which we move to the proper position for the element that was selected in the list, with the moveToPosition() method.
2、从查询游标中读出便签的详细信息,游标的位置移动到列表中选中的元素的位置,使用moveToPosition()方法移动。
* With the extras added to the Intent, we invoke the Intent on the NoteEdit class by passing startActivityForResult() the Intent and the request code. (The request code will be returned to onActivityResult as the requestCode parameter.)
3、附加信息加入Intent中,我们在NoteEdit类型中调用Intent,吧Intent和请求代码传递给startActivityForResult()。(请求代码作为requestCode参数在onActivityResult中返回。)
Note: We assign the mNotesCursor field to a local variable at the start of the method. This is done as an optimization of the Android code. Accessing a local variable is much more efficient than accessing a field in the Dalvik VM, so by doing this we make only one access to the field, and five accesses to the local variable, making the routine much more efficient. It is recommended that you use this optimization when possible.
注意:我们把mNotesCursor属性赋值到本地变量,这是对Android代码的优化。访问本地变量比访问DalvikVM的一个属性更有效率,因此,这样做了后我们访问了这个属性一次,访问本地变量5次,使程序有效得多。推荐尽可能的使用这种优化方法。

Step 6
第六步

The above createNote() and onListItemClick() methods use an asynchronous Intent invocation. We need a handler for the callback, so here we fill in the body of the onActivityResult().
上面提到的createNote()和onListItemClick()方法使用了异步Intent引用。我们需要处理回调行数,因此,此处我们填充onActivityResult()代码。
onActivityResult() is the overridden method which will be called when an Activity returns with a result. (Remember, an Activity will only return a result if launched with startActivityForResult.) The parameters provided to the callback are:
onActivityResult()是一个重载方法,当Activity返回结果的时候被调用。(记住,如果使用startActivityForResult触发,则Activity仅仅会返回一个结果。) 回调函数提供的参数如下:
* requestCode — the original request code specified in the Intent invocation (either ACTIVITY_CREATE or ACTIVITY_EDIT for us).
1、requestCode——在Intent引用中指定的原始请求代码(要么是ACTIVITY_CREATE,要么是ACTIVITY_EDIT)
* resultCode — the result (or error code) of the call, this should be zero if everything was OK, but may have a non-zero code indicating that something failed. There are standard result codes available, and you can also create your own constants to indicate specific problems.
2、resultCode——调用的结果或者错误代码,如果一切都没有问题,应该是0,但是可以是非0值,则表示有错误发生。有标准的结果代码获得,你也可以创建你自己的常量来指示特定错误。
* intent — this is an Intent created by the Activity returning results. It can be used to return data in the Intent "extras."
3、intent——通过有返回结果的Activty创建的Intent。他能用于返回数据,数据存放在Intent的“extras”中。
The combination of startActivityForResult() and onActivityResult() can be thought of as an asynchronous RPC (remote procedure call) and forms the recommended way for an Activity to invoke another and share services.
startActivityForResult()和onActivityResult()的结合能作为异步RPC,提供活动相互调用和共享服务的推荐方法。
Here's the code that belongs in your onActivityResult():
下面是onActivityResult()的代码:
super.onActivityResult(requestCode, resultCode, intent);
Bundle extras = intent.getExtras();

switch(requestCode) {
case ACTIVITY_CREATE:
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mDbHelper.createNote(title, body);
fillData();
break;
case ACTIVITY_EDIT:
Long mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (mRowId != null) {
String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
mDbHelper.updateNote(mRowId, editTitle, editBody);
}
fillData();
break;
}

* We are handling both the ACTIVITY_CREATE and ACTIVITY_EDIT activity results in this method.
1、在这个方法中,我们处理 ACTIVITY_CREATE 和 ACTIVITY_EDIT两种活动返回结果。
* In the case of a create, we pull the title and body from the extras (retrieved from the returned Intent) and use them to create a new note.
2、在创建的代码中,我们从extras获取标题和内容,然后用来创建新的便签。
* In the case of an edit, we pull the mRowId as well, and use that to update the note in the database.
3、在编辑的代码中,我们还是获取mRowId,然后用来修改数据库的便签数据。
* fillData() at the end ensures everything is up to date .
4、fillData()保证所有信息都是最新的。

Step 7
第七步

The Art of Layout
布局的艺术
The provided note_edit.xml layout file is the most sophisticated one in the application we will be building, but that doesn't mean it is even close to the kind of sophistication you will be likely to want in real Android applications.
note_edit.xml布局文件是我们建立的应用程序中最成熟的一个文件,但是那不并意味着接近你在真实Android程序中可能要求的那种成熟。
Creating a good UI is part art and part science, and the rest is work. Mastering Android layout is an essential part of creating a good looking Android application.
创建好的UI部分(参见“Android layout”)是艺术部分是科学,剩余的就是工作。掌握Android布局是创建好看的Android程序的基本部分。
Take a look at the View Gallery for some example layouts and how to use them. The ApiDemos sample project is also a great resource from which to learn how to create different layouts.
仔细看看一些例子的布局,研究怎么使用他们,参见“View Gallery”。ApiDemos例子项目也是好资源,可以学习了解怎么创建不懂的布局。

Open the file note_edit.xml that has been provided and take a look at it. This is the UI code for the Note Editor.
打开已经提供的note_edit.xml文件,仔细看看。这是Note Editor的UI代码。
This is the most sophisticated UI we have dealt with yet. The file is given to you to avoid problems that may sneak in when typing the code. (The XML is very strict about case sensitivity and structure, mistakes in these are the usual cause of problems with layout.)
这是我们已经处理的最成熟的UI。提供这个文件为了避免问题,这些问题可能由于键入代码而悄悄潜入。(XML是非常严格的,在大小写和结构等方面的错误是布局问题的最常见原因。)
There is a new parameter used here that we haven't seen before: android:layout_weight (in this case set to use the value 1 in each case).
这里有一个以前没有见过的新参数:android:layout_weight(本例中在所有情况下设置为值1)
layout_weight is used in LinearLayouts to assign "importance" to Views within the layout. All Views have a default layout_weight of zero, meaning they take up only as much room on the screen as they need to be displayed. Assigning a value higher than zero will split up the rest of the available space in the parent View, according to the value of each View's layout_weight and its ratio to the overall layout_weight specified in the current layout for this and other View elements.
layout_weight用在LinearLayouts中,在布局中赋值"importance"给View。所有View默认layout_weight值为0,意味着他们仅仅占据他们需要的显示空间。赋值大于0将会在父View中分割可获得的剩余空间,分割空间的依据是每个View的layout_weight的值,还有在View元素的现有布局中指定的全部layout_weight的比率。
To give an example: let's say we have a text label and two text edit elements in a horizontal row. The label has no layout_weight specified, so it takes up the minimum space required to render. If the layout_weight of each of the two text edit elements is set to 1, the remaining width in the parent layout will be split equally between them (because we claim they are equally important). If the first one has a layout_weight of 1 and the second has a layout_weight of 2, then one third of the remaining space will be given to the first, and two thirds to the second (because we claim the second one is more important).
举一个例子:假设我们在一行中有一个文本标签和两个文本编辑框。标签没有指定layout_weight,因此他占据必须的最小空间。如果两个文本编辑框的layout_weight都设置为1,可在父窗口中的剩余宽度被他们平分(因为我们声称他们同等重要)。如果第一个是1,第二个是2,那么剩余空间的三分之一分配给第一个,三分之二分配给第二个(因为我们要求第二个更重要)
This layout also demonstrates how to nest multiple layouts inside each other to achieve a more complex and pleasant layout. In this example, a horizontal linear layout is nested inside the vertical one to allow the title label and text field to be alongside each other, horizontally.
这个布局文件也证明怎么互相嵌套多个布局元素,以便获得更多复杂和合适的布局界面。本例中,水平线布局嵌套进入垂直线,以便允许标题标签和文本字段相互并排。

Step 8
第八步

Create a NoteEdit class that extends android.app.Activity.
创建NoteEdit类,继承android.app.Activity
This is the first time we will have created an Activity without the Android Eclipse plugin doing it for us. When you do so, the onCreate() method is not automatically overridden for you. It is hard to imagine an Activity that doesn't override the onCreate() method, so this should be the first thing you do.
我们第一次没有在ADT帮助下创建Activity。onCreate()方法没有自动重载。想像没有重载onCreate()方法的Activity是困难的,因此,首先要做的事情是重载onCreate()
1. Right click on the com.android.demo.notepad2 package in the Package Explorer, and select New > Class from the popup menu.
1、右击com.android.demo.notepad2包,从弹出菜单中选择 New > Class
2. Fill in NoteEdit for the Name: field in the dialog.
2、在对话框的“Name:”标签后填入“NoteEdit”
3. In the Superclass: field, enter android.app.Activity (you can also just type Activity and hit Ctrl-Space on Windows and Linux or Cmd-Space on the Mac, to invoke code assist and find the right package and class).
3、在“Superclass: ”标签中,键入“android.app.Activity”(你也能输入Activity,按 Ctrl-Space,启用代码助手找到正确的包和类)
4. Click Finish.
4、点击Finish
5. In the resulting NoteEdit class, right click in the editor window and select Source > Override/Implement Methods...
5、在NoteEdit类中,右击编辑窗口,选择菜单Source > Override/ImplementMethods...
6. Scroll down through the checklist in the dialog until you see onCreate(Bundle) — and check the box next to it.
6、滚动对话框中的清单,找到onCreate(Bundle)——选中前面的检查框
7. Click OK.
7、点击OK
The method should now appear in your class.
这个方法应该出现在你的类中。

Step 9
第九步

Fill in the body of the onCreate() method for NoteEdit.
填写onCreate()的代码
This will set the title of our new Activity to say "Edit Note" (one of the strings defined in strings.xml). It will also set the content view to use our note_edit.xml layout file. We can then grab handles to the title and body text edit views, and the confirm button, so that our class can use them to set and get the note title and body, and attach an event to the confirm button for when it is pressed by the user.
这里将会设置新活动的题目为“Edit Note”(在strings.xml文件中定义的一个字符串)。也会设置内容视图以便使用note_edit.xml布局文件。然后,处理标题和内容文本编辑框、确认按钮,因此,我们的类能使用他们来获取或者设置便签的标题和内容,附着事件到确认按钮,这个事件在用户按确认按钮时触发。
We can then unbundle the values that were passed in to the Activity with the extras Bundle attached to the calling Intent. We'll use them to pre-populate the title and body text edit views so that the user can edit them. Then we will grab and store the mRowId so we can keep track of what note the user is editing.
然后,我们对传入到Activity的值进行分类,传入值使用Activity的extras Bundle,extras Bundle附着到调用的Intent上。我们会使用他们来预先填充标题和内容文本编辑框,因此,用户能编辑他们。然后,我们会获取和存储mRowId,因此我们能留意用户编辑的什么便签。
1. Inside onCreate(), set up the layout:
1、在onCreate()内部,设置布局:
setContentView(R.layout.note_edit);

2. Find the edit and button components we need:
2、找到我们需要的编辑框和按钮组件:
These are found by the IDs associated to them in the R class, and need to be cast to the right type of View (EditText for the two text views, and Button for the confirm button):
在R类中,通过ID找到他们,需要强制转换到正确的视图类型(两个文本视图是EditText,确认按钮时Button)
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);

Note that mTitleText and mBodyText are member fields (you need to declare them at the top of the class definition).
注意:mTitleText 和 mBodyText是成员变量(你需要在顶部声明他们)
3. At the top of the class, declare a Long mRowId private field to store the current mRowId being edited (if any).
3、在类的顶部,生命一个Long类型的mRowId私有变量,用来存储当前编辑的mRowId
4. Continuing inside onCreate(), add code to initialize the title, body and mRowId from the extras Bundle in the Intent (if it is present):
4、继续在onCreate()中增加代码来初始化标题、内容、mRowId,从Intent的extras Bundle(附加包)中获取初始化数据。
mRowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);

if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}
}

* We are pulling the title and body out of the extras Bundle that was set from the Intent invocation.
(1)从来自Intent引用的extras Bundle中获取标题和内容
* We also null-protect the text field setting (i.e., we don't want to set the text fields to null accidentally).
(2)零保护文本框设置(例如,我们不想意外地设置文本框为null)
5. Create an onClickListener() for the button:
5、针对按钮创建onClickListener()
Listeners can be one of the more confusing aspects of UI implementation, but what we are trying to achieve in this case is simple. We want an onClick() method to be called when the user presses the confirm button, and use that to do some work and return the values of the edited note to the Intent caller. We do this using something called an anonymous inner class. This is a bit confusing to look at unless you have seen them before, but all you really need to take away from this is that you can refer to this code in the future to see how to create a listener and attach it to a button. (Listeners are a common idiom in Java development, particularly for user interfaces.) Here's the empty listener:
Listener使UI实现更混乱的一个方面,但是,本例中试图完成的事情是简单的。当用户按确认按钮时,调用onClieck()方法,用来完成一些工作并且然会被编辑的便签的值给Intent调用者。我们利用称为匿名内嵌类的东西来做这些事情。如果你以前没有见到过,那么看起来这有点混乱,但是,你实际需要取走(完成)的所有事情是可以在未来引用这里的代码,注意怎样创建一个Listener,并附着到一个按钮(Listener是Java开发的一个习惯用语,尤其是在UI方面)。下面是空的Listener:
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
}
});

Step 10
第十步

Fill in the body of the onClick() method in our listener.
填充侦听器里面的onClick()代码。
This is the code that will be run when the user clicks on the confirm button. We want this to grab the title and body text from the edit text fields, and put them into the return Bundle so that they can be passed back to the Activity that invoked this NoteEdit Activity. If the operation is an edit rather than a create, we also want to put the mRowId into the Bundle so that the Notepadv2 class can save the changes back to the correct note.
这些代码在用户点击确认按钮时运行。我们使用这些代码从编辑框中获取标题和内容文本串,然后把它们送给Bundle,以便他们能传回到Activity(NoteEdit Activity调用)。如果操作是编辑而不是创建,那么传送mRowId进入Bundle,以便Notepav2类能保存变化到正确的便签。
1. Create a Bundle and put the title and body text into it using the constants defined in Notepadv2 as keys:
1、创建Bundle,传递标题和内容文本到Bundle,使用在Notepadv2中定义的常量作为关键码:
Bundle bundle = new Bundle();

bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}

2. Set the result information (the Bundle) in a new Intent and finish the Activity:
2、在新的Intent中设置结果信息(Bundle),完成Activity:
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
finish();

* The Intent is simply our data carrier that carries our Bundle (with the title, body and mRowId).
(1)Intent是简单的数据运载工具,运载Bundle(携带标题、内容、mRowId)
* The setResult() method is used to set the result code and return Intent to be passed back to the Intent caller. In this case everything worked, so we return RESULT_OK for the result code.
(2)setResult()方法用于设置结果代码,返回Intent为了传回到Intent调用者。本例中,每件事情都正常工作,因此我们返回RESULT_OK作为结果代码
* The finish() call is used to signal that the Activity is done (like a return call). Anything set in the Result will then be returned to the caller, along with execution control.
(3)finish()调用用于发信号表明Activity被激发(像一个回调)。然后,在Result中设置的所有东西会返回给调用者,连同执行控制一起

The full onCreate() method (plus supporting class fields) should now look like this:
完整的onCreate()如下:
private EditText mTitleText;
private EditText mBodyText;
private Long mRowId;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_edit);

mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);

Button confirmButton = (Button) findViewById(R.id.confirm);

mRowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);

if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}
}

confirmButton.setOnClickListener(new View.OnClickListener() {

public void onClick(View view) {
Bundle bundle = new Bundle();

bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}

Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
finish();
}
});
}

Step 11
第11步

The All-Important Android Manifest File
非常重要的Android Manifest文件
The AndroidManifest.xml file is the way in which Android sees your application. This file defines the category of the application, where it shows up (or even if it shows up) in the launcher or settings, what activities, services, and content providers it defines, what intents it can receive, and more.
AndroidManifest.xml文件是Android领会你的程序的方式。这个文件定义了程序的类型,在发射器和设置里面露面的地方,定义了活动、服务、内容提供器是什么,能接收的intent是什么,等等。
For more information, see the reference document AndroidManifest.xml
欲知更多信息参见文档“AndroidManifest.xml”

Finally, the new Activity has to be defined in the manifest file:
最后,新Activity必须定义在manifest文件中:
Before the new Activity can be seen by Android, it needs its own Activity entry in the AndroidManifest.xml file. This is to let the system know that it is there and can be called. We could also specify which IntentFilters the activity implements here, but we are going to skip this for now and just let Android know that the Activity is defined.
在新Activity能被Android了解之前,需要在AndroidManifest.xml中定义自己的Activity入口。这是让系统知道它在那儿且能被调用。我们也能指定Activity实现哪个IntentFilters,但是我们打算暂时跳过这个,只是让Android知道Activity被定义了。
There is a Manifest editor included in the Eclipse plugin that makes it much easier to edit the AndroidManifest file, and we will use this. If you prefer to edit the file directly or are not using the Eclipse plugin, see the box at the end for information on how to do this without using the new Manifest editor.
在Eclipse插件中包含了一个Manifest编辑器,使AndroidManifest文件编辑更加容易,我们将会使用这个编辑器。
1. Double click on the AndroidManifest.xml file in the package explorer to open it.
1、在AndroidManifest.xml文件上双击打开它
2. Click the Application tab at the bottom of the Manifest editor.
2、在Manifest编辑器底部点击Application便签
3. Click Add... in the Application Nodes section.
3、在Application Nodes段中点击 Add...
If you see a dialog with radiobuttons at the top, select the top radio button: "Create a new element at the top level, in Application".
如果你在顶部看到一个带有单选按钮的对话框,那么选择顶部的单选按钮"Create a new element at the top level, in Application"
4. Make sure "(A) Activity" is selected in the selection pane of the dialog, and click OK.
4、保证 "(A) Activity"在对话框的选择面板中被选中,然后点击OK
5. Click on the new "Activity" node, in the Application Nodes section, then type .NoteEdit into the Name* field to the right. Press Return/Enter.
5、在 Application Nodes段中,点击新的"Activity"节点,然后在“Name*”标签右边输入.NoteEdit,按Return/Enter

The Android Manifest editor helps you add more complex entries into the AndroidManifest.xml file, have a look around at some of the other options available (but be careful not to select them otherwise they will be added to your Manifest). This editor should help you understand and alter the AndroidManifest.xml file as you move on to more advanced Android applications.
Android Manifest编辑器帮助你增加更多复杂的条目到AndroidManifest.xml文件中,仔细看看可获得的其他选项(但是小心不要选择他们否则他们会被加入到你的Manifest文件中)。这个编辑器应该帮助你理解和修改AndroidManifest.xml文件,当你移到更多高级的Android程序时会很有用处。
If you prefer to edit this file directly, simply open the AndroidManifest.xml file and look at the source (use the AndroidManifest.xml tab in the eclipse editor to see the source code directly). Then edit the file as follows:
如果你宁愿直接编辑这个文件,简单打开 AndroidManifest.xml文件,然后了解资源(使用eclipse编辑器的AndroidManifest.xml标签直接领悟源代码)。然后,按照下面这样编辑文件:

This should be placed just below the line that reads:
这应该表示:
for the .Notepadv2 activity.
针对.Notepadv2活动

Step 12
第12步

Now Run it!
现在运行。
You should now be able to add real notes from the menu, as well as delete an existing one. Notice that in order to delete, you must first use the directional controls on the device to highlight the note. Furthermore, selecting a note title from the list should bring up the note editor to let you edit it. Press confirm when finished to save the changes back to the database.
现在,你应该能从菜单增加真实的便签,也能删除存在的便签。注意:为了删除,你必须首先使用设备方向控制来选中便签。此外,从列表中选择便签标题应该被提出到便签编辑器,以便你能编辑他。按确认按钮保证保存修改到数据库中。