建立、配置和使用Activity——Activity
Activity是Android应用中最重要、最常见的应用组件(此处的组件是粗粒度的系统组成部分,并非指界面控件:widget)。Android应用的一个重要组成部分就是开发Activity,下 面将会详细介绍Activity开发、配置的相关知识。
Activity
与开发Web应用时建立Servlet类相似,建立自己的Activity也需要继承Activity基类,当然,在不同应用场景下,有时也要求继承Activity的子类。例如如果应用程序界面只包括列表,则可以让应用程序继承ListActivity;如果应用程序界面需要实现标签页效果,则可以让应用程序继承TabActivity。
图4.1显示了Android提供的Activity类。
如图4.1所示,Activity类间接或直接地继承了Context、ContextWrapper、ContextThemeWrapper等基类,因此Activity可以直接调用它们的方法。
与Servlet类似,当一个Activity定义出来之后,这个Activity类何时被实例化、它所包含的方法何时被调用,这些都不是由开发者决定的,都应该由安卓系统来决定。
为了让Servlet能响应用户请求,开发者需要重写HttpServlet的doRequest(...)、doResponse(...)方法,或重写service(...)方法。Activity与此类相似,创建一个Activity也需要实现一个或多个方法,其中最常见的就是实现onCreate(Bundle status)方法,该方法将会在Activity创建时被回调,该方法调用Activity的setContentView(View view)方法来显示要展示的View。为了管理应用程序中的各种组件,调用Activity的findViewById(int id)方法来获取程序界面中的组价,接下来去修改格组件的属性和方法即可。
实例:用LauncherActivity开发启动Activity的列表。
通过前几章的实例介绍了Activity、ListActiviy、TabActivity等基类的用法,接下来开发一个继承LauncherActivity的应用。
LauncherActivity继承了ListActivty,因此它本质上也是一个开发列表界面的Activity,但它开发出来的列表界面与普通列表界面有所不同。它开发出来的列表界面中的每个列表项都对应一个Intent,因此当用户单击不同的列表项时,应用程序会自动启动对应的Activity。
使用LauncherActivity的方法并不难,由于依然是一个ListActivity,因此同样需要为它设置Adapter——既可使用简单的ArrayAdapter,也可使用SimpleAdapter,当然也可以扩展BaseAdapter来实现自己的Adapter。与使用普通ListActivity不同的是,继承LauncherActivity时通常应该重写Intent intentForPosition(int position)方法,该方法根据不同列表项返回不同的Intent(用于启动不同的Activity)。
下面的程序中是一个LauncherActivity的子类。
package com.example.studyactivity; import android.os.Bundle; import android.app.Activity; import android.app.LauncherActivity; import android.content.Intent; import android.view.Menu; import android.widget.ArrayAdapter; public class OtherActivity extends LauncherActivity { //定义两个Activity的名称 String[] names={"设置程序参数","查看星际兵种"}; //定义两个Activity对应的实现类 Class<?>[] clazzs={PreferenceActivityTest.class,ExpandableListActivityTest.class}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ArrayAdapter<String> adapter=new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,names); //设置该窗口显示列表所需的Adapter setListAdapter(adapter); } @Override protected Intent intentForPosition(int position) { // TODO Auto-generated method stub return new Intent(OtherActivity.this,clazzs[position]); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.other, menu); return true; } }
上面的程序中第一行粗体字代码为该ListActivity设置了所需的内容Adapter,第二段粗体字代码则根据用户单击的列表项去启动对一个的Activity。
上面的程序还用到了如下两个Activity。
- ExpandableListActivityTest:它是ExpandableListActivityTest的子类,用于显示一个可展开的列表窗口。
- PreferenceActiviyTest:它是PreferenceActivity的子类,用于显示一个显示设置选项参数并进行保存的窗口。
实例:使用ExpandableListActivity实现可展开的Activity
ExpandableListActivityTest,它继承了ExpandableListActivty的基类,ExpandableListActivity的用法与前面介绍的ExpandableListView的用法基本相似,只要为该Activity传入一个ExpandableListAdapter对象即可,接下来ExpandableListActivity将会生成一个显示可展开列表的窗口。
下面是ExpandableListActivityTest的代码。
package com.example.studyactivity; import android.os.Bundle; import android.app.Activity; import android.app.ExpandableListActivity; import android.database.DataSetObserver; import android.view.Gravity; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.ExpandableListAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; public class ExpandableListActivityTest extends ExpandableListActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ExpandableListAdapter adapter=new ExpandableListAdapter(){ int[] logos=new int[]{R.drawable.p,R.drawable.z,R.drawable.t}; private String[] armTypes=new String[]{"神族兵种","虫族兵种","人族兵种"}; private String[][] arms=new String[][]{ {"狂战士","龙骑士","黑暗圣堂","电兵"}, {"小狗","刺蛇","飞龙","自爆飞机"}, {"机枪手","护士MM","幽灵"} }; @Override public boolean areAllItemsEnabled() { // TODO Auto-generated method stub return false; } @Override public Object getChild(int groupPosition, int childPosition) { // TODO Auto-generated method stub return arms[groupPosition][childPosition]; } @Override public long getChildId(int groupPosition, int childPosition) { // TODO Auto-generated method stub return childPosition; } //该方法决定每个子选项的外观 @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { // TODO Auto-generated method stub TextView textView=getTextView(); textView.setText(getChild(groupPosition,childPosition).toString()); return textView; } @Override public int getChildrenCount(int groupPosition) { // TODO Auto-generated method stub return arms[groupPosition].length; } private TextView getTextView() { AbsListView.LayoutParams lp=new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,64); TextView textView=new TextView(ExpandableListActivityTest.this); textView.setLayoutParams(lp); textView.setGravity(Gravity.CENTER_VERTICAL|Gravity.LEFT); textView.setPadding(36, 0, 0, 9); textView.setTextSize(20); return textView; } @Override public long getCombinedChildId(long arg0, long arg1) { // TODO Auto-generated method stub return 0; } @Override public long getCombinedGroupId(long groupId) { // TODO Auto-generated method stub return 0; } @Override public Object getGroup(int groupPosition) { // TODO Auto-generated method stub return armTypes[groupPosition]; } @Override public int getGroupCount() { // TODO Auto-generated method stub return armTypes.length; } @Override public long getGroupId(int groupPosition) { // TODO Auto-generated method stub return groupPosition; } //该选项决定每个组选项的外观 @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { // TODO Auto-generated method stub LinearLayout ll=new LinearLayout(ExpandableListActivityTest.this); ll.setOrientation(0); ImageView logo=new ImageView(ExpandableListActivityTest.this); logo.setImageResource(logos[groupPosition]); ll.addView(logo); TextView textView=getTextView(); textView.setText(getGroup(groupPosition).toString()); ll.addView(textView); return ll; } @Override public boolean hasStableIds() { // TODO Auto-generated method stub return true; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { // TODO Auto-generated method stub return true; } @Override public boolean isEmpty() { // TODO Auto-generated method stub return false; } @Override public void onGroupCollapsed(int groupPosition) { // TODO Auto-generated method stub } @Override public void onGroupExpanded(int groupPosition) { // TODO Auto-generated method stub } @Override public void registerDataSetObserver(DataSetObserver observer) { // TODO Auto-generated method stub } @Override public void unregisterDataSetObserver(DataSetObserver observer) { // TODO Auto-generated method stub }}; //设置该窗口的显示列表 setListAdapter(adapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.expandable_list_activity_test, menu); return true; } }
上面的程序中粗体字代码为ExpandableListActivity设置了一个ExpandableListAdpter对象,即可使得该Activity实现可展开列表的窗口。
实例:PreferenceActivity结合PreferenceFragment实现参数设置界面
PreferenceActivity是一个非常有用的基类,当我们开发一个Android应用程序时,不可避免地需要进行选项设置,这些选项设置会以参数的形式保存,习惯上我们会用Preferences进行保存。
需要指出的是,如果Android应用程序中包含的某个Activity专门用于设置选项参数,那么Android为这种Activity提供了便捷的基类:PreferenceActivity。
一旦Activity继承了PreferenceActivity,那么该Activity完全不需自己控制Preferences的读写,PreferenceActivity会为我们处理一切。
PreferenceActivity与普通Activity不同,它不再使用普通的界面布局文件,而是使用选型设置的布局文件。选项设置的布局文件以PreferenceScreen作为根元素——它表明定义一个参数设置的界面布局。
为了创建一个PreferenceActivity,需要先创建一个对应的界面布局文件。从Android3.0开始,Android不再推荐直接让PreferenceActivity加载选项设置的布局文件。而建议将PreferenceActivity与FreferenceFragment结合使用,其中PreferenceActivity只负责加载选项设置列表的布局文件,PreferenceFragment才负责加载选项设置的布局文件。
本实例中PreferenceActivity加载的选项设置列表布局文件如下:
<?xml version="1.0" encoding="utf-8"?> <preference-headers xmlns:android="http://schemas.android.com/apk/res/android" > <!-- 指定启动指定PreferenceFragment 的列表项 --> <header android:fragment="com.example.studyactivity.PreferenceActivityTest$Prefs1Fragment" android:icon="@drawable/ic_settings_applications" android:title="程序选项设置" android:summary="设置应用的相关选项" /> <!-- 指定启动指定PreferenceFragment的列表项 --> <header android:fragment="com.example.studyactivity.PreferenceActivityTest$Prefs2Fragment" android:icon="@drawable/ic_settings_display" android:title="界面选项设置" android:summary="设置显示界面的相关选项"> <!-- 使用extra可向Activity传入额外的数据 --> <extra android:name="website" android:value="www.crazyit.org"/> </header> <!-- 使用Intent启动指定Activity的列表项 --> <header android:icon="@drawable/ic_settings_display" android:title="使用Intent" android:summary="使用Intent启动某个Activity"> <intent android:action="android.intent.action.VIEW" android:data="http://www.crazyit.org"/> </header> </preference-headers>
上面的布局文件中指定使用Prefs1Fragment、Prefs2Fragment两个内部类,为此我们将会在PreferenceActivityTest类中定义这两个内部类。下面是PreferenceActivityTest的代码。
package com.example.studyactivity; import java.util.List; import android.os.Bundle; import android.preference.PreferenceActivity; import android.preference.PreferenceActivity.Header; import android.preference.PreferenceFragment; import android.app.Activity; import android.view.Menu; import android.widget.Button; import android.widget.Toast; public class PreferenceActivityTest extends PreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //该方法用于为该界面设置一个标题按钮 if(hasHeaders()) { Button button=new Button(this); button.setText("设置操作"); //将该按钮添加到该界面上 setListFooter(button); } } //重写该方法负责加载页面布局文件 @Override public void onBuildHeaders(List<Header> target) { // TODO Auto-generated method stub //加载选项设置列表的布局文件 loadHeadersFromResource(R.xml.preference_headers,target); } public static class Prefs1Fragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); } } public static class Prefs2Fragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.display_prefs); //获取传入该Fragment的参数 String website=getArguments().getString("website"); Toast.makeText(getActivity(), "网站域名:"+website, Toast.LENGTH_LONG).show(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.preference_activity_test, menu); return true; } }
上面的Activity重写了PreferenceActivity的public void onBuildHeaders(List<Header> target)方法,重写该方法指定加载前面定义preference_headers.xml布局文件。
上面的Activity中定义了两个PreferenceFragment,它们需要分别加载preferences.xml、display_prefs.xml两个选项设置的布局文件。
建立选项设置的布局文件需要创建根元素为PreferenceScreen的xml布局文件,它默认保存在/res/xml路径下。
preferences.xml布局文件如下:
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <!-- 设置系统铃声 --> <RingtonePreference android:ringtoneType="all" android:title="设置铃声" android:summary="选择铃声(测试RingtionePreference)" android:showDefault="true" android:key="ring_key" android:showSilent="true" ></RingtonePreference> <PreferenceCategory android:title="个人信息设置组"> <!-- 通过输入框填写用户名 --> <EditTextPreference android:key="name" android:title="填写用户名" android:summary="填写您的用户名(测试EditTextPreference)" android:dialogTitle="您所使用的用户名为:" ></EditTextPreference> <!-- 通过列表框选择性别 --> <ListPreference android:key="gender" android:title="性别" android:summary="选择您的性别(测试ListPreference)" android:dialogTitle="ListPreference" android:entries="@array/gender_name_list" android:entryValues="@array/gender_value_list" > </ListPreference > </PreferenceCategory> <PreferenceCategory android:title="系统功能设置组"> <CheckBoxPreference android:key="autoSave" android:title="自动保存进度" android:summaryOn="自动保存:开启" android:summaryOff="自动保存:关闭" android:defaultValue="true"> </CheckBoxPreference > </PreferenceCategory> </PreferenceScreen>
上面的界面布局文件定义了一个参数设置界面,该参数设置界面中包括两个参数设置组,而且该惨呼设置界面全面应用了各种元素,这样方便读者以后查询。
一旦定义了参数设置的界面布局文件之后,接下来在PreferenceFragment程序中使用该界面布局文件进行参数设置、保存十分简单,只要如下两步即可。
- 让Fragment继承PreferenceFragment
- 在onCreate(Bundle saveInstanceState)方法中调用addPreferencesFromRsource(...)方法加载指定的界面布局文件。
上面的实例中还用到了一个display_prefs.xml选项设置布局文件,该布局文件的创建步骤与preferences.xml文件的创建步骤相同。该文件的代码如下:
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory> <!-- 通过列表项选择灯光强度 --> <ListPreference android:key="light" android:title="灯光强度" android:summary="请选择灯光强度(测试ListPreference)" android:dialogTitle="请选择灯光强度" android:entries="@array/light_strength_list" android:entryValues="@array/light_value_list"/> </PreferenceCategory> <PreferenceCategory> <!-- 通过SwitchPreference设置是否自动滚屏 --> <SwitchPreference android:key="autoScroll" android:title="自动滚屏" android:summaryOn="自动滚屏:开启" android:summaryOff="自动滚屏:关闭" android:defaultValue="true" /> </PreferenceCategory> </PreferenceScreen>
至此,我们为该应用程序开发了三个Activity类,但这三个Activity还不能使用,还必须在AndroidManifest.xml清单文件中配置Activity才行。
配置Activity
Android应用要求所有应用程序组件(Activty、Services、ContentProvider、BroadcastReceiver)都必须显示进行配置。
只要为<application.../>元素添加<activity...>子元素即可配置Activity。例如下面的配置片段:
<activity android:name="com.example.studyactivity.PreferenceActivityTest" android:icon="@drawable/ic_settings_applications" android:label="@string/title_activity_preference_activity_test" android:exported="true" android:launchMode="singleInstance"> ...
</activity>
从上面的配置片段可以看出,配置Activity时通常指定如下几个属性。
- name:指定该Activity的实现类的类名。
- icon:指定该Activity对应的图标。
- label:指定该Activity的标签。
- exported:指定该Activity是否允许被其他应用调用。如果将该属性设为true,那么该Activity将可以被其他应用调用。
- launchMode:指定该Activity的加载模式,该属性支持standard、singleTop、singleTask和singleInstance这4种加载模式。
除此之外,配置Activity时通常还需要指定一个或多个<intent-filter.../>元素,该元素用于指定该Activity可响应的Intent。
为了在AndroidManifest.xml布局文件中配置、管理上面的三个Activity,可以在清单文件的<application.../>元素中增加如下三个<activity.../>子元素
<activity android:name="com.example.studyactivity.OtherActivity" android:label="@string/app_name" > <!--指定该Activity是程序的入口--> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.example.studyactivity.ExpandableListActivityTest" android:label="查看星际兵种" > </activity> <activity android:name="com.example.studyactivity.PreferenceActivityTest" android:icon="@drawable/ic_settings_applications" android:label="设置程序参数" > </activity>
上面的配置片段配置了三个Activity,其中第一个Activity还配置了一个<intent-filter.../>元素,该元素指定该Activity作为应用程序的入口。
运行上面的应用程序,将看到如图4.6所示的界面。
在图4.6所示的程序界面中,用户单击任意列表项即可启动对应额Activity,例如单击“设置程序参数”将会启动PreferenceActivityTest。单击“查看星际兵种”将会启动ExpandableListAcivityTest。单击4.6所示列表的第一个列表项将看到如图4.7所示的界面。
图4.7就是利用PreferenceActivity生成的选项设置列表界面。这个界面只是包含三个列表项,其中前两个列表项用于启动PreferenceFragment,最后一个列表项将会根据Intent启动其他Activity。
单击图4.7所示界面的第一个列表项,将可以看到如图4.8所示界面。
图4.8所示界面就是利用PreferencesFragment生成的选项设置界面,这个界面非常漂亮,而且系统会自动将设置的参数永久地保存到系统中——这都得益于PreferenceActivity。例如我们单击图4.8所示界面中的“填写用户名”列表项,系统将会显示如图4.9所示的输入框。
如果单击图4.6所示列表框中第二个列表项,将看到如图4.11所示的界面。