索尼爱立信(Sony Ericsson)Xperia X10 Android 2.3.3系统Root指南

本文是基于XDA开发者论坛上aZuZu用户所写的X10官方2.3.3系统Root方法的文章翻译整理的。在此过程中亦对IT168索尼爱立信论坛中673225019用户的相关文章有所参考。在此表示感谢。

一、为什么要Root

所谓root简单来说就是指取得系统的底层权限以进行一些原本无法执行的操作。比如修改系统等。对于普通用户来说,root之后可以删除系统预装的一些无用软件,可以修改设备的地区信息以在市场中下载有地区限制的软件等,也有很大的价值。

二、Root之前的准备

  • 一部安装有索尼爱立信官方2.3.3系统的X10(通过官方途径升级或是强刷皆可)
  • 下载固件刷写工具FlashTool,解压于任意文件夹内
  • 下载root所需的基带包(其一其二),将ftf文件置于flashtool所在文件夹下的firmwares文件夹内
  • 确保X10有充足的电力完成root
三、Root的具体步骤
  1. 在手机的 设置-应用程序 中激活 “安装任意来源的apk包” 选项,在 设置-应用程序-开发 中激活“USB调试”选项。
  2. 关闭手机
  3. 打开flashtool,点击左上角的“刷写”按钮,在左侧列表中选择“X10_aZuZu_Kernel_Downgrade_Generic.ftf”,在右侧列表中复数选择loader.sin和kernel.sin两个文件。
  4. 点击Ok,弹出新窗口。按照其提示拔出USB线(如果原本手机和电脑相连接的话)、按住手机返回键的同时再次连接USB线,之后Flashtool会自动开始刷写。
  5. 等Flashtool完成刷写后拔出USB线(注意要确实等待刷写完成,整个过程约30秒,完成后会有包含finished内容的文字提示)。不要关闭Flashtool。
  6. 打开手机,并迅速连接USB线。可能会自动安装USB驱动等内容。这时不必操作,等待一切准备就绪后Flashtool上的第二个按钮“root”将会变得可以被按下,此时选择“root”按钮。
  7. 当root完成后(同样flashtool会有包含finished的文字提示),拔出USB线,取出电池,等待至少三十秒。
  8. 点击Flashtool左上角的“刷写”按钮,在左侧列表中选择“X10_aZuZu_Kernel_UpGrade_Generic.ftf”,在右侧列表中复数选择loader.sin和kernel.sin两个文件。
  9. 点击Ok,弹出新窗口。按照其提示拔出USB线(如果原本手机和电脑相连接的话)、关机、按住手机返回键的同时再次连接USB线,之后Flashtool会自动开始刷写。
  10. 等Flashtool完成刷写后拔出USB线(再次注意须确实完成刷写),打开手机。Root成功。

四、Root后可以做的事

root后程序列表中会新增一个superuser程序,它的作用是管理root权限。之后可以从Android Market中下载安装Market Enabler以修改手机地区信息,或是下载钛备份等程序以删除不需要的预装程序,或是更多。

 

Android用日常事项管理软件 Routine 版本升级 1.1.4

这几天把自己写的Android程序Routine作了一下升级,在这里简单地做一下说明,也算是作为存档。

Routine是一款Android平台的GTD软件,写这个程序的初衷是为了弥补没有很适合自己用的Xperia X10的GTD工具的遗憾。程序的特色就是使用了索尼爱立信的UXP主题,能够和X10家族的机型保持界面风格上的统一。

这次的1.1.4的升级是继1.0.7支持多任务列表之后的一次较大的升级,关于1.0.0初版的信息,请参见当时的发布日志

在保持了Routine原有特色的同时,这次的升级主要内容有以下几点:

  1. 改变了数据的存储方式,由文件存储改为了SQLite的数据库方式。提高了数据操作的性能。
  2. 大幅改进了界面,在美化的同时提高了可用性。
  3. 为条目提供了更多值域的支持,为将来支持新特性做准备。
  4. 支持标签色彩,视觉效果更为一目了然。
今后计划增加的特性暂定有条目的多标签支持、截止日期以及提醒的支持、多种排序方式的支持等。近期来说,在X10的Android 2.3.3系统升级发布之后会有一次UI风格的升级。

目前Routine可以在Android Market的效率类应用程序中找到,搜索Routine、ルーチン或者pub:BREEZE即可。如果需要一款简单好用的GTD工具,不妨试试看Routine吧。

Android Market页面:

http://market.android.com/details?id=org.breezesoft.routine

最后是一些新版Routine的程序截图:

意图和意图过滤器

一个程序的三个核心组件——活动、服务和广播接收者——通过被称为意图(intent)的消息来被激活。意图消息是一种在同一个或不同程序的组件间的运行后绑定机制。意图自身,即一个Intent对象,是包含了对于要执行的操作的抽象描述的被动数据结构——或者,对于广播来说,是对于已发生正在被播报的事件的描述。对于向不同类型的组建传递意图,有不同的机制:

  • 一个Intent对象被传递给Context.startActivity()或Activity.startActivityForResult()以启动一个活动,或是让一个已存在的活动进行新的工作。(它也可以被传递给Activity.setResult()使其向调用了startActivityForResult()的活动返回信息。)
  • 一个Intent对象被传递给Context.startService()以初始化一个服务或向正在运行的服务发出新的指令。同样的,一个意图可以被传递给Context.bindService()以在进行调用的组件和目标服务间建立联系。如果服务不在运行,还可以选择初始化该服务。
  • 被传递给任何广播方法(例如Context.sendBroadcast()、Context.sendOrderedBroadcast()或Context.sendStickyBroadcast())的Intent对象将被发送给所有相关的广播接收者。很多种类的广播在系统代码中被创建。

在任何一种情况下,Android系统将寻找合适的活动、服务或广播接收者来响应意图,在必要时对它们进行初始化。这些消息系统之间没有交集:广播意图仅被发送给广播接收者,而不会给活动或是服务。一个传递给startActivity()的意图将仅被发送给一个活动,而不会是服务或广播接收者,依此类推。

本文档将首先对Intent对象进行描述。之后介绍Android用以映射意图至组建的规则——它如何了解哪一个组建应当接收意图消息。对于没有显式地命名目标组件的意图,这个过程将包括使用与可能对象关联的意图过滤器(intent filter)来测试Intent对象。

Intent对象

Intent对象是一捆数据。它包含了接受该意图的组件所感兴趣的信息(例如要采取的操作及操作的对象数据)及Android系统所需的信息(例如要处理该意图的组件类别及如何启动目标活动的指令)。原则上,它包含以下内容:

组件名称

应当处理该意图的组件名称。这部分是一个ComponentName对象——一种目标组件的完整类名(例如“com.example.project.app.FreneticActivity”)和组件所在程序的manifest文件中的包名称组(例如“com.example.project”)的组合。组件名称的包部分和manifest中的包名称组不一定要相同。

组件名称是可选的。如果被设定,Intent对象将被发送给指定类的实例。如果没有设定,Android使用Intent对象中的其他信息来选择合适的目标——参见本文档中之后的“意图解决方案”。

组件名称通过setComponent()、setClass()或setClassName()来设定,通过getComponent()来读取。

行为

命名了要被执行的行为的字符串——或者,对于广播的意图来说将要发生并被报告的行为。Intent类定义了很多行为常量,包括:

常量 目标组件 行为
ACTION_CALL 活 动 初始化一次电话呼叫。
ACTION_EDIT 活 动 向用户显示数据以供编辑。
ACTION_MAIN 活 动 作为一个任务(task)的首活动启动,不包含数据输入且没有返回的输出。
ACTION_SYNC 活 动 以移动设备上的数据同步服务器上的数据。
ACTION_BATTERY_LOW 广播 接收者 电量低的警告。
ACTION_HEADSET_PLUG 广播接收者 耳机插入或是拔出设备。
ACTION_SCREEN_ON 广播接收者 屏幕被打开。
ACTION_TIMEZONE_CHANGED 广播接收者 时区设定被改变。

参见Intent类的描述以获知通用行为的预定义常量列表。其他的行为在Android API的其他部分被定义。也可以为激活自己的程序中的组件而定义自有的行为字符串。它们应当包含作为前缀的程序包——例如:“com.example.project.SHOW_COLOR”

行为很大程度上决定了意图剩余部分的结构——特别是数据和额外信息——就好像是一个方法的名称决定了一组参数和返回值。因此,最好尽可能具体地使用行为名称,将它们紧密地和意图的其他部分配对。换言之,不要单独定义一个行为,而是要为组件可以处理的Intent对象定义一整套的协议。

一个Intent对象的行为通过setAction()方法设置,通过getAction()来读取。

数据

将被执行操作的数据的URI及数据的MIME类型。不同的行为和不同的数据规格相对应。例如,如果行为域是ACTION_EDIT,那数据域就需要包含要被显示以编辑的文档的URI。如果行为是ACTION_CALL,那数据域需要是一个要拨打的号码的tel: URI。同样地,如果行为是ACTION_VIEW且数据域是http: URI,那接收方活动将会下载或显示该URI指向的任何数据。

在配对一个意图和一个有能力处理其数据的组件时,知道数据的类型(即它的MIME类型)以及URI是很重要的。例如,一个可以显示图像数据的组建不应被调用来播放音频文件。

在许多情况下,数据类型可以由URI获知——特别是content: URI,它表明数据位于设备中并由一个内容提供者所控制(参见另外的关于内容提供者的解说)。不过也可以在Intent对象内显式地设定类型。setData()方法以仅URI来指定数据,setType()仅以MIME类型来指定数据,setDataAndType()同时以URI和MIME类型来指定数据。URI通过getData()读取,类型由getType()读取。

类别

一个包含了应该由哪一种类型的组件来处理意图的额外信息的字符串。Intent对象中可以存放任何数量的类别描述。和行为一样,Intent类定义了一些类别常量,其中包括:

常量 意义
CATEGORY_BROWSABLE 目标活动可以安全地被浏览器用于显示某一链接指向的数据——例如,一幅图像或是一条电子邮件消息。
CATEGORY_GADGET 该活动可以被另一个持有小部件(gadget)的活动嵌于内部。
CATEGORY_HOME 该活动显示主界面(home screen)这一用户启动设备或按下HOME键后首先看到的画面。
CATEGORY_LAUNCHER 该活动是一个任务中的首活动,被列于应用程序启动器的首级。
CATEGORY_PREFERENCE 目标活动是一个偏好设置面板。

 参见Intent类的描述以获知完整的类别列表。

addCategory()方法将一个类别存入一个Intent对象,removeCategory()则删除之前添加的一个类别,getCategories()用于获取当前在意图对象内的整个类别集。

额外信息

关于需要被传递给要处理意图的组件的其他信息的键值对(key-value pair)。如同一些行为与特定类型的数据URI相配对,另一些会与特定的额外信息相配对。例如,一个ACTION_TIMEZONE_CHANGED意图有一个额外信息“time-zone”来识别新的时区,ACTION_HEADSET_PLUG则有一个额外信息“state”来识别耳机是被插入还是拔出,另外还有额外信息“name”来识别耳机的类型。如果想要设计一个SHOW_COLOR行为,颜色的值将被设置于一个额外的键值对之中。

Intent对象有一系列的put…()方法来插入不同类型的额外数据,类似地有一组get…()方法来读取这些值。这些方法和Bundle对象中的相类似。事实上,额外信息可以如同一个Bundle那样通过putExtras()和getExtras()方法来被设置和读取。

旗标

各种类型的旗标。其中很多指示了Android系统如何去启动一个活动(例如,该活动应该属于哪一个任务)以及在启动后应该如何处理它(例如,它是否应该属于最近活动列表)。所有的这些旗标在Intent类中被定义。

Android系统和该平台的应用程序使用Intent对象来发出系统创建的广播及激活系统定义的组件。要了解如果构建一个意图以激活一个系统组件,参见附录中的意图列表。

意图的解决方案

意图可以被分为两类:

  • 显式意图通过其名称指定目标组件(前面提到过,该组件的名称域有一个值集)。因为组件的名称通常不会被其他程序的开发者知道,显式的意图往往被用于程序内部消息——例如一个活动启动一个附属的服务或是一个平级的活动。
  • 隐式意图不会命名目标(组件的名称域是空白的)。隐式意图常用于激活其他活动中的组件。

Android将一个显式意图传递给所指定的目标类的一个实例。只有Intent对象中的组件名称会影响到意图最终将被传送给哪一个组件。

对于隐式意图需要一种不同的策略。由于没有指定的目标,Android系统必须找到最为合适的那一组件(或那一些组件)——一个单独的执行所请求的行动的活动或服务,或是一组广播接收者来响应广播通告。系统通过比较Intent对象的内容和意图过滤器(和组件相关联的用以接收潜在意图的结构)来实现这一目的。过滤器告知了一个组件的功能并界定了它可以处理的意图。它们使得组件可能接收所告知的类型的隐式意图。如果一个组件没有任何的意图过滤器,它将只能接收显示意图。一个拥有意图过滤器的组件可以同时接收显式和隐式意图。

当一个Intent对象被意图过滤器检查时只有三个方面会被考虑:

行为

数据(同时包括URI和数据类型)

类别

额外信息和旗标不会影响到决定由哪个组件接收意图。

意图过滤器(Intent filter)

为了告知系统它可以处理哪些隐式意图,活动、服务和广播接收者都可以拥有一个或多个意图过滤器。每一个过滤器将描述组件的某一种功能以及该组件要接收的意图集。它有效地过滤下所需的类型,排除不需要的意图——不过仅能排除不需要的(未命名目标类的)隐式意图。显式意图将始终被传递给其目标而无论它包含什么内容;过滤器不会起作用。然而一个隐式意图就只有在通过了组件的过滤器之后才能够被传递给组件。

一个组件对每一类它可以处理的工作及用户所能看见的每一种形式分别有着不同的过滤器。例如,范例中Note Pad程序的NoteEditor活动有两个过滤器——一个用于创建用户可以查看并编辑的特定便筏,另一个用于启动一个空的用户可以输入并保存的新的便筏。(Note Pad的所有的过滤器将会在之后的Note Pad范例一节中详细说明。)

一个意图过滤器是IntentFilter类的一个实例。不过因为Android系统必须在启动一个组件之前知道它所具有的功能,所以意图过滤器一般不在Java代码中使用,而是作为程序的manifest文件中(AndroidManifest.xml)的<intent-filter>元素。(不过有一个例外是广播接收者的过滤器是通过调用Context.registerReceiver()动态地在代码中注册的;它们被作为IntentFilter对象直接创建。)

一个过滤器有着和Intent对象中的行为、数据和类别域相对应的域。一个隐式意图将检测过滤器的所有这三个内容。要被传递给某个组件,一个意图必须通过该组件的过滤器的所有三项检测。即使只有一项不满足,Android系统也不会将该意图传递给组件——至少不会是基于过滤器传递给组件。不过,由于一个组件可以有多个意图过滤器,一个意图如果无法通过其中一个过滤器还是有可能能通过其他的的。

过滤器和安全

意图过滤器不能确保安全性。尽管它只让一个组件接受特定类型的隐式意图,它无法阻止显式意图指向目标组件。即使过滤器约束了意图使其只能使用将会被请求处理特定的行为和数据源的组件,一些人仍然可以将一个显式意图与不同的行为和数据源结合在一起,并将目标以组件的名称来命名。

下面将详细描述三种检测是如何进行的:

行为检测

manifest文件中的一个<intent-filter>元素以<action>子元素的形式列出了行为。例如:

如同示例所展示的,一个Intent对象只命名一个单独的行为,而一个过滤器可以列出多个行为。列表不能为空;一个过滤器至少要包含一个<action>元素,否则它将屏蔽所有的意图。

要通过这个检测,在Intent对象中指定的行为必须与过滤器所列出的行为之一相匹配。如果该对象或是过滤器没有指定一个行为,就会有下面的结果:

  • 如果过滤器没有列出任何行为,就没有行为可以与意图相匹配,所有的意图都无法通过检测。没有意图可以通过过滤器。
  • 另一方面,如果Intent对象没有指定任何的行为,它将自动通过检测——只要过滤器含有一个或以上的行为。

类别检测

<intent-filter>也会将类别作为子元素列出。例如:

注意之前描述过的表示行为和类型的常量不在mainfest文件中被使用。而是使用完整的字符串值。例如,在范例中的“android.intent.category.BROWSABLE”字符串和文档之前提到的CATEGORY_BROWSABLE常量相对应。类似地,字符串“android.intent.action.EDIT”ACTION_EDIT常量相对应。

一个意图要通过类别检测,Intent对象中的每一个类别都必须与过滤器中的某个类别相匹配。过滤器可以列出其他更多的类别,但不能省略任何一个意图中含有的类别。

原则上,所以说,一个不含有类别的Intent对象,无论过滤器中有哪些类别,总是能够通过这项检测。这通常是正确的。不过,有一个例外,Android把所有传递给startActivity()的隐式意图视为它们包含了至少这样一个类别:“android.intent.category.DEFAULT”CATEGORY_DEFAULT常量)。因此,要接受隐式意图的活动必须在其意图过滤器中包含有“android.intent.category.DEFAULT”。(设有“android.intent.action.MAIN”“android.intent.category.LAUNCHER”的过滤器不在此范围之中。它们将活动标记为了新任务的开始,并显示在应用启动器(launcher)屏幕上。它们可以在类别列表中包含“android.intent.category.DEFAULT”,不过这并不是必须的。)参见之后的“使用意图匹配”来进一步了解这些过滤器。

数据检测

如同行为和类别,意图过滤器的的数据类型也是作为子元素保存的。而且和它们一样,这些子元素可以出现多次或完全不出现。例如:

每一个<data>元素可以指定一个URI和数据类型(MINE媒体类型)。URI的每一个部分由不同的属性——模式(scheme)、主机(host)、接口(port)和路径(path)。

scheme://host:port/path

例如,下面的URI,

content://com.example.project:200/folder/subfolder/etc

其模式是“content”,主机是“com.example.project”,接口是“200”,路径是“folder/subfolder/etc”。主机和接口共同构成了URI的授权;如果没有指定主机,那么接口将被忽略。

每一个属性都是可选的,但并非与其他属性相互独立:要让一个授权变得有意义,必须指定一个模式。要让路径具有意义,必须指定一个模式和一个授权。

当一个Intent对象中的URI和过滤器中的URI相比较时,仅比较过滤器中所包含的URI部分。例如,如果一个过滤器仅指定了一个模式,那所有具有这个模式的URI就与过滤器相匹配。如果过滤器指定了一个模式及一个授权但是没有指定路径,那么无论是什么路径,具有相同模式和授权的URI将得到匹配。如果过滤器指定了模式、授权和路径,那就只有具有相同模式、授权和路径的URI得到匹配。不过,过滤器中指定的路径可以包含通配符以仅仅限定部分路径。

一个<data>元素的类型(type)属性指定了数据的MINE类型。在过滤器中这比URI更为常见。Intent对象和过滤器都可以用”*”通配符作为子类别域来标识子类别匹配,例如“text/*”“audio/*”

数据检测同时将Intent对象中的URI和数据类型与过滤器中的URI和数据类型相比较。该过程遵循以下规则:

  • 不包含URI和数据类型的Intent对象只有在过滤器也不指定任何URI及数据类型时才能通过检测。
  • 包含URI但不包含数据类型(且数据类型不能通过URI推断出来)的Intent对象只有在其URI与过滤器中的URI相匹配且过滤器同样没有指定类型时才能通过检测。这是仅仅在类似mailto:和tel:这样没有指向实际数据的URI时才会出现的情况。
  • 包含数据类型但不包含URI的Intent对象只有在过滤器列出了相同的数据类型而没有指定URI时才能通过检测。
  • 同时包含有URI和数据类型(或URI可以推导出数据类型)的Intent对象在其数据类型与过滤器中列出的类型相匹配时通过检测的数据类型部分。当其URI与过滤器中的URI相匹配,或,其具有一个content:file:URI并且过滤器没有指定URI时,通过测试的URI部分。换言之,如果过滤器仅列出了数据类型,一个组件将被假定支持content:file:数据。

如果一个意图可以通过不止一个活动或服务的过滤器,用户将被询问要激活哪一个组件。如果没有一个目标能被找到,则会抛出一个例外。

常见情况

上面数据检测中的最后一条规则,说明了组件可以从一个文件或是内容提供者处获取本地数据。因此,它们的过滤器可以仅仅列出数据类型而不需要显式地命名content:file:模式。这是一种典型的情况。比如说,一个如下的<data>元素,告诉Android组件可以从内容提供者处获取图像数据并显示:

因为大部分可用的数据由数据提供者分发,所以指定了数据类型而没有指定URI的过滤器也许是最为常见的了。

另一种常见的情况是过滤器指定了模式和数据类型。例如,如下的一个<data>元素告诉Android组件可以从网络获取视频数据并播放:

试想,例如,浏览器类应用在用户打开网页上的一个链接后将做什么。它首先尝试显示数据(如果该链接是一个HTML页面的话)。如果它无法显示数据,它将生成一个包含模式和数据类型的隐式意图并试图启动一个可以处理该工作的活动。如果没有合适的活动,它将请求下载管理器下载该数据。这处于内容提供者的控制之下,所以有大量的可能合适的活动(它们的过滤器仅指定了数据类型)将能够做出响应。

大部分程序还有一种不需要任何指定数据引用的开始刷新的方法。能够初始化一个程序的活动有着将行为指定为”android.intent.action.MAIN”的过滤器。如果它们需要被显示在应用程序启动器中的话,它们还需要被指定为”android.intent.category.LAUNCHER”类别:

使用意图匹配

将意图与意图过滤器相匹配不仅仅是为了寻找要激活的某一目标组件,同时也是要获取设备上的某一组组件的某些属性。例如,Android系统通过查找所有具有指定了“android.intent.action.MAIN”行为和“android.intent.category.LAUNCHER”类别的过滤器的活动(如前一节所示)来弹出应用程序启动器这一列出了所有可供用户启动的程序的顶层屏幕内容。之后它在启动器中显示那些活动的图标(icon)和标签(label)。同样地,它通过查找具有“android.intent.category.HOME”类别的过滤器的活动来显示主界面。

程序可以以一种类似的方式来使用意图匹配。PackageManager拥有一组query…()方法来返回所有可以接受某一特定意图的组件,以及一系列的resolve…()方法来决定响应意图最合适的组件。例如,queryIntentActivities()将返回一个列出了所有可以将意图作为参数传递的活动的列表,queryIntentServices()将返回一个类似作用的所有服务的列表。这两种方法都不会激活组件;它们只是列出了那些可以做出响应的。对于广播接收者,还有一个类似的queryBroadcastReceivers()方法。

记事本(Note Pad)范例

记事本范例允许用户浏览笔记列表、查看列表中项目的详细内容、编辑项目、向列表增加新项目。本节将分析在其manifest文件中声明的意图过滤器(如果正在离线使用SDK,可以在<sdk>/samples/NotePad/index.html下找到包括manifest文件在内的该范例程序的所有源文件。如果是在在线查看文档,源文件则是在这里的“教程和范例代码”一节。

在manifest文件中,记事本程序声明了三个活动,每一个都有至少一个意图过滤器。它还声明了一个内容提供者用以管理笔记数据。以下是整个manifest文件的内容:

第一个活动,NoteList,和其他的活动不同,它要对一系列的笔记(笔记列表)进行操作而不是某条单独的笔记。它通常将作为用户进入程序的初始界面。如其三个意图过滤器所描述的,它能执行三项工作:

该过滤器声明了Note Pad程序的主入口。标准MAIN行为是一种不需要Intent中的其他信息(比如不需要数据类型)的入口(entry point),LAUNCHER类别则说明该入口应该被列于应用程序启动器中。

该过滤器声明了这个活动可以对一列笔记进行何种操作。它可以允许用户查看或编辑笔记(通过VIEWEDIT行为),或是从列表中选取一条特定笔记(通过PICK行为)。

<data>元素的mimeType属性指定了这些行为要执行操作的数据类型。它表明了这个活动可以从一个持有Note Pad数据的内容提供器(vnd.google.note)中获取一个指向零个或更多个项目(vnd.android.cursor.dir)的Cursor。启动该活动的Intent对象包含一个content: URI用以指定活动应该打开的确切数据类型。

也请注意该过滤器中提供的DEFAULT类别,因为Context.startActivity()和Activity.startActivityForResult()方法在意图包含DEFAULT类别时将会对其进行处理——只有两种例外:

  • 显式地命名了目标活动的Intent
  • 包含了MAIN行为和LAUNCHER类别的Intent

因此,所有的过滤器都需要DEFAULT类别——除非有MAIN行为和LAUNCHER类别。(意图过滤器不过滤显式意图)

该过滤器说明了这个活动可以返回一条用户选中的笔记而不需要这条笔记所在的列表的相关信息。GET_CONTENT行为类似于PICK行为。两种情况下活动都将返回用户所选择的笔记的URI。(将返回给调用了startActivityForResult()来启动NoteList活动的那个活动。)不过现在,调用者指定了所需的数据类型而不是用户要进行选择的数据列表的类型。

数据类型,vnd.android.cursor.item/vnd.google.note,标明了该活动可以返回的数据的类型——一条单笔记的URI。通过所返回的URI,调用者可以从持有Note Pad数据(vnd.google.note)的内容提供其中获取某一特定条目(vnd.android.cursor.item)的Cursor。

也就是说,前一个过滤器中的PICK行为,数据类型标识出了活动可以显示给用户的数据的类型。而GET_CONTENT过滤器则标识出了活动可以返回给它的调用者的数据的类型。

有了这些能力,下面的意图可以实现活动NotesList的功能:

行为:android.intent.action.MAIN

不需要特定的数据就能启动活动

行为:android.intent.action.MAIN 

类别:android.intent.category.LAUNCHER

不需要特定的被选择的数据就能启动活动。这是实际上应用程序启动器用来填充其最高层列表(top-level list)的意图。所有具有匹配该行为和类别的过滤器的活动将被添加至列表。

行为:android.intent.action.VIEW 

数据:content://com.google.provider.NotePad/notes

请求活动显示在content://com.google.provider.NotePad/notes下的所有笔记的列表。之后用户可以浏览该列表并获取列表中项目的信息。

行为:android.intent.action.PICK 

数据:content://com.google.provider.NotePad/notes

请求活动显示在content://com.google.provider.NotePad/notes下的笔记的列表。之后用户可以从列表中选取一条笔记,活动将返回该条目的URI给启动了NoteList活动的那个活动。

行为:android.intent.action.GET_CONTENT 

数据类型:vnd.android.cursor.item/vnd.google.note

请求活动提供一条单独的Note Pad数据。

第二个活动,NoteEditor,向用户显示一条单独的笔记条目并允许对其进行编辑。根据其两个意图过滤器的描述,它可以进行两项任务:

这个活动的第一个,也是主要的目的是使用户与一条笔记交互,VIEW这条笔记或是EDIT它。(类别EDIT_NOTEEDIT的意义相同。)该意图将包含匹配MIME类型vnd.android.cursor.item/vnd.google.note的数据的URI——也就是说,这条指定的笔记的URI。它通常是被NoteList活动的PICKGET_CONTENT行为所返回的URI。

和之前一样,该过滤器列出了DEFAULT类别因此这个活动可以被其他没有显式地指定NoteEditor类的意图启动。

这个活动的第二个目的是使用户创建一条新的笔记并INSERT至现有的笔记列表中。该意图包含了匹配MIME类型vnd.android.cursor.dir/vnd.google.note的数据的URI——即笔记将被插入的笔记列表的URI。

有了这些能力,下面的意图可以实现NoteEditor活动的功能:

行为:android.intent.action.VIEW 

数据:content://com.google.provider.NotePad/notes/ID

请求活动显示由ID确定的笔记的内容。(关于content: URIs是如何指定一个群组内的个别成员的,请参见“内容提供器”。)

行为:android.intent.action.EDIT 

数据:content://com.google.provider.NotePad/notes/ID

请求活动显示由ID确定的笔记的内容,并允许用户对其进行编辑。如果用户保存了修改,该活动将更新内容提供器中的笔记数据。

行为:android.intent.action.INSERT 

数据:content://com.google.provider.NotePad/notes

请求活动在content://com.google.provider.NotePad/note的笔记列表中创建一个新的空笔记并允许用户对其进行编辑。如果用户保存了笔记,其URI将被返回给调用者。

最后一个活动,TitleEditor,允许用户编辑笔记的标题。这可以通过直接调用该活动来实现(通过显式地在Intent中设定该组件名称),而不需要通过一个意图过滤器。不过这里主要关注如何对现存的数据进行其他操作:

这个活动的唯一的意图过滤器使用了一个名为”com.android.notepad.action.EDIT_TITLE“的自定义行为。它必须像之前的VIEWEDIT行为一样通过一条指定的笔记调用(数据类型vnd.android.cursor.item/vnd.google.note)。不过,这个活动将显示包含于笔记数据中的标题,而不是笔记内容自身。

另外为了支持常见的DEFAULT类别,这个标题编辑器也支持其他两种常见类别:ALTERNATIVE和SELECTED_ALTERNATIVE。这两个类别表明了活动可以在一个选项菜单中显示给用户(就好象LAUNCHER类别表明了活动应当在应用程序启动器中显示给用户一样)。注意过滤器也支持一个显式标签(通过android:label=”@string/resolve_title”)以更好地控制用户能够看到当前被这个活动以其他行为方式查看的数据的哪些内容。(关于这些类别和创建选项菜单的更多信息,请参见PackageManager.queryIntentActivityOptions()和Menu.addIntentOptions()方法。)

有了这些能力,下面的意图就可以实现TitleEditor活动的功能:

行为:com.android.notepad.action.EDIT_TITLE 

数据:content://com.google.provider.NotePad/notes/ID

请求活动显示和笔记ID相关联的标题,并允许用户对其进行编辑。

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

媒体

Android平台为多种常见媒体类型提供了内建的编码/解码支持,因而可以简单地向程序整合音频、视频和图像。要使用平台的媒体功能非常简单——同样只需使用意图-活动的机制,之后Android会处理其余的工作。

Android可以从多种数据源类型中播放音频和视频。可以播放存储在程序资源(原始资源 raw resource)中的音频或视频媒体文件,可以播放文件系统中的某一文件,可以播放经由网络的流数据。要让程序播放视频或音频,需使用MediaPlayer类。

如果移动设备硬件支持的话,平台也支持录制音频和视频。要录制音频或视频,需使用MediaRecorder类。注意,模拟器没有获取音频或视频的硬件,不过实际的移动设备通常会提供这些支持,可以通过MediaRecorder类使用。

要了解Android提供内建支持的媒体格式列表,参见附录“Android媒体格式”。

音频和视频播放

可以播放任意来源的媒体:原始资源、系统中的文件或是可用的网络(URL)。

仅可以通过标准输出设备播放音频数据;目前来说,就是至移动设备的扬声器或是蓝牙耳机。现在不能在通话音频中播放声音文件。

播放原始资源

最为常见的需求或许就是从自有程序中播放媒体(一般是声音)了吧。这很容易做到:

  1. 将声音(或其他媒体资源)文件放入工程的res/raw文件夹,Eclipse插件(或aapt)将会发现它并将其识别为R类可以引用的资源。
  2. 创建一个MediaPlayer的实例,用MediaPlayer.create引用该资源,之后调用实例的start()方法:

要停止播放,调用stop()。如果希望之后重放该媒体,就必须在再次调用start()之前reset()及prepare()该MediaPlayer对象。(create()在第一次使用时会调用prepare()。)

要暂停播放,调用pause()。在想要继续播放的时候调用start()。

播放文件或流

可以播放储存于文件系统中或位于网络URL的媒体文件:

  1. 使用new来创建一个新的MediaPlayer实例
  2. 以一个包含了要播放的文件的路径(文件地址或是URL)的字符串调用setDataSource()
  3. 首先prepare()之后start()该实例:

stop()和pause()的工作方式同上。

注意:当引用的文件不存在时,IllegalArgumentExceptionIOException 可能会在使用setDataSource()时被接收或传递。

注意:如果传递的是在线媒体文件的URL,该文件必须可以被逐步下载。

播放JET内容

Android平台包含了一个JET引擎,它允许在程序中添加与JET音频内容的播放控制交互。可以使用SDK附带的JetCreator授权程序创建JET内容的播放控制交互。要在程序中播放并管理JET内容,需要使用JetPlayer类。

关于JET理念的描述及使用JetCreator授权工具的使用说明,参见“JetCreator用户手册”。该工具在OS X和Windows平台下全功能可用,在Linux下所有的内容创建功能也都被支持,只是输入内容审查功能不被支持。

这里有一个如何从一个储存于SD卡的.jet文件中建立JET播放的例子:

SDK包含了一个程序范例——JetBoy——演示了如何使用JetPlayer创建游戏中的音乐音轨播放交互。它同时也展示了如何使用JET事件来同步音乐播放和游戏逻辑。该程序位于<sdk>/platforms/android-1.5/samples/JetBoy

音频录制

通过设备录制音频要比播放音频/视频稍微复杂一些,不过其实也是相当简单:

  1. 使用new来创建android.media.MediaRecorder的一个新的实例
  2. 使用MediaRecorder.setAudioSource()来设置音频源。通常会使用MediaRecorder.AudioSource.MIC
  3. 使用MediaRecorder.setOutputFormat()来设置输出文件格式
  4. 使用MediaRecorder.setOutputFile()来设置输出文件名
  5. 使用MediaRecorder.setAudioEncoder()来设置音频编码
  6. 对MediaRecorder实例调用MediaRecorder.prepare()
  7. 调用MediaRecorder.start()以开始音频录制
  8. 调用MediaRecorder.stop()以结束音频录制
  9. 当不再需要MediaRecorder实例时,对其调用MediaRecorder.release()。建议调用MediaRecorder.release()来立即释放资源
范例:录制音频并播放录制的音频

下面的这个范例类演示了如何建立、开始及终止音频录制,之后播放录制的音频文件。

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

Android SDK升级至revision 12后的一个Bug及解决方案

今天把SDK从revision 11升级至了最新的revision 12,却发现出现了问题。模拟器无法运行,在运行时提示:

也就是说似乎是由于参数错误导致无法启动模拟器。

在网上还看到了有人出现这样的错误信息:

经过一番折腾之后终于找到了问题所在。应该是路径的问题。路径参数之前还有内容,被忽略了。这应该是本次升级的一个Bug。SDK所在文件夹的名称中如果有空格就会导致问题的发生(我使用的是android SDK这一名称)。于是将文件夹重命名为android-SDK之后,问题解决。

Android编程中Debug certificate expired错误的解决方法

今天晚上突然eclipse中所有的项目都报错,项目文件夹上标记了叉号。提示“Your project contains error(s),please fix them begore running your application”。可是可以肯定的是它们的代码并没有问题。

一查看,发现问题信息是Error generating final archive: Debug certificate expired on 2011/06/22 19:10! 无语……

既然现有的签名过期了,那就重新生成一个吧。

在命令提示行中,通过以下命令生成新的密钥:

密码是 android

validity设为10000可以保证数十年的期限。

将生成的keystore替换原有的。或者在eclipse – Window – preferences – Android – build中将路径改为新的keystore。

问题解决。

Android中调用摄像头并自动对焦拍照

代码与范例:
首先需要在Manifest文件中添加以下内容:

以使程序获取摄像头使用以及自动对焦功能的使用权限。

CameraTestActivity.java

CameraView.java

说明:这是一个简单的示例程序。功能是使用自动对焦功能拍摄一张照片保存于SD卡根目录下。需要注意的是要在manifest文件中声明相关的许可。本例中许多参数都没有设置,可以根据具体需要进行适当的修改以满足特定要求。

Android中EditText在软键盘弹出后自适应改变大小

在使用EditText的过程中遇到的一个问题:具有android:layout_height=”fill_parent”属性的EditText控件在弹出软键盘后高度仍然保持了原有的值,以至于最上方的内容无法被显示。

于是找了一下解决方法,意外的简单,只需要为该EditText控件添加android:scrollbars=”vertical”即可。

安全性和许可

Android是一个权限分离(privilege-separated)的操作系统,其中的每一个运行的程序有一个单独的系统识别(identity)(Linux 用户ID及组ID)。此外系统的不同部分也被分入了不同的识别。通过这种方式Linux使不同的程序和系统之间相互独立。

其他的高级安全特性也由一种称为“权限”的机制所提供,它强行规定了一个进程可以执行哪些特定操作,同时URI级别的权限(per-URI permission)担保了对于特定数据片段的ad-hoc访问。

安全构架

Android安全构架的一个中心设计要点就是默认来说没有程序有进行任何可能对其他程序、操作系统或是用户产生负面影响的任何操作的许可(permission)。这包括了读写用户私人数据(例如通讯录或是电子邮件),读写其他程序的文件,执行网络接入或保持设备不进入睡眠状态等。

因为内核将程序通过沙盒分离,所以程序必须显式地共享资源及数据。它们通过声明许可来获取它们所需的没有通过基本沙盒提供的额外功能。程序静态地声明它们所需的许可,Android系统将在程序安装时向用户征求许可。Android没有动态(在运行时)担保许可的机制,因为这将使安全性受损,使得用户体验变糟。

只有内核负责将程序通过沙盒与其他程序分离。尤其要注意Dalvik VM以及任何可以运行原生代码(native code)的程序(参见Android NDK)并不是安全边界。所有类型的程序——Java,原生(native)或是混合(hybrid)——都以同样方式被沙盒化,相互之间拥有相同的级别的安全性。

应用程序签名

所有的Android应用程序(.apk文件)必须用一个开发者拥有其私钥的认证(certificate)来进行签名。该认证用于识别应用程序的权限。一个认证不需要由认证权威来进行签名:Android程序完全可以,而且通常也会,使用自签名的认证。Android的认证的目的是为了识别应用程序作者。这使得系统可以允许或是拒绝程序获取签名级别(signature-level)的许可以及程序希望获取“相同作者程序”的Linux识别的请求。

用户ID与文件读取

在安装时,Android分给每一个包(package)一个独自的Linux用户ID。该识别将在这个包在设备上的整个周期内保持不变。在不同的设备上,同一个包可能会有不同的UID;不过重要的是在给定的某一个设备上每一个包都有一个独自的UID。

因为安全强化实在进程级别进行的,所以两个不同包的代码无法正常运行于同一个进程,他们需要作为不同的Linux用户运行。可以在每一个包的AndroidManifest.xml的manifest标签中使用sharedUserId属性来将其标为相同的用户ID。这样一来,这两个包就被认为是同一个程序,有同样的用户ID和文件权限了。注意为了保持安全性,只有两个有同样签名(且请求了同样的sharedUserId)的程序才会被分配同样的用户ID。

一个程序存储的任何数据会被分配以这个程序的用户ID,而无法被其他包正常访问。当使用getSharedPreferences(String, int),openFileOutput(String, int)或openOrCreateDatabase(String, int, SQliteDatabase.CursorFactory)来创建新文件时,可以用MODE_WORLD_READABLE和/或MODE_WORLD_WRITEABLE 旗标来允许其他包读/写该文件。当设置了这些旗标后,文件仍然属于原来的程序,但是因为设置了全局读/写许可,所以对任何其他程序都可见。

使用许可

一个基本的Android程序没有与之关联的许可,这意味着它无法进行任何可能影响用户体验或是影响设备上任何数据的操作。要利用这些设备上受保护的特性,就必须在AndroidManifest.xml文件中包含一个或更多的<uses-permission>标签来申明程序所需的许可。

例如,一个需要监视收到的短信的程序应该指定:

在程序安装时,基于用户对声明了许可和/或交互的程序签名的检查,包安装器(package installer)将会认可程序所请求的许可。在程序运行时不会进行任何检查:要么在安装时许可被允许而可以如所希望的那样使用该特性,要么许可不被认可,任何使用该特性的操作将不经过用户判断而直接拒绝。

通常许可错误会导致一个SecurityException被抛回程序。然而,这也不是在所有情况下都会发生的事。例如,sendBroadcast(Intent)方法会在方法调用被返回之后检查数据被传递至每一个接收者的许可,因此即使有许可错误,也不会收到异常。不过,在绝大多数情况下,许可失败都将会被打印于系统日志上。

Android系统所提供的许可可以在Manifest.permission中找到。任何程序也可以定义并执行其自有许可,因此那并非是所有可用许可的完整列表。

一个特定的许可可以在程序执行时被执行于多个地方:

  • 在系统执行一个调用时,阻止应用程序执行某一功能。
  • 在启动一个活动时,阻止应用程序启动属于其他程序的活动。
  • 在发送和接收广播时,控制广播的接收方和发送方。
  • 在读取并操作内容提供者时。
  • 在绑定或启动一个服务时。

声明并执行许可

要执行自有许可,就必须首先在AndroidManifest.xml中通过一个或多个<permission>标签来声明它们。

例如,如果一个程序希望可以控制谁能启动它的活动,它可以像这样为这个操作声明一个许可:

<protectionLevel>属性是必需的,它会按链接文档中描述的那样告诉系统用户将如何被告知程序需要该许可,或是谁将被允许拥有这个许可。

<permissionGroup>属性是可选的,仅仅被用来帮助系统向用户显示该许可。通常会对一个标准系统组(standard system group)(列于android.Manifest.permission_group)或在某些特殊情况下对自己定义的系统组设置该属性。推荐使用一个已有的组,以简化向用户显示的许可界面。

注意必须同时向许可提供标签(label)和描述(description)。它们是用户在查看一列许可(android:label)或某一许可的详细内容(android:description)时将被显示的字符串资源。标签应当简短,用几个词描述该许可所保护的主要功能。描述可以是说明该许可允许持有者能做什么事的几句句子。习惯上用两句话来描述,第一句描述该许可,第二句告知用户当程序获得该许可后将有哪些可能的不良后果。

这是一个CALL_PHONE许可的标签和描述的例子:

可以通过shell指令adb shell pm list permissions来查看当前系统中所定义的许可。一般来说,’-s’选项将会像展示给用户看到的那样展示这些许可。

在AndroidManifest.xml中执行许可

限制系统或程序的整体组件接入的高级别许可可以在AndroidManifest.xml中被应用。所需要做的只是将android:permission属性包含于所希望的组件之中,并为将被用于控制接入的许可命名。

Activity许可(应用于<activity>标签)限制了谁可以启动相关的活动。该许可在Context.startActivity()和Activity.startActivityForResult()的过程中会被检查;如果调用者没有所需的许可,该调用将抛出SecurityException。

Service许可(应用于<service>标签)限制了谁可以启动或是绑定相关的活动。该许可在Context.startService()、Context.stopService()和Context.bindService()的过程中会被检查;如果调用者没有所需的许可,该调用将抛出SecurityException。

BroadcastReceiver许可(应用于<receiver>标签)限制了谁可以向关联的接收者发送广播。该许可在Context.sendBroadcast()返回后系统试图向给定接收者传递所请求的广播时被检查。因此,许可错误不会向调用者抛出例外;它只是不会传递该意图。同样地,该许可可以用于Context.registerReceiver()控制谁可以向一个在代码中注册的接收者发送广播。另一方面,该许可也可以用于在调用Context.sendBroadcast()时限制哪一个BroadcastReceiver对象可以被允许接收广播(见下文)。

ContentProvider许可(应用于<provider>标签)限制了谁可以读取内容提供者内的数据。(内容提供者拥有一项重要的额外安全机制,允许它们调用URI许可。这将在之后说明。)和其他组件不同,内容提供者可以设置两个不同的属性:android:readPermission限制了谁可以读取该提供者,而android:writePermission限制了谁可以进行写操作。注意,如果一个提供者同时被读和写许可所保护,仅持有写许可并不意味着就可以读取提供者的内容。许可将在第一次检索一个提供者时和对提供者执行操作时被检查(如果没有持有任何一个许可,则将会抛出一个SecurityException)。使用ContentResolver.query()来请求持有读许可;使用ContentResolver.insert()、ContentResolver.update()和ContentResolver.delete()来请求写许可。在所有这些情况下,未持有所需的许可将导致调用抛出SecurityException 。

在发送广播时执行许可

另外,对于限制谁可以向一个注册的BroadcastReceiver发送Intent的许可(如同之前所描述的),还可以在发送广播时指定一个必需的许可。通过调用Context.sendBroadcast()及一个许可字符串,可以要求一个接收者程序必须持有该许可以接收广播。

注意,接收者和广播者都可以请求许可。在这时,Intent必须通过对两种许可的检查才能被传送至相关联的目标。

其他许可执行

强制的细化许可可以对任意服务调用执行。这通过Context.checkCallingPermission()方法执行。以一段所需的许可字符串调用后它将会返回一个整型值,显示该许可是否已被当前调用的进程所许可。注意这只能在执行来自另一进程中的调用时使用(这通常要通过一个由服务发布的IDL接口或是某些其他方法来实现)。

还有许多其他有用的检查许可的方法。如果拥有另一进程的pid,就可以用Context方法的Context.checkPermission(String, int, int)来检查违反该pid的许可。如有有另一个程序的包名,就可以直接用PackageManager方法PackageManager.checkPermission(String, String)来查看特定的包是否已经被某一指定许可所认可。

URI许可

在使用内容提供者时,至今所描述的标准许可系统常常是不足以胜任的。内容提供者可能需要读写许可以保护自己,同时它的直接客户端也需要处理其他程序的特殊URI以供执行操作。典型的例子是邮件程序中的附件。应当通过许可来保护对邮件的访问,因为这是用户敏感数据。不过,如果一个指向图像附件的URI被交给了图像查看器,该查看器将不会有打开附件的许可,因为它没有理由持有访问所有电子邮件的许可。

这个问题的解决方案是单URI许可(per-URI permission):当启动一个活动或是向一个活动返回结果时,调用者可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这将许可接收方访问Intent内的指定数据URI,而不论它是否拥有任何访问和Intent相对应的内容提供者中数据的许可。

这一机制使得常见的能力-风格模型(capability-style model)得以实现,用户交互操作(打开附件,从列表中选择联系人等)可以创建临时的细化许可权限。这是减少程序所需许可至仅有和其行为直接相关许可的工具。

然而,对细化URI许可的授权也需要一些拥有那些URI的内容提供者的配合。强烈建议内容提供者应用这一工具,通过android:grantUriPermissions属性或是<grant-uri-permissions>标签来声明它们支持这一特性。

更为详细的信息可以在Context.grantUriPermission()、Context.revokeUriPermission()和Context.checkUriPermission()方法中找到。

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

Android如何绘制视图

当一个Activity接收到焦点时,它将被请求绘制其布局(layout)。Android框架将处理这个绘制过程,同时Activity必须提供其布局层级的根节点。

绘制过程从布局的根节点开始。它被请求测量并绘制布局树。绘制过程通过遍历布局树并渲染每一个穿插于invalid区域View进行。每一个视图组顺次负责请求其每一个子视图进行绘制(以draw()方法),而每一个View则负责绘制自身。因为布局树是依次遍历的,这就意味着父视图将在其子视图之前(即位于其下方)被绘制,而兄弟视图根据其出现在数中的位置按顺序被绘制。

绘制布局是一个分为两条线路的过程:测量线路(measuring pass)和布局线路(layout pass)。测量线路由measure(int ,int)完成,是视图树的一次从上至下的遍历。在递归过程中每一个View沿着树向下推送它的尺寸。在测量线路的最后,每一个View就都已储存了其测量尺寸值。第二条线路在layout(int, int, int, int)中完成,它同样也是自上而下的。在这个线路中父视图负责通过在测量线路中计算得到的尺寸来定位其所有的子视图。

Android框架不允许绘制不在invalid区域内的View,同时它将负责绘制View的背景。

可以通过调用invalidate()来强制绘制一个View。

当一个View的measure()方法返回时,必须设定它与它的子孙类的getMeasuredWidth()和getMeasuredHeight()值。一个View的测量宽度和测量高度必须满足其父类的约束。这保证了在测量线路的最后,所有的父类可以符合所有子类的测量值。一个父类View可以不止一次对其子类调用measure()。例如,父类可以以未指定的尺寸来测量每一个子类以确定他们所需的尺寸,之后以实际值对其再次调用measure()来检查所有子类的未约束的尺寸是否过大或是过小。(即,如果子类和其应该占用的尺寸不符,父类将会进行干预,将规则设置为第二条线路)。【抱歉这部分翻译质量欠佳,之后会找时间考虑如何改进(其实很多部分翻译质量都需要提高,总之现在先完成第一遍再说了。渐渐开始理解那些中文版教材令人无法满意的翻译背后的翻译者了……)】

测量线路使用两个类来交流尺度。View.MeasureSpec类被View用来告诉其父类希望被测量和定位的情况。基本的LayoutParams类用来描述View所希望的高宽尺寸。对于每一个维度,可以指定以下之一:

  • 一个确切的数字
  • FILL_PARENT 表示View希望和其父类一样大(减去间隙)
  • WRAP_CONTENT 表示View希望其大小足以包含其内容(加上间隙)。

要实例化一个布局,需要调用requestLayout()。该方法也将在一个View认为自身不可能适合当前边框时自调用。

对于ViewGroup的不同子类有相对应的LayoutParams的子类。丽日,RelativeLayout有其专属的LayoutParams子类,它可以水平及垂直定位其子视图。

MeasureSpec用于将需求沿着布局树从父至子向下推送。一个MeasureSpec可以以下三种模式存在:

  • UNSPECIFIED: 这被父类用于决定某一子视图的期望尺度。例如,一个LineraLayout可以对其子类调用measure(),将高度设置为UNSPECIFIED,宽度设置为EXACTLY 240以确定该子类在被分配了240像素宽度时所需的高度是多少。
  • EXACTLY: 这被父类用于强制设置其子类的确切尺寸。子类必须使用该尺寸,且保证它的所有子嗣也都满足这个尺寸限定。
  • AT_MOST: 这被父类用语强制设置其子类的最大尺寸。子类必须保证它及其子嗣满足该尺寸。

返回“用户界面”

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