Android中EditText如何定位光标位置

代码:edittext.setSelection(int);

范例:

说明:在写Techo Lite时遇到的问题。找了一下发现EditText控件提供了这样一个函数以设置光标所在位置。

进程和线程

在一个程序的仅有的一个应用程序组件启动时(除此之外没有其他组件在运行),Android系统会为该程序开启一个包含一个单独执行线程的新的Linux进程。默认来说,同一个程序的所有组件都运行于同一个进程的同一个线程中(被称为“主”线程(main thread))。如果一个组件在启动时该程序已经有一个进程存在的话(这是因为该程序存在有其他的组件),那么组件将运行于这个进程中且共用线程。不过,可以安排程序中的不同组件运行于不同的线程,以及为任何进程创建新的线程。

本文档将讨论Android应用程序中的进程和线程是如何工作的。

进程

默认地,同一个应用程序的所有组件都运行于同一个进程,大部分的程序不应该改变这一点。不过,如果认为有必要控制某个特定的组件所属的进程,可以在manifest文件中进行设定。

每一种类型的组件元素——<activity>、<service>、<receiver>和<provider>的manifest条目都支持android:process属性来指定组件应当运行于哪一个进程之中。可以设置该属性以使组件运行于其自有的进程或让一些组件共用进程的同时另一些使用独立进程。还可以通过设置android:process来事不同程序的组件运行于统一进程——假设这些程序共享同样的Linux用户ID并被相同的权限标记。

<application>元素也有android:process属性,用来给所有的组件设定默认值。

Android在某些时候(比如内存不足而用户又马上需要更多的内存来运行其他的进程)会做出关闭某个进程的决定。运行于该进程的应用程序组件也就相应地会被销毁。当再次需要它们时将会重启一个进程。

在决定终止哪一个进程时,Android会权衡它们对于用户的重要性。比如,系统更有可能关闭一个活动在屏幕上不可见的进程而不是一个活动仍然可见的进程。因此,是否终止一个进程取决于进程中组件的运行状态。具体决定终止哪一个进程的规则将在之后讨论。

进程生命周期

Android系统总是试图尽可能长时间地保持一个程序进程,不过旧的进程最终将被移除来为更为重要的进程提供内存。为了决定保留哪个进程,杀除哪个进程,系统将每一个进程置于一个基于进程内运行的组件及其状态的“重要性层级”之中。最低重要性的进程将被最先去除,之后是次低重要性的,以此类推直至能够提供足够的系统资源。

重要性层级中有五个等级。下面根据重要性顺序列出了不同类型的进程(第一种进程是最为重要的,将被最后杀除):

1. 前台进程

一个用户当前正在使用的进程。如果以下任一条件被满足,进程就被划归为该类型:

  • 它包含一个用户正在进行交互操作的活动(该活动的onResume()方法已被返回)。
  • 它包含一个与用户正在进行交互操作的的活动相绑定的服务。
  • 它包含一个“在前台运行的”服务——服务调用了startForeground()。
  • 它包含了一个正在执行其生命周期回馈方法之一(onCreate(),onStart()或onDestroy())的服务。
  • 它包含了一个正在执行其onReceive()方法的广播接收者。

通常,在某一时刻只会存在少数的前台进程。它们将被最后杀除——在内存极端不足的情况下。通常此时设备处于内存分页状态,需要杀除前台进程来保持用户界面响应。

2. 可见进程

这类进程没有任何前台组件,但是可以影响用户在屏幕上看到的内容。如果以下任一条件被满足,进程就被划归为可见:

  • 它包含一个不在前台但是对用户可见的活动(其onPause()方法已被调用)。比如,前台活动是一个对话框,而前一个活动仍可以被看到存在于其后方的情况。
  • 它包含一个与可见活动(或前台活动)相绑定的服务。

3. 服务进程

这是一类运行着由startService()方法启动的服务但不属于前两种进程的进程。尽管服务进程不与任何用户直接看到的内容绑定,它们确实在执行用户所希望的工作(比如背景播放mp3或是从网络下载数据),因此系统只有在亟需内存维持所有的前台和可见进程时才会杀除他们。

4. 后台进程

这是一类包含一个当前不对用户可见的活动(活动的onStop()方法已被调用)。这些进程对用户体验没有直接影响,当前台、可见或服务进程需要内存时它们会被随时杀除。通常有着大量的后台进程在运行,它们被保存在一个LRU(Least recently used)表中以确保最后看到的活动所在的进程会被最后杀除。如果一个活动正确地执行其生命周期方法并保存其状态,杀死它的进程不会影响用户体验。这是因为当用户返回活动时,所有的可见状态将被恢复。参见“活动(Activities)”文档以了解关于保存与恢复状态的更多信息。

5. 空进程

这是一类没有任何活动的(active)应用程序组件的进程。保持这样一个进程的唯一理由是作为缓存来提高下一次有组件要在其内运行时的启动速度。系统为了在进程缓存和底层核心缓存间平衡整体系统资源而常常杀除这些空进程。

Android根据进程中当前活动着的组件的重要性来尽可能高地给一个进程定级别。比如,如果一个进程包含一个服务和一个可见活动,那么进程会被分级为可见进程而不是服务进程。

另外,进程的层级可能会因为其他进程依赖于它而被提升。服务于另一个进程的进程不可能比那个进程的层级低。比如,进程A中的一个内容提供者服务于进程B中的一个客户端,又或是进程A中的一个服务与进程B中的一个组件绑定,那么进程A被认为至少要和进程B同样重要。

由于运行服务的进程比运行后台活动的进程层级高,一个执行长时间操作的活动应该开启一个服务来处理该工作而不是简单地创建一个工作线程——特别是该操作可能比活动本身持续更久时。这样的例子有个后台播放音乐或是上传一张拍摄的照片到网上。使用服务至少可以确保该操作有服务进程的优先级,而不必在意活动本身怎样。正如之前在广播接收者生命周期一节中提到的,这也是为什么广播接收者应当用服务而不是用线程来执行花费时间的工作的原因。

线程

当一个程序被启动后,系统将创建一个被称为“主线程(main)”线程来执行该程序。这个线程非常重要,它负责正确地向UI控件分发事件,包括绘图事件。它同时还与Android UI toolkit中的组件(即android.widget和android.view包中的组件)进行交互。因此,主线程有时也被称作为UI线程。

系统不会为每一个组件的实例创建新的线程。所有运行于同一进程的组件都在UI线程中被实例化,系统对它们的调用也都是从该线程中分发。因此,要响应系统回馈的方法(比如回馈用户操作的onKeyDown()或是生命周期回馈方法)总是要运行于进程的UI线程中。

例如,当用户点击屏幕上的一个按钮,程序的UI线程将分发触摸事件至控件,控件将设置其点击状态并向事件队列发送invalidate请求。UI线程会将此请求出列并通知控件进行重绘。

当程序在响应用户交互操作中需要处理密集型工作时,这样的单线程模式可能会在没有很好的设计程序的情况下降低程序表现。特别是如果所有的任务都由UI线程执行,网络连接或是数据请求等长时间操作将会阻塞整个UI。当线程被阻塞,事件就无法被分发,包括绘图事件。从用户的角度来看,程序似乎就停止响应了。更糟糕的情况下,如果UI线程被阻塞的时间过长(目前来说大约是5秒)用户就会看到糟糕的“程序没有相应”(application not responding,ANR)对话框。用户之后就会考虑退出程序,甚至不满地卸载它。

另外,Android UI toolkit不是线程安全的。因此,不能在工作线程中进行UI处理——必须将所有用户交互操作在UI线程中实行。于是,对于Android的单线程模式有两条规则:

  1. 不要阻塞UI线程
  2. 不要在UI线程外读取Android UI toolkit

工作线程

由上面所描述的单线程模式可知,不阻塞UI线程对于程序UI的响应性是很重要的。如果不得不执行无法立即完成的工作,就必须确保它们处于不同的线程之中(“后台”或“工作”线程)。

例如,下面是一个点击监听器(click listener)的代码,它将从另一个线程中下载一张图片并将其显示在一个ImageView中:

最初这看起来能很好的运行,因为它创建了一个新的线程来处理网络操作。不过,它违反了单线程模式的第二条规则:不要在UI线程外读取Android UI toolkit——这个范例在工作线程中修改了ImageView而不是在UI线程中。这可能导致未定义的不被希望的行为,并且可能要花费很长时间来解决这个问题。

要解决这个问题,Android提供了几种不同的方法来从其他线程中接入UI线程。这里列出了有用的一些方法:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

例如,可以用View.post(Runnable)方法来解决上面的问题:

现在代码是线程安全的了:网络操作在另一个线程中完成,同时ImageView是由UI线程处理的。

不过,当操作的复杂性提升时,这样的代码会变得很复杂而难以维护。要处理更为复杂的与工作线程的交互,应该考虑在工作线程中使用Handler,来处理接收自UI线程的消息。不过,最好的解决方案或许是继承一个AsyncTask类,来简化工作线程与UI间交互任务的执行。

使用AsyncTask

AsyncTask允许在用户界面中执行异步工作。它在一个工作线程中执行阻塞性操作并把结果发布于UI线程而不需要处理线程关系。

要使用它,必须继承AsyncTask并使用doInBackground()回馈方法(它运行于后台线程池中)。要更新UI,需要使用onPostExecute()以传递来自doInBackground()的结果来安全地更新UI。之后可以通过在调用execute()来运行UI线程中的任务。

例如,可以像这样使用AsyncTask来实现上一个范例:

现在不但UI是安全的,代码也变得更简单了,因为工作被分成了需要在工作线程中完成的部分和应当在UI线程中完成的部分。

应该阅读AsyncTask的参考文档来了解关于如何使用这个类的完整信息,不过这里有关于它工作方式的简介:

  • 可以使用generic来指定参数类型,进度值(progress value)和任务的最终值
  • doInBackground()方法自动在工作线程中执行
  • onPreExecute()、onPostExecute()和onProgressUpdate()都在UI线程中
  • onPreExecute()所返回的值被发送至onPostExecute()
  • 可以在onInBackground()执行的任意时刻调用publishProgress()以在UI线程中执行onProgressUpdate()
  • 可以在任一时刻,在任意线程中,取消任务

注意:在使用工作线程时可能遇到的另一个问题是由于运行配置的改变而活动自动重启(比如用户改变了屏幕方向),这可能会销毁该工作线程。要防止任务在这种情况下重启,并在活动被销毁时正确地取消任务,请参见Shelves范例程序的源代码。

线程安全方法(Thread-safe methods)

在某些情况下,所使用的方法可能会被不止一个线程调用,因此这些方法必须是线程安全的。

对于可以被远程调用的方法——比如在绑定的服务中的那些方法——来说,这是非常必要的。当在一个IBinder所运行的进程中调用一个在该IBinder中使用的方法时,这个方法会在调用者的线程中被执行。然而,如果这个调用是在另一个进程中的,那么这个方法将在从线程池中选择的一个系统保留的与IBinder处于同一进程的线程中执行(并不是在进程的UI线程中被执行)。例如,尽管一个服务的onBind()方法会被在服务的进程的UI线程中调用,对象中onBind()所返回的方法(比如,使用了RPC方法的一个子类)会在线程池中的线程中被调用。因为一个服务有不止一个客户端,所以多个池线程可以同时和同一个IBinder方法相关联。因此IBinder()方法必须是线程安全的。

线程间通信(Interprocess Communication,IPC)

Android提供了一种使用远程过程调用(remote procedure call,RPC)的进程间通信(IPC)机制,使得一个被某一活动或其他应用程序组件调用的方法将被(于另一进程中)远程执行,而所有的结果将被返回给调用者。这将会分解一个方法的调用及其数据至操作系统可以理解的级别,将其从本地进程及地址空间传输至远程进程及地址空间,然后在远程进程中重新组装激活这个调用。之后返回值将被反向传输。Android提供了所有执行这样的IPC事务所需的代码,因此只需关注如何定义和使用RPC编程接口即可。

要进行IPC,程序必须通过bindService()和一个服务相绑定。更多信息请参阅“服务开发者指南”。

本作品采用知识共享 署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

Android用记事本程序 Techo Lite

虽然标题中用的是“记事本”,不过更确切的说,应该是一个简单的便筏程序。

Android平台上各类记事本程序已经数不胜数了,自己写这样一个程序的动机纯粹是满足自己的需要。和其他同类程序相比,Techo Lite的特点在于:只能保存一条记录,因而进入程序后直接可以编辑。也就是说,比其他类似程序要少一个选择要编辑的文件的步骤。

这一点对我来说很重要。我平时常常会需要用手机记录一些简单的文本,因此希望程序可以尽可能快地进入编辑模式。此外我一般只是用作短时记忆用,所以也并不需要能保存多条记录。万一遇到要记录几条不同的内容,将它们都记录在一条中也不会有太大的问题。之前也用过各种各样的程序。Color Note的问题在于进入程序后仍然要点击两次才能进入编辑模式,而且两次点击还需要移动手指位置,不是特别人性化。而另一款程序AK Notepad在进入程序后只需要再点击一次,选择要编辑的文件后即可开始编辑(如果使用桌面插件也可以实现一次点击即进入编辑)。可惜最新升级的版本不知怎么在自己的手机上经常会崩溃,于是只好暂时放弃使用。

由于以上原因,自己决定动手写一个简单的便筏程序供自己使用。程序本身相当简单。进入程序后即可编辑文本内容,退出程序后内容就会被自动保存。另外还有清空已输入内容和更换背景颜色这两个功能,除此之外就没有其他多余的功能了。事实上,设计程序的图标和考虑中日英的程序简介文本所花的时间比程序本身所花的时间更长。程序名称中的Lite有两个意义。其一是指明这个程序的功能将会相当简单,其二是因为我另外还有编写一个全功能记事本的计划,会命名为Techo。它将无缝集成文本和涂鸦两种输入模式并提供文件输出等一系列功能。不过这个计划的优先级不是很高……

总结来说,相比现有的记事本程序,Techo Lite具有以下特点:

  • 操作快捷。由简洁的设计理念,使得Techo Lite不但程序结构简单,没有冗余的操作步骤,而且事实上的反应速度也相当迅速。
  • 功能精简。完全没有同步等文本编辑之外的多余功能。没有繁琐的设置选项。这在一定程度上也保证了Techo Lite基本不存在Bug,且与任何终端都保持了高度的兼容性。
  • 可更换背景色。并不是所有同类程序都可以更换背景色。Techo Lite虽然简单,但是仍提供了多种背景色可供选择,以满足不同的人的喜好。

目前Techo Lite可以在Android Market的效率类应用程序中找到,搜索Techo或是pub:”BREEZE”即可。如果正在寻找一款将“减法法则”发挥到极致的记事本程序,不妨试试看Techo Lite吧。

Android Market页面:

https://market.android.com/details?id=org.breezesoft.techolite

最后是几张Techo Lite的截图:

索尼终于发表平板电脑

在今天召开的Sony IT Mobile Meeting上,索尼正式发表了两款平板电脑。

图像版权属于Sony Corporation

目前已知情报的一个整理:

左侧的平板型代号为S1。搭载了9.4寸屏幕,分辨率1280×800。采用独特设计以适合单手把持。

右侧的折叠型代号为S2。配有两块5.5寸屏幕,分辨率1024×480×2。上下屏幕可以分别执行不同任务。

两者都搭载了PlayStation Suite,运行于Android 3.0系统。

软硬件都经过了优化,而具有高速流畅的响应速度。同时网络娱乐内容丰富,支持跨设备连接等特性。

更为详细的内容还有待公布。

关于Sony IT Mobile Meeting的更多信息请参见(日文):

http://www.sony.co.jp/united/simm/?fm=prx000000

创建状态栏通知

状态栏通知会向系统状态栏添加一个图标(还可以附有文本消息)以及一条“通知”窗口内的可扩展消息。当用户选择了该可扩展消息,Android发出一个有通知定义的Intent(通常来启动一个活动)。也可以设置通知来以声音、振动和设备闪光来提醒用户。

状态栏通知应当被用于任何后台服务需要提示用户发生了事件并要求回应的情况。后台服务不应自己启动活动来接收用户交互操作。它应当创建一个状态栏通知,在用户选择该通知时启动一个活动。

下面的截图展示了一个左侧有通知图标的状态栏。

下一张截图展示了“通知”窗口中扩展的通知消息。将状态栏下拉可展开通知窗口(或从主页面选项菜单中选择“通知”)。

基本内容

活动或是服务可以初始化一个状态栏通知。由于一个活动只有在其状态是活动的且处于焦点时才能执行行动,所以应当通过服务来创建状态栏通知。这样,通知就能从后台创建,即使用户在使用另一个程序或是设备处于休眠状态。要创建通知,必须使用两个类:Notification和NotificationManager。

使用Notification类的一个实例来定义状态栏通知的属性,例如状态栏图标,扩展消息,或要播放的音效等更多内容。NotificationManager是一个Android系统服务,它执行并管理所有Notification。不必实例化NotificationManager。为了传递Notification,必须通过getSystemService()获取NotificationManager的一个引用,之后再希望通知用户时,使用notify()将其传递给自己的Notification对象。

要创建一个状态栏通知:

1.获取NotificationManager的一个引用

2.实例化Notification:

3.定义Notification的扩展消息和Intent:

4.将Notification传递给NotificationManager:

这样之后,用户将收到通知。

管理通知

一个Notification对象定义显示于状态栏和通知窗口的通知消息的具体细节以及其他提示设置,例如声音或是灯光闪烁。必须通过getSystemService()方法检索一个指向它的引用。例如:

在希望发送状态栏通知时,需要用notify(int, Notification)来传递Notification对象至NotificationManager。第一个参数是Notification的唯一ID,第二个是Notification对象。ID唯一识别了程序中的Notification。如果需要更新Notification或(程序管理多个不同Notification时)在用户由Notification中定义的Intent返回程序时选择正确的行动,这将会是很必要的。

要在用户从通知窗口中选择状态栏通知的同时清除它的话,就向Notification对象添加”FLAG_AUTO_CANCEL”旗标。也可以手动地通过向cancel(int)传递通知ID来清除,或使用cancelAll()来清除全部通知。

创建通知

一个Notification对象定义了显示在状态栏和通知窗口的通知消息的细节,以及其他提示的设置,例如声音和灯光闪烁。

一个状态栏通知需要以下所有内容:

  • 一个状态栏的图标
  • 一个标题和扩展视图中的扩展消息(除非另外自定义了一个的扩展视图)
  • 一个PendingIntent,以供在通知被选中时放出

状态栏通知的可选设置则包括:

  • 状态栏的ticker-text消息(注:暂时还没有决定怎么翻译这个ticker-text比较好)
  • 提示音
  • 振动设置
  • LED灯设置

新建一个Notification的基本内容包括构造函数Notification(int, CharSequence, long)和setLatestEventInfo(Context, CharSequence, CharSequence, PendingIntent)方法。它们定义了一个Notification所需的所有设置。下面的片段展示了一种基本的通知创建方式:

更新通知

在程序中的事件持续发展的过程中可以不断更新状态栏通知中的信息。例如,在前一条短信被阅读前又收到新的一条时,短消息程序会更新现有通知,显示收到的未读短信的总数。这一更新通知的做法要比向NotificationManager添加新的通知更好,因为它避免了使通知窗口变得杂乱。

由于每一个通知都有NotificationManager通过一个整型ID唯一定义,因此可以通过以新值调用setLatestEventInfo()来修改通知,改变Notification的一些域的值,之后再次调用notify()。

可以通过对象成员域来修改每一种属性(除了Context和扩展消息标题及文本)。应当始终在通过新值调用setLatestEventInfo()更新通知时修改contentTitlecontentText的文本消息。之后调用notify()来更新通知。(当然,如果创建了自定义扩展视图,那更新这些标题和文本的值就没有效果了。)

添加声音

可以使用(用户所定义的)默认消息提示音或是程序指定的声音来提示用户。

要使用用户默认提示音,在defaults域中添加“DEFAULT_SOUND”:

要在程序中使用不同的提示音,需要传入一个Uri引用至sound域。下面的例子使用了一个保存于设备SD卡中的已知音频文件:

接下来的例子中,音频文件是从内部的MediaStore的ContentProvider中选择的:

在本例中,媒体文件的确切ID(”6″)是已知的,附加于内容Uri之上。如果不知道确切的ID,就必须使用ContentResolver来请求所有MediaStore内可用的媒体。参见内容提供者文档中关于使用ContentResolver的更多信息。

如果希望声音在用户响应通知或通知被取消前不断持续,就在flags域中添加“FLAG_INSISTENT”。

注意:如果defaults域包含了“DEFAULT_SOUND”,那么默认声音将会覆盖其他所有定义过的sound域。

添加振动

可以使用默认振动模式或是程序自定义的振动模式来提醒用户。

要使用默认模式,在defaults域中添加“DEFAULT_VIBRATE”:

要定义属于自己的振动模式,需向vibrate域传递一组long型数组值:

long型数组定义了震动有无的时长间隔(以毫秒计)。第一个值是在开始之前等待的时间(无震动),第二个值是首次震动的长度,第三个值是之后的暂停时长,依此类推。振动模式可以为任意长度,不过不能设置循环。

注意:如果defaults域包含了“DEFAULT_VIBRATE”,那么默认振动将覆盖所有在vibrate域中定义的震动。

添加灯光闪烁

要使用LED灯光闪烁来提醒用户,可以用默认灯光模式(如果可用),或自定义灯光模式及色彩。

要使用默认灯光设置,在defaults域中添加“DEFAULT_LIGHTS”:

要自定义模式和色彩,需要为ledARGB域(色彩),ledOffMS域(LED关闭时长,单位毫秒)和ledOnMS域(LED开启时长,单位毫秒)设定值,并在flags域添加“FLAG_SHOW_LIGHTS”:

在这个例子中,绿灯将反复闪烁,亮300毫秒,暗1秒。设备的LED并不支持光谱中所有的色彩,每一种设备支持的色彩也有不同,硬件会尽可能提供最佳效果。绿色是最常用的通知色彩。

更多特性

可以通过Notification域和旗标向通知添加更多特性。包括以下有用的特性:

“FLAG_AUTO_CANCEL”

旗标将其加入flags域以在通知被从通知窗口中选择时自动取消

“FLAG_INSISTENT”

旗标将其加入flags域以重复音频播放直至用户回应

“FLAG_ONGOING_EVENT”

旗标将其加入flags域以将通知分组于通知窗口的“正在进行”标题之下。这表示程序正在运行——它的进程仍在后台运行,尽管程序本身不可见(例如音乐和通话)

“FLAG_NO_CLEAR”

旗标将其加入flags域以表明通知不应当被“清除通知”按钮清除。这在程序正在运行时很有用。

number

这个值显示了当前被通知所表示的事件数。正确的数字会在状态栏图标顶部显示。如果要用这个域,那就必须在通知被创建时从“1”开始计数。(如果在一次更新时将这个值由零改为了比零大的任何数,这个数字就不会被显示。)

iconLevel

这个值显示了当前用于通知图标的LevelListDrawable等级。配合在LevelListDrawable中定义的drawable,可以通过改变这个值使状态栏中的图标呈现动画效果。参见LevelListDrawable以获取更多信息。

参见Notification类参考资料以获取关于添加特性来自定义程序的更多信息。

创建自定义扩展视图

默认情况下用于通知窗口的扩展视图包括基本的标题和文本消息。这些是由setLatestEventInfo()方法的contentTitlecontentText参数所定义。然而,也可以使用RemotiViews来自定义扩展视图的布局。下面的屏幕截图展示了一个以线性布局使用ImageView和TextView的自定义扩展视图。

要定义扩展消息自有的布局,需要实例化RemoteViews对象并将其传递给Notification的contentView域。还要将PendingIntent传递给contentIntent域。

可以通过一个范例来更好的理解如何创建一个自定义扩展视图:

1.创建扩展视图的XML布局。例如,创建一个名为custom_notification_leyout.xml的布局文件并像这样编写:

该布局将被用于扩展视图,不过ImageView和TextView的内容仍需要由程序来定义。RemoteViews提供了一些用以定义这些内容的便利方法…

2.在程序代码中,使用了RemoveViews方法来定义了图片和文本。之后将RemoteViews对象传递至Notification的contentView域,就像这样:

如上所示,将程序的package名和布局资源ID传递给RemoteViews的构造函数。之后,通过setImageViewResourse()和setTextViewText()来定义ImageView和TextView的内容。两者都需要传递希望使用的View对象的正确的引用ID以及其值。最后RemoteViews对象被传递到了Notification的contentView域。

3.因为在使用自定义视图时不需要使用setLatestEventInfo()方法,所以必须在contentIntent域中定义Notification所使用的Intent,如下所示:

4.现在通知就可以像往常一样被发送了:

RemoteViews类还包括可以用来在通知的扩展视图中轻松添加Chronometer(计时器)或是ProgressBar(进度条)的方法。关于通过RemoteViews来创建自定义布局更多信息,请参考RemoteViews类的参考资料。

注意:在创建自定义扩展视图时,必须非常小心以确保自定义布局在不同设备方向(orientation,即横屏或是竖屏)和分辨率下都能正常工作。尽管这个建议对于Android上创建的所有View布局来说都是值得注意的,但在这一部分里尤为重要,因为布局所受的限制非常严格。所以不要将自定义布局设计的过于复杂,此外应当在多种不同情况下作测试。

返回通知用户

本作品采用知识共享 署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

通知用户

在不少情况下会需要通知用户程序当前发生的事件。一些事件需要用户回应,另一些则不需要。例如:

  • 当比如保存文件之类的事件完成时,应该出现一条信息确认保存成功。
  • 如果程序运行于后台,需要获取用户的注意时,程序应该创建一条通知让用户根据情况作出回应。
  • 如果程序正在执行需要用户等待的任务(比如读取文件),程序应该显示进度圈或是进度条。

以上每一种通知任务都是通过不同技术实现的:

  • Toast通知,用于来自后台的简单信息。
  • 状态条通知,用于来自后台需要用户回应的长期消息。
  • 对话框通知,用于和活动相关的通知。

本文综述了这些用以通知用户的技术,并包含了指向完整文档的链接。

Toast通知

toast通知是出现在窗口表层的弹出式信息。它只占用信息所需的空间,用户当前的活动仍然是可见且能够操作的。该通知自动地淡入淡出,不接受交互事件。toast可以由后台服务创建,因此即使程序本身不可见,也能够被显示。

toast最适合于确定用户正在关注屏幕时显示简短信息,例如“文件已保存”。toast不能接受用户交互事件;如果希望用户进行回应并作出操作的话,就要改用状态条通知。

更多信息,请参考“创建toast通知”。

状态条通知(Status Bar Notification)

状态条通知会向系统状态条添加一个图标(还可以附有文本消息)以及一条“通知”窗口内的可扩展消息。当用户选择了该可扩展消息,Android发出一个有通知定义的Intent(通常来启动一个活动)。也可以设置通知来以声音、振动和设备闪光来提醒用户。

这类通知适用于程序在后台服务中工作、需要通知用户某一事件时。如果需要在活动仍然处于焦点时提示用户某一事件的发生的话,考虑该用对话框通知比较好。

更多信息,参阅“创建状态条通知”。

对话框通知(Dialog Notification)

对话框通常是一个出现于当前活动前方的小窗口。下层的活动失去了焦点使得对话框将接受所有的用户交互操作。对话框常用作通知以及和程序执行过程直接相关的活动。

应当在需要显示一个进度条或有需要用户确认的简短消息(比如一个有“Ok”和“取消”按钮的提示)的情况下使用对话框。也可以将对话框作为程序UI的一个组成部分等其他非通知用途。要获取关于所有可用的对话框类型的完整讨论以及其使用方法,参阅“创建对话框”。

返回用户界面

本作品采用知识共享 署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

常见布局对象

这一节将描述一些在程序中较为常用的布局对象类型。和其他布局一样,它们是ViewGroup的子类。另请参阅“Hello Views”教程以获取使用Android View布局的更多指导。

FrameLayout

FrameLayout是最为简单的布局对象类型。简单说它是屏幕上的一块空白空间,之后可以在其中填充单个对象——例如,一张需要读取的图片。所有FrameLayout的子元素位于屏幕的左上角;不能为子视图指定另外的位置。后继的子视图将简单地覆盖之前的视图,部分或是完全遮住它们(除非新的那个对象是透明的)。

LinearLayout

LinearLayout根据在orientation属性中所定义的,沿着单一方向——水平或是垂直——排列子视图。所有的子视图一个接一个堆砌,因此一个垂直列表中无论子视图有多宽,每一行都只有一个子视图。而一个水平列表高度只有一行(最高的子视图的高度加上间隙)。LinearLayout能显示每一个子视图之间的边距和重力(右侧、中心或左侧)。

LinearLayout还支持为其单独的子元素设定权重(weight)。该属性给视图设定了“重要性”。允许其扩展填充父视图中的任意剩余空间。子视图可以指定一个整型权重值,之后视图组中的剩余空间会按子视图所声明的权重比来分配。默认权重是零。例如,如果有三个文本框,其中两个的权重被声明为1,另一个没有权重(0),那么这个没有权重的文本框不会扩展,只会占它内容所需的空间。另两个会按相同比例扩展,充满三个文本框被计算后所剩余的空间。如果第三个文本框权重为2(而不是0),那它就被声明为比其他两个”更为重要“,而获得总剩余空间的一半,另两个则是平分另一半。

下面的两种形式代表了含有一组元素的LinearLayout:一个按钮、一些标签和一些文本框。文本框的宽度设置为fill_parent;其他元素设置为wrap_content。重力(gravity)默认是向左(left)。两个版本哦差异在于左边的没有设置权重(默认为零),右边的一个文本框的权重被设为了1。如果Name文本框也被设为了1的话,Name和Comments文本框则会一样高。

 

linearlayout
linearlayout

Tip:为了在屏幕上创建比例合适的布局,可以创建一个容器视图组,layout_widthlayout_height属性设为fill_parent;设置子视图的高和宽为0;根据所希望的屏幕比例设置每一个子视图的相对权重(weight)。

在一个水平LinearLayout中,项目根据其文本基准线的位置排列(最上部或最左侧的第一个元素的第一条线被认为是参考线)。这样在浏览元素时就不必上下移动视线以阅读相邻元素的元素文本了。可以通过在布局XML中设置android:baselineAligned=”false”来关闭这一功能。

要查看其他的范例代码,请参阅“Hello LinearLayout”教程。

TableLayout

TableLayout将其子视图按行、列排列。TableLayout容器不显示其行、列或单元格的边线。表格的列数和拥有单元格最多的那行相同。一张表格可以有空单元格,不过单元格无法跨越列,就像HTML中一样。

TableRow对象是TableLayout的子视图(每一个TableRow定义了表中单独的一行)。每一行有零个或更多的单元格,其每一个可以被其他类型的视图所定义。因此,一行单元格可能是由多种View对象,比如ImageView或TextView,构成的。一个单元格也可以是一个ViewGroup对象(例如,可以把另一个TableLayout嵌套于一个单元格内)。

下面的范例布局有两行,每行有两个单元格。相应的截图显示了其结果,单元格边界以点线显示(作为视觉效果而添加)。

可以将竖栏隐藏、拉伸填充屏幕或是标记为可缩小的以强制缩小表格适应屏幕。参见TableLayout参考文档以了解更多细节。

参见“Hello TableLayout”教程以查看范例代码。

RelativeLayout

RelativeLayout允许子视图指定它们相对于父视图的位置或它们之间的相互位置(通过ID来指定)。因此可以将两个元素靠着右侧边缘排列,或是于屏幕中心、或偏左侧上下排列,或是其他排列方式。元素将按指定的方式排列,因此如果第一个元素位于屏幕中央,其他和该元素相关的元素也会相对于屏幕中央进行排列。同时,由于有这样的顺序,如果是通过XML指定这种视图的话,要引用的元素(为了能定位其他的视图对象)必须在在其他视图引用其ID之前就被列于XML文件之中。

下面的例子展示了一个XML文件及其屏幕界面的显示结果。需要注意,引用了相对元素的那些属性(比如,layout_toLeft)是通过相对资源(@id/id)的语法来引用其ID的。

这些属性中的一部分直接被元素所支持,另一些则被其LayoutParams成员(RelativeLayout对于屏幕上所有元素的子类,这是因为所有元素都是RelativeLayout父对象的子对象)所支持。所定义的RelativeLayout参数有:widthheightbelowalignToptoLeftpadding[Bottom|Left|Right|Top]margin[Bottom|Left|Right|Top]。注意,其中一些参数是用于指定相对布局位置——它们的值必须是希望作为相对视图基准的元素的ID。例如,设置参数toLeft=”my_botton”至一个TextView将把该TextView放在ID为my_button的元素(比如在XML中先于TextView出现)的左侧。

要查看该范例代码,请参见“Hello RelativeLayout”教程。

重要视图组的总结

这些对象都有子UI元素。一些提供了可见的界面自有形式,另一些则是不可见的结构,只能用于管理其子视图。

描述
FrameLayout 用于显示单个对象的视图框架布局。
Gallery 一个可水平滚动的图像列表。
GridView 显示m列n行可滚动的表格。
LinearLayout 将子元素水平或是垂直排列为一行/列的布局。如果视窗长度超过屏幕长度,将会自动创建滚动条。
ListView 显示一列滚动列表。
RelativeLayout 允许指定子对象相互之间的位置(如:子对象A在子对象B的左侧) 或与父对象的相对位置(如:在父对象的上部)。
ScrollView 一列垂直的可滚动元素。
Spinner 在一个单行文本框中显示所绑定列表的其中一行。就像一个列表框,可以水平或是垂直下拉。
SurfaceView 提供所指定绘图表层的直接读取。 它可以持有位于屏幕表面的子对象,不过其实是为在屏幕上进行像素操作而非使用控件而设计的。
TabHost 提供了一个标签页选择器,通过监视点击行为以在标签被选择时切换屏幕内容。
TableLayout 一个行列数固定的表格布局。每一个单元格可以持有一个所选择的控件。每一行会自适应最大的列数。单元格的边界是不可见的。
ViewFlipper 在一个单行文本框中一次显示一个项目的列表。可以设置如同幻灯片一样在一定时间间隔后切换项目。
ViewSwitcher 和ViewFlipper相同。

返回用户界面

本页部分内容根据Android Open Source Project创作并共享的内容修改,并在知识共享 署名2.5许可协议中所述条款的限制下使用。

Android模拟器

Android SDK包含了一个移动设备模拟器——一个运行于你的电脑上的虚拟设备。模拟器可以让你进行原型和开发工作,同时可以在没有实体设备的情况下测试Android应用程序。

本作品采用知识共享 署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

开发者指南

欢迎来到Android开发指南!本指南提供了开发Android程序的实用指导和有关平台主要特性的文档。指南挖掘了Android背后的理念,解析了构造应用程序的框架,并介绍了在平台上开发、测试和发布软件的工具。

开发指南包含了Android平台的大部分文档,但不包括框架API的参考资料。对于API的特性,请见参考。

本指南分为若干部分:

Android基础

对Android的最初介绍——它是什么,它提供了哪些内容,你的程序如何适于Android。

框架主题

讨论Android框架和API的具体部分。通过应用程序基础一文获取对框架的简单介绍。之后按需探索其他的主题——从设计用户界面并设置资源到存储数据及使用权限。

Android Market主题

与在Android Market中发布并销售应用程序的主题有关的文档,例如如何加强授权政策以及应用程序内付款。

开发

使用Android开发调试及测试工具的说明。

发布

关于如何准备部署你的应用程序以及如何在准备好之后发布它的一些说明。

基本练习

关于编写高效率高兼容性的应用的推荐技巧。

网络应用程序

关于如何创建无缝运行于Android设备上的网络应用程序以及创建嵌有基于网络的内容的Android应用程序的文档。

附录

参考信息和特性,以及常见问答、术语表和其他信息。

在下载了SDK之后,从阅读开发指南起步吧。如果想要通过查看代码来开始学习,Hello Wordl教程通过一个标准的“Hello World”程序向你介绍了Android应用程序的一些基础知识。应用程序基础文档是学习应用程序框架的一个不错的入门材料。

要获取更多帮助,可以考虑加入一个或多个Android讨论小组。前往开发者论坛页面以了解更多。

本作品采用知识共享 署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。