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所示的界面。