- Android开发权威指南(第二版)
- 李宁编著
- 139字
- 2024-12-21 14:45:44
7.1 Activity XML属性详解
Android应用程序中的每一个Activity都必须在AndroidManifest.xml文件中通过<activity>标签声明才能使用。在前面章节的例子中虽然使用到了android:name、android:label等属性,但却未对其详细说明。为了使读者能更好地控制Activity的各种行为,本节将介绍<activity>标签中常用的属性及其用法。
7.1.1 设置Activity的基本信息
android:name是<activity>标签中唯一必须设置的属性,该属性表示窗口类的名称。通常设置的是相对的类名(相对于<manifest>标签的package属性值),但也可以使用绝对的类名,或是包含一部分包名的类名。也就是package属性值放在前面,<activity>标签的android:name属性值放在后面。下面是这3种类名设置的例子。
指定窗口类名称的方法1:相对类名
在下面的代码中<manifest>标签的package属性值是“mobile.android.camera”,该属性值是Android应用程序的包名,也是该应用的ID。<activity>标签的android:name属性值是“.CameraActivity”,CameraActivity前面的点(.)是可选的,所以完整的类名是mobile.android.camera.CameraActivity。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mobile.android.camera"……>
……
<application……>
<activityandroid:name=".CameraActivity">
……
</activity>
……
</application>
</manifest>
指定窗口类名称的方法2:android:name属性值包含一部分包名
假设CameraActivity类的全名是mobile.android.camera.test.CameraActivity,而<manifest>标签的package属性值是mobile.android.camera,而android:name属性值需要设为“.test.CameraActivity”。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mobile.android.camera"……>
……
<application……>
<activity android:name=".test.CameraActivity">
……
</activity>
……
</application>
</manifest>
指定窗口类名称的方法3:使用完整的类名
不管窗口类是否属于package属性指定的包,都可以直接用完整的类名。例如,android:name属性值可以直接设为mobile.android.camera.test.CameraActivity。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mobile.android.camera"……>
……
<application……>
<activityandroid:name="mobile.android.camera.test.CameraActivity">
……
</activity>
……
</application>
</manifest>
注意
AndroidManifest.xml文件中凡是需要设置类名的地方都可以使用这3种方法进行设置。例如后面的章节介绍的<receiver>、<provider>标签的android:name属性值与<activity>标签的android:name属性值的设置方法相同。
android:label和android:icon属性分别用于设置窗口标题的文本和图像。如果当前窗口是主窗口,这两个属性也分别表示应用程序列表中的图像和下方的文本。下面是一段使用这两个属性的例子。
<application>
<activity
android:name="mobile.android.android.test.AndroidTestActivity"
android:icon="@drawable/icon"
android:label="测试Activity" >
<!-- 通过下面的代码将当前窗口设置成主窗口 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
如果AndroidManifest.xml文件中包含了上面的代码,运行程序后,就会显示如图7-1所示的标题和图像。程序列表中的图像和下方的文本如图7-2白框中所示。
▲图7-1 程序的标题和图像
<application>标签也同样有android:label和android:icon属性。如果未设置<activity>标签的这两个属性,系统会使用<application>标签的同名属性的值代替。如果<application>和<activity>标签都未设置这两个属性,系统会使用默认值设置图像和文本。
▲图7-2 程序列表中的图像和下方的文本
7.1.2 屏幕方向切换(android:screenOrientation)
源代码目录:src/ch07/ScreenOrientation
android:screenOrientation属性值为枚举类型,默认值是unspecified。
默认情况下,Activity会利用Android手机的传感器实现横竖屏的切换,也就是当手机在不同方向旋转后,系统会根据当前方向重新将窗口设为横屏或竖屏。
对于一个完全支持横竖屏切换的系统来说,可以支持4个屏幕方向。也就是说从手机的正常方向开始,按逆时针或顺时针每旋转90度是一个方向(0度、90度、180度和270度),直到旋转360度后回到正常使用手机的方向。但对于标准的Android系统来说,默认只支持3个屏幕旋转方向(180度的情况不支持)。为了使Activity在屏幕旋转方向上适合不同的场合,<activity>标签提供了android:screenOrientation属性用于满足不同的屏幕旋转要求。其中包括使屏幕同时支持4个屏幕旋转方向;只支持横屏或竖屏等。这些屏幕旋转的设置不仅可以在声明窗口时指定,还可以通过Java代码动态改变这些设置。ActivityInfo类提供了一些常量用来与android:screenOrientation属性值一一对应。表7-1给出了android:screenOrientation属性支持的所有的属性值以及与这些属性值对应的常量,并给出这些值的详细描述。
表7-1 Activity支持的屏幕旋转方向设置
【注】③Android 通过回退堆栈来管理Activity,这部分内容会在5.4 节详细介绍。
续表
如果要在声明窗口时指定屏幕旋转方向,需要使用<activity>标签的android:screenOrientation属性,如果用Java代码设置窗口的屏幕旋转方向,需要调用Activity. setRequestedOrientation方法,该方法只有一个参数,参数值就是表7-1中给出的在ActivityInfo类中定义的常量。下面的例子演示了如何通过android:screenOrientation属性和Java代码设置Activity的屏幕旋转方向。本例首先将android:screenOrientation属性值设为nosensor,表示Activity不会随着屏幕的方向而旋转,代码如下:
源代码文件:src/ch07/ScreenOrientation/AndroidManifest.xml
<activity
android:name=".ScreenOrientationActivity"
android:label="@string/title_activity_screen_orientation"
android:screenOrientation="nosensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
上面代码声明的窗口显示效果如图7-3所示。
▲图7-3 设置窗口的旋转方向
图7-3所示3个按钮的单击事件的代码如下:
源代码文件:src/ch07/ScreenOrientation/src/mobile/android/ screenorientation/ScreenOrientation Activity.java
public class ScreenOrientationActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_orientation);
}
// "4个屏幕旋转方向"按钮的单击方法
public void onClick_FourScreenOrientation(View view)
{
// 设置窗口支持4个屏幕旋转方向,相当于fullSensor
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
}
// "横屏"按钮的单击方法
public void onClick_LandscapeOrientation(View view)
{
// 设置窗口只能横屏,相当于landscape
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
// "竖屏"按钮的单击方法
public void onClick_PortraitOrientation(View view)
{
// 设置窗口只能竖屏,相当于portrait
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
现在运行程序,会发现程序的窗口无法随着Android设备的来回转动而旋转。单击“4个屏幕旋转方向”按钮,窗口就会随着Android设备360°地转动以4个方向进行旋转。单击“横屏”按钮,会发现Android设备无论怎么旋转,窗口始终保持横向的状态。单击“竖屏”按钮,无论怎样旋转Android设备,窗口都会保持纵向的状态。
7.1.3 阻止Activity销毁和重建(android:configChanges)
源代码目录:src/ch07/ConfigChanges
android:configChanges属性值为枚举类型,无默认值。
默认情况下,配置变化后,Activity会自己通过销毁和重新创建窗口的方式处理这些变化。例如,最常见的配置变化是屏幕的旋转方向和尺寸改变,这一过程将导致系统调用Activity.onDestroy方法销毁当前的窗口对象,并重新创建这个窗口对象(调用Activity.onCreate方法),在窗口的销毁和重新创建的过程中,窗口类中的变量将全部恢复到初始值。不过这种默认的处理方法很容易通过<activity>标签的android:configChanges属性改变。如果将该属性值设为某一项或某几项配置,那么当这些配置发生变化后,系统将不再执行默认的动作(如销毁窗口对象),而是允许开发人员在Activity. onConfigurationChanged方法中处理配置的变化。onConfigurationChanged方法的原型如下:
public void onConfigurationChanged(Configuration newConfig)
onConfigurationChanged方法只有一个newConfig参数,从该参数中可以获取配置变化后的值,例如,屏幕旋转后是横屏,还是竖屏。屏幕尺寸变化后的屏幕宽度和高度。
android:configChanges属性可以设置一个或多个配置,这些配置都用字符串描述,如果设置多个配置,中间用“ ”分割,“ ”与配置字符串之间不能有空格、Tab等字符。下面的代码是标准的设置android:configChanges属性的代码。
android:configChanges="orientation screenSize"
android:configChanges属性支持的配置如表7-2所示。
表7-2 android:configChanges属性支持的配置
本节的例子包含了一个窗口类,并且将android:configChanges属性值设为“orientation screenSize”,声明窗口类的代码如下:
代源码文件:src/ch07/ConfigChanges/AndroidManifest.xml
<activity
android:name=".ConfigChangesActivity"
android:configChanges="orientation screenSize"
android:label="@string/title_activity_config_changes" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
该窗口只有一个按钮,如图7-4所示。单击该按钮会设置一个类成员变量的值。
▲图7-4 主窗口
窗口类(ConfigChangesActivity)的代码如下:
源代码文件:src/ch07/ConfigChanges/src/mobile/android/config/changes/ConfigChangesActivity.java
public class ConfigChangesActivity extends Activity
{
// 要设置的类变量
private String name = "default";
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_config_changes);
// 输出日志信息
Log.d("invoked method", "onCreate");
}
@Override
protected void onDestroy()
{
// 输出日志信息
Log.d("invoked method", "onDestroy");
super.onDestroy();
}
// “设置变量值”按钮单击事件方法
public void onClick_SetVar(View view)
{
// 设置变量值
name = "android";
}
// 当配置变化时调用该方法(只有通过android:configChanges属性设置的配置才会调用该方法)
@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
// 输出配置变化后的屏幕方向
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
Log.d("orientation", "landscape");
else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
Log.d("orientation", "portrait");
// 输出配置变化后的屏幕宽度(单位是dp)
Log.d("screen_width_dp", String.valueOf(newConfig.screenWidthDp));
// 输出配置变化后的屏幕高度(单位是dp)
Log.d("screen_height_dp", String.valueOf(newConfig.screenHeightDp));
// 输出name变量的值
Log.d("name", name);
}
}
现在运行程序,单击“设置变量值”按钮,这时name变量的值已经变成了“android”。接下来旋转手机或Android模拟器屏幕,使当前窗口旋转。这时LogCat视图中会输出如图7-5所示的日志信息。
▲图7-5 配置改变时输出的日志信息
从图7-5所示的日志信息可以看出,除了窗口创建时调用了一次onCreate方法外,当窗口的旋转方向改变时并没有调用onDestroy和onCreate方法,这也就意味着当前窗口对象并没有销毁和重新创建。而后面的日志信息是在onConfigurationChanged方法中输出的,这也充分说明了在屏幕旋转方向变化后仍然使用原来的窗口对象,并调用了该窗口对象的onConfigurationChanged方法。由于只在程序一开始运行时按了一下“设置变量值”按钮,在屏幕旋转后并没有再次设置name变量,而从name变量的输出值(android)来看,name变量并未恢复到初始值,这也充分说明了窗口对象并未销毁,属于该窗口对象的变量自然也会保留最后设置的值了。
本例在AndroidManifest.xml文件中已经使用<uses-sdk>标签的android:targetSdkVersion属性将要求的最小API Level设为16(Android4.1),具体代码如下。
源代码文件:src/ch07/ConfigChanges/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mobile.android.config.changes"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="16" />
<application…>
……
</application>
</manifest>
因此将android:configChanges属性值中的“screenSize”去掉,系统仍然会采用默认处理配置变化的方法,也就是销毁(调用onDestroy方法)和重新创建(调用onCreate方法)窗口对象。但如果读者将android:targetSdkVersion属性值改为13或更小的值,会发现将android:configChanges属性值中的“screenSize”去掉,仍然会调用onConfigurationChanged方法来处理配置的变化。
注意
屏幕旋转方向的配置变化只能检测出横屏和竖屏,并不能详细区分是正向横屏(landscape)、正向竖屏(portrait)、反向横屏(reverseLandscape)和反向竖屏(reversePortrait)。所以landscape和reverseLandscape获得的屏幕旋转方向都是Configuration.ORIENTATION_LANDSCAPE,portrait和reversePortrait获得的屏幕旋转方向都是Configuration.ORIENTATION_PORTRAIT。
答疑解惑:为什么LogCat视图未输出正确的信息
如果读者按着本节的操作步骤去完成和测试本例,一定会在LogCat视图中输出与图7-5类似的信息。如果没有得到这些信息,原因只有一个,就是读者的机器上连接了多个Android设备(包括真机和Android模拟器),而LogCat同时只能显示当前选中的Android设备输出的日志。解决方法是打开DDMS透视图,找到Devices视图,如图7-6所示。选中运行程序的Android设备即可。
▲图7-6 Devices 视图
多学一招:更方便地输入XML属性值
Android应用程序继承了Java的优良传统,包含了大量的配置文件,例如,AndroidManifest.xml、布局文件等。这些配置文件都是基于XML格式的,而且有很多XML属性值是枚举类型的,例如,<activity>标签的android:configChanges属性就是其中之一,该属性可设置的值如表7-2所示。尽管这些属性值可以通过官方文档查阅(估计没人能记住每一个XML属性的所有属性值,所以必须得查阅相关文档),但每次都要查阅文档比较麻烦,所以一种比较简单的方法就是利用Eclipse的“Content Assist”快捷键快速查看属性值,如果读者不知道自己的“Content Assist”快捷键是什么,可以单击“Help”>“Key Assist”菜单项查看(寻找“Content Assist”项即可)。通常Eclipse的“Content Assist”快捷键是Alt+“/”,其中Alt键相当于苹果电脑的option键。例如,将鼠标定位到android:configChanges属性的两个双引号中间,然后按下“Content Assist”快捷键,就会显示该属性支持的所有属性值,如图7-7所示。最后选中某个属性,按回车键即可录入选中的属性值。另外,输入“android”后,当再输入“:”后,就会列出android命名空间包含的属性列表。关于android命名空间的细节会在后面的章节详细介绍。
▲图7-7 显示XML属性支持的属性值
除此之外,还可以通过选择列表来输入属性值。例如,输入android:configChanges属性值可以双击AndroidManifest.xml文件,并单击右侧的“Application”页,并在下方的“Application Nodes”区域中选中要设置的窗口类,在右侧会出现该窗口类可以设置的属性(就是<activity>标签的所有属性),如图7-8所示。
如果某一个XML属性值是枚举类型的,就会出现相应的选项列表(单选)或选择对话框(多选),例如,图7-9就是android:configChanges属性值的选择框,读者可以选择一个或多个属性值,也可以不选择任何属性值(这种情况将删除android:configChanges属性)。
▲图7-8 <activity>标签的属性
▲图7-9 选择android:configChanges属性的值
7.1.4 允许Activity被实例化(android:enabled)
android:enabled属性值为布尔类型,默认值是true。
如果要实例化Android应用中的某个窗口,该窗口对应的<activity>标签的android:enabled属性值必须为true。该属性的默认值是true。如果该属性值为false,窗口类将不能被实例化。如果将主窗口(程序启动第一个显示的窗口)的android:enabled属性值设为false,尽管运行程序不会出错,但由于无法成功创建主窗口类的对象,所以运行程序后不会有任何反应。如果普通窗口类的android:enabled属性值为false,调用startActivity方法显示该窗口时会直接抛出异常。
7.1.5 在最近应用列表中显示(android:excludeFromRecents)
android:excludeFromRecents属性值为布尔类型,默认值是false。
当长按手机的“Home”键时会显示如图7-10所示的最近应用列表。在该列表中显示了最近运行的Android应用程序,如果触摸某一个列表项,就会重新运行该程序。
▲图7-10 最近应用列表
如果不想让自己的Android应用程序在该列表中显示,只需要将主窗口的<activity>标签的android:excludeFromRecents属性设为true即可。该属性的默认值为false。
7.1.6 允许其他程序访问当前窗口(android:exported)
android:exported属性值为布尔类型,默认值是true。
前面介绍的显式窗口(Activity)的方法一直是直接指定窗口类,这属于显式访问窗口类,也就是明显地指定具体的窗口类。Android还支持一种访问窗口类的方式:隐式访问。简单地解释隐式访问的原理就是将一个字符串与一个或多个窗口绑定,然后窗口类的访问者并不需要直接指定窗口类,而只需要通过这个字符串来访问该窗口类。如果有多个窗口类与同一个字符串绑定,系统会显示一个选择菜单,由用户决定访问哪个窗口。
与窗口类的绑定的字符串由<action>标签指定,通常还需要指定一个Category。下面的代码就是在声明窗口类时为该窗口类绑定了一个字符串,并指定了一个Category。
<activity android:name=".MyActivity">
<intent-filter>
<!-- android:name属性的值就是绑定的字符串(可以是任意字符串) -->
<action android:name="mobile.android.MYACTION" />
<!-- 指定的Category是android.intent.category.DEFAULT -->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
假设有一个A.apk文件,该文件中包含了上面代码声明的窗口类。现在有一个B.apk,在B.apk中可以使用下面的代码显示MyActivity。
Intent intent = new Intent("mobile.android.MYACTION");
startActivity(intent);
上面的代码在A.apk中也同样可以显示MyActivity,但如果在声明MyActivity类时将android:exported属性值设为false,则在B.apk文件中就无法使用上面的代码显示MyActivity了(会抛出异常),但在A.apk中仍然可以使用上面的代码显示MyActivity。也就是说,android:exported属性控制了其他程序是否可以通过隐式调用的方式访问本程序内部的窗口类,但并不限制本程序内部窗口之间通过隐式调用的方式互相访问。android:exported属性的默认值是true。
注意
就算android:exported属性值为true,也必须至少指定一个Action才能通过隐式方式访问窗口类。
7.1.7 硬件加速(android:hardwareAccelerated)
android:hardwareAccelerated属性值为布尔类型,默认值是false。
从Android 3.0开始,支持基于OpenGL的硬件加速技术。如果将android:hardwareAccelerated属性设为true(该属性的默认值为false),表示当前窗口在Canvas、Paint、Xfermode、ColorFilter、Shader和Camera上的大多数操作将被加速。加速的结果就是使动画和滚动更平滑,并且拥有更快的响应速度。要注意的是,这里的OpenGL硬件加速并不需要显式地调用OpenGL API,系统会自动进行处理。
尽管硬件加速可以换来更好的用户体验,但付出的代价就是要占用更多的内存和CPU计算时间,因此,在任何情况下使用硬件加速并不是一个好主意。
注意
并不是所有的2D操作都被加速,而且由于硬件加速会占用大量的内存,因此尽量在配置比较低的Android设备上进行测试,以确保应用程序不会由于过多占用内存而崩溃。
7.1.8 在多进程中创建窗口实例(android:multiprocess)
android:multiprocess属性值为布尔类型,默认值是false。
在默认情况下,窗口类的所有实例都运行在窗口类所在的应用程序的进程中,也就是说窗口类的所有实例只能运行在一个进程中,该进程通常是包含窗口类的应用程序的进程。但如果将android:multiprocess属性的值设为true,则窗口类的实例可以运行在多个进程中。
7.1.9 无法返回的Activity(android:noHistory)
android:noHistory属性值为布尔类型,默认值是false。
最小Android版本要求:API Level=3。
从这个属性名就可以猜出该属性的大概功能。No History,不保存历史。这里的历史指的是显示窗口留下的轨迹。在默认情况下,一个窗口被显示,都会将该窗口类的实例放入回退栈的栈顶,只有在栈顶的窗口才能显示并获得焦点。当显示了另外一个窗口(可以是另一个程序的窗口,如接听电话时的窗口),或按“Home”键切换到桌面后。再恢复到原来的程序后,这个在栈顶的窗口又会重新显示。
不过将android:noHistory属性值设为true时,一切都将改变。这时系统不再保留窗口显示的历史,也就是说尽管也会显示android:noHistory属性值为true的窗口,但并不会将该窗口对应的窗口类对象放入回退栈。这样就会带来一个问题,当窗口停止(执行Activity.onStop)后,就不会恢复到原来的窗口。例如,一个Android应用中有两个窗口:ActivityA和ActivityB。ActivityA是主窗口,通过单击ActivityA中的一个按钮来显示ActivityB。如果声明ActivityB时<activity>标签的android:noHistory属性值设为false,或未设置android:noHistory属性(默认值是false)。如果ActivityB已经显示,那么这时接听来电,然后再返回ActivityB所在的应用,仍然会显示ActivityB。但如果将ActivityB的android:noHistory属性值设为true,关闭接听来电窗口后,将不再回到ActivityB,而是直接回到了ActivityA。发生这种情况的原因是如果android:noHistory属性值为true,当前显示的窗口将不会被添加到回退栈中,而只有在回退栈顶的窗口才能重新显示。
7.1.10 指定要返回的窗口(android:parentActivityName)
android:parentActivityName属性值为字符串类型,无默认值。
最小Android版本要求:API Level=16。
android:parentActivityName属性主要用于Action Bar。当按下Action Bar的回退按钮后,系统会回退到android:parentActivityName属性值指定的窗口。如果android:parentActivityName属性指定的窗口在回退栈中不存在,系统还可以使用TaskStackBuilder和android:parentActivityName属性值。
7.1.11 使窗口受到权限的保护(android:permission)
android:permission属性值为枚举类型,无默认值。该属性值可输入的内容为Android支持的权限。
在默认情况下,只要为<activity>标签指定了一个Action,就可以在其他程序中不受限制地使用隐式方式显示<activity>标签声明的窗口。不过我们可以用android:permission属性为这些窗口添加一些权限。Android系统预定义了一些权限,可以将光标位置移到android:permission属性值的两个双引号之间,按Content Assist快捷键(通常是Alt+“/”),就会显示如图7-11所示的权限列表。android:permission属性只能设置一个权限,中间不能使用“ ”分割设置多个权限。
假设有一个A.apk,其中包含一个MyActivity,声明的代码如下:
<activity
android:name=".MyActivity"
android:permission="android.permission.ACCESS_WIFI_STATE">
<intent-filter>
<action android:name="mobile.android.MYACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
上面的代码为MyActivity指定了一个Action,并且将android:permission属性值设为android.permission.ACCESS_WIFI_STATE。
▲图7-11 Android支持的权限
如果在B.apk中要显示MyActivity,除了要执行下面的代码外,还要在AndroidManifest.xml文件中通过<uses-permission>标签指定该权限。
执行下面的Java代码显示MyActivity:
Intent intent = new Intent("mobile.android.MYACTION");
startActivity(intent);
使用<uses-permission>标签指定权限。
<manifest ……>
……
<!-- 指定窗口的权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application……>
……
</application>
</manifest>
<application>与<activity>标签一样,也有一个android:permission属性。如果<activity>标签未指定android:permission属性,但<application>标签指定了android:permission属性,那么<application>标签中声明的所有窗口都会应用该权限。如果<application>与<activity>标签都指定了android:permission属性,以<activity>标签的android:permission属性值为准。
注意
如果在包含MyActivity的程序中(A.apk)用隐式方法显示MyActivity就不需要使用<uses-permission>标签设置权限了。
7.1.12 改变窗口所在的进程(android:process)
源代码目录:src/ch07/Process
android:process属性值为字符串类型,无默认值。
默认情况下,一个Android应用程序(apk文件)只建立一个进程(进程名为当前应用程序的包名),该程序中的所有组件(Acitivty、Service等)都运行在这个进程中。但为了满足某些特殊需要,Android系统允许为程序中的某个组件单独建立一个进程(为了使组件之间保持相对的独立性),也允许不同应用程序中的多个组件共享一个进程(为了节省内存空间和共享数据)。这些功能需要通过<activity>标签的android:process属性来完成。
android:process属性的值有如下两种类型的值。
以冒号(:)开头,如android:process= " :new_process"。系统会为当前组件在当前应用程序中创建一个私有的进程。
以小写字母开头,如android:process="new_process"。系统会使当前组件运行在android:process属性指定的全局进程中,但要注意,这个全局进程必须存在,否则程序无法安装在Android设备上。
如果未设置<activity>标签的android:process属性,也可以用<application>标签的android:process属性为所有的组件指定同一个进程。
下面来看一个例子,该例子中包含了3个窗口类(ProcessActivity、ActivityA和ActivityB)。其中ProcessActivity是主窗口,声明ActivityA时使用了android:process属性指定了一个新的进程(进程名前加冒号),声明ActivityB时未设置android:process属性。在每一个窗口类的onCreate方法中都输出了当前窗口所在的进程ID和系统当前运行的所有进程名和ID。
现在先来运行程序,会看到如图7-12所示的输出信息。
▲图7-12 窗口所在的进程ID和所有运行的进程信息
在图7-12所示的输出信息中很容易找到当前窗口的进程ID和进程名(黑框中的内容)。在该界面上方还有两个按钮,分别用来显示ActivityA和ActivityB。ProcessActivity类的实现代码如下:
源代码文件:src/ch07/Process/src/mobile/android/process/ProcessActivity.java
public class ProcessActivity extends Activity
{
// 获取系统当前运行的所有进程的名称和ID,该方法声明成static,
// 是为了另外两个窗口类也可以调用该方法
public static String getAllProcess(Context context)
{
StringBuilder sb = new StringBuilder();
// 获取系统服务(ActivityManager对象)
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
// 扫描所有已运行进程的RunningAppProcessInfo对象
for (RunningAppProcessInfo rapi : am.getRunningApp Processes())
{
// 将当前进程的名称和ID添加到StringBuilder对象中
sb.append(rapi.processName + "(" + rapi.pid + ")\n");
}
return sb.toString();
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_process);
// 创建TextView对象(用于显示文本信息)
TextView textView = (TextView) findViewById(R.id.textview);
// 获取当前窗口所在的进程ID
String processId = String.valueOf(android.os.Process.myPid());
// 下面两行代码将与进程相关的信息显示在TextView控件中
textView.setText("当前窗口所在进程的ID:" + processId + "\n");
textView.append("系统中运行的所有进程名称\n" + getAllProcess(this));
}
// “创建新进程”按钮的单击事件方法
public void onClick_CreateNewProcess(View view)
{
Intent intent = new Intent(this, ActivityA.class);
// 显示ActivityA(创建一个新进程)
startActivity(intent);
}
// “使用当前应用程序的进程”按钮的单击事件方法
public void onClick_UseOldProcess(View view)
{
Intent intent = new Intent(this, ActivityB.class);
// 显示ActivityB,仍然使用当前应用程序的默认进程(进程名称为应用程序的包名)
startActivity(intent);
}
}
现在单击“使用当前应用程序的进程”按钮,仍然会显示与图7-12类似的信息(进程ID仍然是1532)。现在看看ActivityA的声明代码。
源代码文件:src/ch07/Process/AndroidManifest.xml
<activity
android:name=".ActivityA"
android:label="@string/title_activity_activity" android:process=":new_process" />
由于在声明ActivityA时设置了android:process属性,并且该属性值为“:new_process”,所以在显示ActivityA时会在当前应用程序中创建一个私有进程,进程名称格式:应用程序包名+android:process属性值。现在单击“创建新进程”按钮,会看到输出了如图7-13所示的信息。很明显,ActivityA所在的进程ID与ProcessActivity所在的进程ID不同,而且又多了一个私有进程(在第一个黑框内)。
▲图7-13 列出新创建的私有进程
如果想让当前窗口运行在全局的进程中,如com.android.calendar,可以使用如下代码声明窗口。但要注意,当前窗口必须要有权限在指定的进程中运行。
<activity
android:name=".ActivityA"
android:label="@string/title_activity_activity" android:process="com.android.calendar" />
7.1.13 不保存窗口的状态(android:stateNotNeeded)
android:stateNotNeeded属性值为布尔类型,默认值是false。
如果android:stateNotNeeded属性值为true,窗口不管在任何情况下都不会保存状态,在窗口重新显示后,也不会恢复到原来的状态。如果该属性为false,系统会在适当的时候保存窗口的状态,该属性的默认值是false。
通常情况下,窗口在临时关闭后(停止状态),会调用Activity.onSaveInstanceState方法。该方法将窗口的状态存储在了Bundle对象中。当窗口重新显示时会调用onCreate方法,这个保存了窗口原来状态的Bundle对象会通过onCreate方法的参数传入窗口对象。如果将android:stateNotNeeded属性设为true,Activity.onSaveInstanceState方法将永远不会被系统调用,而这时传入onCreate方法的参数值就是null。本章后面会专门用一节来详细介绍如何保存和恢复窗口的状态。
7.1.14 窗口的主题(android:theme)
android:theme属性值为字符串类型,无默认值。
该属性用于引用风格(Style)资源。该资源可用于定义窗口的各种风格,例如,可以将窗口变成对话框风格;设置窗口的开始动画等。除了可以使用android:theme属性设置窗口主题(Style资源)外,还可以使用Activity.setTheme方法设置动态设置窗口主题。
如果未设置该属性,窗口会使用<application>标签的android:theme属性指定的主题。如果<application>标签也没有设置该属性,那么系统会使用默认的主题风格,也就是我们建立的默认窗口风格。本章后面会专门用一节来介绍如何将主题应用在窗口上,在后面的资源详解一章会详细探讨Style资源的建立和使用。
7.1.15 扩展窗口UI(android:uiOptions)
android:uiOptions属性值为枚举类型,默认值是none。
最小Android版本要求:API Level=14。
android:uiOptions属性只能设置表7-3所示的值。
表7-3 android:uiOptions属性可以设置的值
7.1.16 设置输入法显示模式(android:windowSoftInputMode)
android:windowSoftInputMode属性值为枚举类型,默认值是stateUnspecified adjustUnspecified。
最小Android版本要求:API Level=3。
该属性用于设置当前窗口如何与输入法软键盘进行交互。设置该属性会对软键盘和窗口有如下影响。
软键盘的状态。也就是说当窗口处于焦点状态时软键盘是隐藏还是显示。
对当前窗口进行调整。也就是说当软键盘窗口显示后,当前窗口的大小是否做出调整,以及窗口上当前获得焦点的控件是否仍然处于可视状态(控件整体会上移)。
android:windowSoftInputMode属性必须设置表7-4所示的一个或多个值,如果设置多个值,中间用“ ”连接,该符号两侧不能有空格、Tab等字符。
表7-4 android:windowSoftInputMode属性可以设置的值