Activity是Android项目中最直观并且也是使用最多的一个组件,基本上所有的项目都是由多个Activity组成的。作为Android的四大组件之一,在使用它的时候还需要对其在清单文件AndroidManifest.xml中进行配置。本小节将讲解Activity在Android中的正常使用,以及如何开启一个新的Activity、如何在Activity开启的过程中传值、如何在清单文件AndroidManifest.xml中配置Activity等内容。
在一个新建的Android项目中,将会自动生成一个Activity,默认情况下,这个Activity作为当前应用的主页面,在应用被启动的时候初始化。
从自动生成的Activity中可以看出,使用Activity非常的简单,只需要继承即可。Activity位于android.app.Activity包下,间接继承了Context,而作为一个承载显示界面的系统组件,它有具有多个子类,用于实现一些特殊的显示界面。
下图显示了Activity的继承结构:
如上图所示,Ac tivity类间接或直接继承了Context、ContextWrapper、ContextThemeWrapper,因此可以直接使用它们中定义的方法,Activity还具有很多已实现的子类,无非就是对Activity进行了扩展,在Activity的基础上封装了一些额外的功能,例如ListActivity就在Activity中封装了一个ListView,ListActivity的例子在讲解ListView已经给出,这里不再赘述。
既然Activity是用来承载一个操作界面的,那么必须在Activity初始化的时候,完成操作界面的绘制,这个必须在onCreate()方法中实现,这是一个Activity的生命周期方法,在启动Activity的时候会被回调,在onCreate()方法中调用Activity.setContentView()方法加载一个需要显示的View,setContentView()具有多个重载方法,无非就是数据源不同而已,下面是这些setContentView()方法的重载方法的完整签名:
· void setContentView(int layoutResID):指定一个XML布局资源。
· void setContentView(View view):指定一个View对象。
· void setContentView(View view,ViewGroup.LayoutParams params):指定一个View对象,并设置其位置。
当加载了界面显示的View之后,可以使用Activity.findViewById(int)方法找到界面显示的View中的UI组件。
下面在默认项目中添加一个初始的Activity。
代码清单:\codes\05\01\ActivityBaseDemo\src\com\bookdemo\activitybasedemo\NewActivity.java
public class NewActivity extends Activity { private Button btnFinish; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_new); btnFinish = (Button) findViewById(R.id.btnFinish); } }
新定义的Activity还无法使用,还需要对其进行配置,下面来讲解如何配置一个Activity。
Android四大组件,在使用的时候均需要在清单文件AndroidManifest.xml中显示的进行配置。所以只定义一个Activity还无法使用它,需要在AndroidManifest.xml文件的<application…/>元素中添加<activity…/>子元素即可完成Activity的配置。在<activity…/>元素中,必须配置android:name属性,用来设定配置的Activity的实现类,它还具有一些其它的属性,用来配置其它的内容。
代码清单:\codes\05\01\ActivityBaseDemo\AndroidManifest.xml
<activity android:icon="@drawable/icon" android:name="com.bookdemo.activitybasedemo.NewActivity" android:label="NewActivity" > </activity>
上面为之前定义的Activity进行了配置,其中的<activity…/>元素内有三个比较常用的属性,它们分别代表了:
· android:name:指定Activity的实现类。
· android:label:指定Activity的标签。
· android:icon:指定Activity的图标。
对于android:name属性而言,有时候会看到不写全类名,而只是用一个点代替,这是因为在当前<manifest…/>的包下的Activity,是可以使用省略包名,使用一个点代替,如:
android:name=".MainActivity"
在<activity…/>元素中,还有一个子元素<intent-filter…/>,这是一个意图过滤器,用来指定开启的Activity,在后面会有单独章节对其进行详细介绍。
Android项目中会包含多个Activity,但是只有一个Activity作为程序的入口Activity,该Activity通过清单文件AndroidManifest.xml进行配置,通常其它Activity会从一个当前显示的Activity进行启动。
从一个Activity启动另外一个Activity,需要用到一个Activity.startActivity()方法,这方法会传递一个Intent对象,它封装了一个意图信息,用于指定启动的Android组件,这里指定的是一个Activity。这个方法的完整签名如下:
void startActivity(Intent intent)
启动了一个Activity就伴随着需要关闭一个Activity。关闭Activity除了使用设备上的回退键之外,还可以使用Activity.finish()方法,这个方法会关闭当前的Activity,使当前Activity从回退栈退栈,并显示退栈后栈顶的Activity。
示例:开启一个新的Activity,并在其中关闭自己,注意新声明的Activity也需要在清单文件中进行配置。
代码清单:\codes\05\01\ActivityBaseDemo\src\com\bookdemo\activitybasedemo\MainActivity.java
public class MainActivity extends Activity { private Button btnOpenNewActivity; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnOpenNewActivity=(Button) findViewById(R.id.btnOpenNewActivity); btnOpenNewActivity.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 开启一个Activity,指定当前上下文和目标Activity Intent intent=new Intent(MainActivity.this, NewActivity.class); startActivity(intent); } }); } }
代码清单:\codes\05\01\ActivityBaseDemo\src\com\bookdemo\activitybasedemo\NewActivity.java
public class NewActivity extends Activity { private Button btnFinish; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_new); btnFinish = (Button) findViewById(R.id.btnFinish); btnFinish.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(NewActivity.this, "您关闭了新开启的Activity", Toast.LENGTH_SHORT).show(); finish(); } }); } }
启动与关闭一个Activity,两个Activity并没有任何数据的交互。但是在实际项目中,通常情况下开启一个新的Activity是为了处理一些任务,并在任务处理结束后,获得处理结果。
如果需要从新开启的Activity中返回数据,需要用到Activity的另外一个启动方法Activity.startActivityForResult(),它会传递一个Intent对象,还需要传递一个整形的请求码,用于标识唯一的请求,这个标识可以是任意数。startActivityForResult()方法的完整签名如下:
void startActivityForResult(Intent intent,int requestCode)
在新的Activity中,处理完成任务之后,需要把处理结果进行返回。对于一些简单的逻辑,只需要在返回成功或者失败的情况下,可以使用setResult()方法,指定一个响应码,使用这个响应码来确定任务是否成功完成,最后调用finish()方法关闭这个Activity,它的完整签名如下:
void setResult(int resultCode)
在确定了requestCode和resultCode之后,还需要再重写原Activity的onActivityResult()方法,用于接受新开启的Activity的返回码开启时的请求码,onActivityResult()方法的完整签名如下:
void onActivityResult(int requestCode, int resultCode, Intent data)
onActivityResult()方法的参数很简单,requestCode是开启Activity的时候传递的请求码,resultCode是新开启的Activity返回的响应码,date是新开启的Activity传递的数据内容,之后会详细讲解。
从新Activity中返回数据的整个流程如下:
1. 在原Activity中使用startActivityForResult()方法启动一个目标Activity,并指定请求码。
2. 在目标Activity中执行任务,在执行完成之后使用setResult()方法返回响应码,并使用finish()关闭Activity。
3. 重写原Activity的onActivityResult()方法,用于处理目标Activity返回的数据。
示例:开启一个新的Activity,获得其用户选择的性别。
代码清单:\codes\05\01\ActivityBaseReturnCodeDemo\src\com\bookdemo\activitybasereturncodedemo\MainActivity.java
public class MainActivity extends Activity { private Button btnOpenNewActivity; private final static int REQUEST_CODE = 101; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnOpenNewActivity = (Button) findViewById(R.id.btnOpenNewActivity); btnOpenNewActivity.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 实例化一意图,指定当前上下文和目标Activity Intent intent = new Intent(MainActivity.this, NewActivity.class); // 启动一个指定请求码的Activity startActivityForResult(intent, REQUEST_CODE); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 通过请求码和响应码判断逻辑 if (requestCode == REQUEST_CODE) { if (resultCode == 1) { Toast.makeText(MainActivity.this, "您选择了:male!", Toast.LENGTH_SHORT) .show(); } else if (resultCode == 0) { Toast.makeText(MainActivity.this, "您选择了:female!", Toast.LENGTH_SHORT) .show(); } } super.onActivityResult(requestCode, resultCode, data); } }
代码清单:\codes\05\01\ActivityBaseReturnCodeDemo\res\layout\activity_new.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请选择性别" /> <RadioGroup android:id="@+id/rgGender" android:layout_width="wrap_content" android:layout_height="wrap_content" > <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="male" /> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="female" /> </RadioGroup> <Button android:id="@+id/btnReturn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="关闭" /> </LinearLayout><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请选择性别" /> <RadioGroup android:id="@+id/rgGender" android:layout_width="wrap_content" android:layout_height="wrap_content" > <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="male" /> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="female" /> </RadioGroup> <Button android:id="@+id/btnReturn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="关闭" /> </LinearLayout>
代码清单:\codes\05\01\ActivityBaseReturnCodeDemo\src\com\bookdemo\activitybasereturncodedemo\NewActivity.java
public class NewActivity extends Activity { private Button btnReturn; private RadioGroup rgGender; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_new); rgGender = (RadioGroup) findViewById(R.id.rgGender); btnReturn = (Button) findViewById(R.id.btnReturn); btnReturn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int flag = -1; for (int i = 0; i < rgGender.getChildCount(); i++) { // 判断用户选择的性别 RadioButton radioButton = (RadioButton) rgGender .getChildAt(i); if (radioButton.isChecked()) { if (radioButton.getText().toString().equals("male")) { flag = 1; } else if (radioButton.getText().toString() .equals("female")) { flag = 0; } } } if (flag == -1) { Toast.makeText(NewActivity.this, "请选择性别!", Toast.LENGTH_SHORT).show(); }else{ // 设置响应码,并结束Activity setResult(flag); finish(); } } }); } }
在模拟器上运行效果如图:
在原Activity重写的onActivityResult()方法中,通过请求码和响应码判断响应逻辑是非常明智的设计,因为同一个Activity可以被多个Activity启动,而一个Activity也可以启动多个Activity。使用请求码和响应码就可以在onActivityResult()方法中唯一确定一个返回的处理逻辑,这样避免了处理逻辑的混乱。
无论是开启一个Activity还是从开启的Activity中返回数据,仅仅使用请求码和响应码,是远远达不到我们的需求的。那么如何在Activity启动和关闭的过程中,传递一个复杂的数据呢?
先让我们回忆一下前面讲解的启动与关闭Activity时获取数据的几个关键的方法:
· void startActivity(Intent intent):开启一个Intent指定的Activity。
· void startActivityForResult(Intent intent,int requestCode):开启一个待返回数据的Intent指定的Activity,并设置请求码。
· void setResult(int resultCode,Intent date):设置返回码,并指定一个Intent对象。
· void onActivityResult(int requestCode, int resultCode, Intent data):以startActivityForResult()方法开启的Activity,结束时设置了返回码的情况下被回调。
上面的方法,都涉及到一个Intent对象,之前讲解如何启动Activity的时候,提到Intent是一个意图,可以指定待启动的Android组件,其实它还可以在各个Android组件之间传递数据。
Intent本身并不维护数据,它维护一个Bundle对象,这个Bundle对象内部又维护了一个Key-Value键值对的HashMap<String, Object>集合,在这个Map集合中即可完成数据的存储,并伴随着Intent在各个Android组件之间进行传递。
Bundle既然是一个HashMap<String, Object>的集合,它也提供了一系列的putXxx()方法,用于设置Key-Value键值对的数据,还为putXxx()方法提供了一系列对应的getXxx()方法,用于通过key值获取数据。Bundle除了可以操作基本数据类型和基本数据类型的数组,它还可以传递一个实现了Serializable接口的对象,Serializable指定该对象可被序列化,也就是说,Bundle不但可以传递基本数据类型,还可以传递任何对象数据。而Bundle本身也是实现了Serializable接口,可以被序列化的,所以它才可以在不同组件之间完成数据的传递。
定义好Bundle对象中的数据之后,可以把数据关联到Intent中,在Intent中设置与获取其中的Bundle对象,可以使用如下两个方法:
· Intent putExtras(Bundle extras):为Intent设置Bundle对象。
· Bundle getExtras():获取Intent中的Bundle对象。
Intent内部维护了Bundle对象,为了简化操作,它还提供了putExtra()方法,这个方法具有一系列的重载,用于支持Bundle类支持的多种类型,其实内部还是在Bundle对象中保存数据。Intent并没有提供对应的getExtra方法,只能通过getExtras()获取Intent中的对象,再从Bundle对象获取所需的数据。