今天,我们来研究一下Intent,没错,就是前面说过的比较难理解的那个东西,希望通过这篇文章之后,你发现前面那句话其实是不对的。
前文中说过,Intent就像Activity之间的双面胶,就字面意思而言:“意图, 意向, 目的”, 基本上可以诠释这个对象的作用。它里面包含的就是一些信息,这些信息能够告诉我们当前发生了什么,以及想要干什么。我觉得和前面的事件驱动中的事件非常 像,不同的是,它不仅仅包含事件,还包含一些数据信息。Intent或者也可以说像一个需求说明一样,说明了当前的事件以及一些数据,接下 来,Android会依据这个Intent的说明,为其找到一个Activity,并把这个Intent交给这个Activity。看起来Intent好 像是征婚广告一样哦,Intent提出自己的一些描述(不同的是,他描述的是自己,而不是描述的别人),然后Android根据这些描述找到何时的 Activity。
Intent有两种,一种是显式Intent,另一种是隐式Intent。显式Intent会在自己的说明中直接声明由谁(哪个Activity)来接收 处理这个Intent。显式的Intent是说,我已经有了意中人了,告诉Android不用费劲找了,可以直接定位到意中人,例如前文中的这个例子:
Intent intent = new Intent();
intent.putExtra("name", editText.getText().toString());
intent.setClass(ActivityLifecycle.this, AlertDialog.class);
通过setClass方法,直接告诉Android,此Intent交由AlertDialog处理。也可以通过setComponent()、setClassName()等方法来做同样的事情。
在一般的程序中, 我觉得使用显式Intent已经足够了。这样程序看起来简单易懂,毕竟大多数的Android程序都不会有太复杂的Activity之间的关系。
在前文中的另外一个方法中,使用的就是隐式的Intent。隐式Intent呢,比较含蓄,比较糊涂,比较模糊,还没有明确的目标,并且比较悲观,不会对对方提一些要求,只是描述了一下自己的情况,意思就是说,我就是我所描述的这样的一个人,谁能接受, 谁就可以应征。:)
Intent intent = new Intent();
intent.putExtra("name", editText.getText().toString());
intent.setAction("com.roiding.sample.action.MAIN");
Intent说明自己的方式有:
action 用来指明执行的动作,比如说ACTION_VIEW, ACTION_EDIT, ACTION_MAIN,这都是在Android中定义的标准action,开发者可以自己定义自己需要的action,用字符串来表示。更多标准 action参看这里。
category 描述action的附加信息,这个属性我一直不太明白,它被设计出来的用意到底是为什么,到目前为止,我对他的理解是:它就像一个action类别,例 如:CATEGORY_LAUNCHER、CATEGORY_ALTERNATIVE。属于CATEGORY_LAUNCHER这个类别的,可以被 Android放到顶级列表(类似于九宫格的那个界面)中被执行,属于CATEGORY_DEFAULT这个类别的,可以用 startActivity(intent)来启动Activity,等等。而这些行为都是Android内定的,所以自定义category不知道会有 什么意义,更多标准category参看这里。
data 动作操作的数据,这部分数据会像嫁妆一样,携带给接收它的Activity,Uri格式。Uri的概念和传统的URI的概念是一致的,遵循RFC 2396。 而Uri最终是由ContentProvider提供的,所以会涉及到ContentProvider的概念,这里也暂时不去解释了,只需要知道这个 data所声明的数据,就像一个url一样,通过这个url,可以找到提供这个url的ContentProvider提供的数据,然后就可以操作这些数据了。这里面的data似乎和前面的extras功能有重叠?按照我的理解,data确实可以放到extras中传递,但是单独把它提出来确有它的用意。在后面的IntentFilter中会有交代。
既然已经比喻为征婚广告了,把Intent比喻为征婚的需求,那么由Android系统根据什么挑选的候选人呢?
前面中说过这个文件:AndroidManifest.xml,而且当时将其比喻为户口簿,这下好了,所有的适龄青年(Activity)都在这个大的户口簿上了,按照Intent的需求直接在户口簿中寻找就可以了。那又是如何知道哪个适龄青年是否符合要求呢?
在AndroidManifest文件中,对每个适龄青年(Activity)都做了说明,就是intent-filter,它说明了当前这个适龄青年愿意接收什么样的需求,这样当Intent与这个Intent-filter中的说明一致时,Intent就找到了Activity。
这样,当一个Intent的所有说明,都被Activity的intent-filter接受时,这个Activity就是候选人之一。如果有多个 Activity成为候选人,那么在程序运行的时候Android提示你从多个Activity中手动选择一个来作为最终的intent接收执行者。
对于Activity来说,在它的intent-filter中,可以有多个action,多个category,多个data,这样可以有多个Intent可以与之匹配。
说到这里,前面的那个关于data和extras功能重叠的问题似乎还没有解决,所以这个data的重要的作用就是,它提供了一种反向挑选的机制,使 Activity对Intent也可以提出要求,要求Intent的data必须是intent-filter中声明的data中之一(因为可以有多个 data)。这点是非常重要的,不然又会出现ActivityNotFoundException之类的错误了。
到此篇文章为止,一般的Android界面编程应该已经够用了,而对于ContentProvider、Adapter、Services等,在一般的应用中,可以完全不用考虑。等到必须要使用的时候再说吧。
在前面的文章中,已经创建好了一个用户演示Activity生命周期的示例,但是由于那篇文章篇幅过长,所以分成两部分来写,在这篇文章中,将详细介绍Activity的生命周期。
了解Activity的生命周期是非常有必要的,在后面的文章中会涉及到数据的保存(也就是持久化)等功能,而保存时机的选择关乎用户体验,而了解Activity的生命周期会对时机的选择有更好的理解。
在文档中,已经有了比较清晰的说明:
如果一个Activity显示在最前端,它的状态时active或者是running
如果一个Activity失去焦点,但是还能在屏幕上看到它,比如说,被一个非全屏的或者是透明的另一个Activity遮盖,它此时的状态是paused。一个处于paused状态的Activity,保持着所有自身的状态和信息,秉持着与window manager的联系,但是当系统内存不足时,可能会被系统终止
如果一个Activity完全看不见了,被其他的Activity完全挡住,它的状态变成stopped。处于stopped的Activity虽然也保留着如同paused状态一样的特性,但是此时的它更容易被系统终止
如果一个Activity处于paused或者stopped状态时,系统可以通过要求Activity结束或者强制kill的方式从内存中删除这个Activity,当它在此需要显示时,就需要restart和重新加载以前的数据。(可见,适时的保存数据的重要性)
用一个状态装换图来说明问题:
仔细品味这张图,和上面的说明,会很好的理解Activity的生命周期。接下来,用前面的示例程序来具体说明一下。在前面的示例程序中,写了很多空方法,方法中只是打印了一行日志,这些方法都是Activity在进行状态转换的时候要使用的方法。
程序启动
INFO/ActivityLifecycle: onCreate …
INFO/ActivityLifecycle: onStart …
INFO/ActivityLifecycle: onResume …
点击一下第一个按钮,会弹出一个小对话框,主Activity在对话框的后面还是能看的见,此时的Activity的状态时paused
INFO/ActivityLifecycle: onPause …
然后,点击一下模拟器的返回按键,小对话框消失,返回到主Activity
INFO/ActivityLifecycle: onResume …
点击一下第二个按钮,一个新的Activity会代替当前的Activity
INFO/ActivityLifecycle: onPause …
INFO/ActivityLifecycle: onStop …
这时候,主Activity的状态就变成了stopped了
点击一下模拟器的返回按键,返回到主Activity
INFO/ActivityLifecycle: onRestart …
INFO/ActivityLifecycle: onStart …
INFO/ActivityLifecycle: onResume …
再次点击一下模拟器的返回按键,退出程序
INFO/ActivityLifecycle: onPause …
INFO/ActivityLifecycle: onStop …
INFO/ActivityLifecycle: onDestroy …
注意一下第一个按钮和第二个按钮被点击之后,所执行的过程是不同的。然后可以进行各种组合的前进退出,通过观察日志了解Activity的生命周期。
前面的文章已经讲到了如何在一个Activity中与用户进行交互。这篇文章将解释如何创建多个Activity的用户界面,以及了解Activity的生命周期。这次是一个完整的示例,可以在这里下载源代码。
这个多Activity的示例程序的功能大概是这样的:
在主页面上有三个元素:一个文本输入框,两个按钮。用户可以在文本框中输入一些字符
点击第一个按钮,将弹出一个对话框形式的Activity,显示”Hello, …”
如果点击的是第二个按钮,将显示另一个全屏的Activity,显示”Hello, …”
OK,按照前面的规矩,分成几步:
第1步,先完成UI的设计,创建布局。
从程序来看,似乎有三个Activity,但是第二个和第三个几乎是相同的,于是,只创建两个布局:
res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<EditText
android:id="@+id/edittext_input"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/button_sayhello_dialog"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello_dialog"
/>
<Button
android:id="@+id/button_sayhello_activity"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello_activity"
/>
</LinearLayout>
res/layout/dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/textview_hello"
android:text="@string/hello"
/>
第2步,然后呢,需要把上面提到的字符变量声明一下:
res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello, </string>
<string name="hello_dialog">Say Hello(Dialog)</string>
<string name="hello_activity">Say Hello(Activity)</string>
<string name="app_name">Activity Lifecycle</string>
</resources>
通过前面的文章,上面的东西应该都比较好懂。
第3步,创建Activity,这里和第一步一样,虽然有三个Activity,但是看起来用两个其实就够了。然而事实并非如此,具体原因后续,我们需要定义三个Activity:分别将他们定义为:ActivityLifecycle(用来显示主画面)、AlertDialog(用来显示对话框)和AlertActivity(用来显示那个全屏的Activity)。
主Activity:
package com.roiding.sample;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class ActivityLifecycle extends Activity {
private static final String TAG = "ActivityLifecycle";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate …");
setContentView(R.layout.main);
final EditText editText = (EditText) findViewById(R.id.edittext_input);
// try to display a dialog
Button [...]
通过上一篇文章,知道了如何创建一个应用程序来显示一个静态的View。然而,在大多数情况下仅仅显示是不够的,应该还需要与用户的交互行为。现在完成一个简单的和用户交互的示例。
这个小程序的需求是:
画面上有三行元素:
第一行是一个TextView,用来显示文字
第二行是一个EditText,用户将在此输入文字
第三行是一个Button
用户点击Button之后,会在第一行的TextView中显示:Hello,(用户在第二行中输入的文字)。
比如说用户在输入框中输入:“Roiding.com”,那么点击Button之后,会在显示区域显示:“Hello, Roiding.com”。
要完成这个程序,大概需要用5步:
第1步,先完成UI的设计,在res/layout/目录下创建一个文件,名为:sayhello.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/textview_display"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20px"
android:textStyle="bold"
/>
<EditText
android:id="@+id/edittext_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/button_sayhello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sayhello"
/>
</LinearLayout>
如果已经看过前一篇文章的话,这个文件还是比较容易懂的,不需要太多的解释了,只解释一下这几个语句:
android:id=”@+id/textview_display”
这个是用来为当前的View声明一个ID,这样,在Java程序中就可以通过这个ID来找到这个元素,进而能够操作这个元素。
android:textSize=”20px”
声明字体大小为20px,对于每种View,都会有一系列的可以设置的属性,具体的需要参考每个View的说明文档。
第2步,创建好这个布局文件之后,再把这个文件需要的android:text=”@string/sayhello”在res/values/strings.xml中声明一下:
<string name="sayhello">say hello</string>
第3步,现在,UI已经设计好了,那么创建一个Activity(命名为:SayHello.java)来显示这个UI:
package com.roiding.study;
import android.app.Activity;
import android.os.Bundle;
public class SayHello extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sayhello);
}
}
在这一步完成的时候,就可以运行一下这个Activity,看看最终显示的效果是否和预期的一致,如果有差别,再调整一下。
第4步,为UI添加交互动作,添加完毕之后的SayHello.java:
package com.roiding.study;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class SayHello extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sayhello);
Button btn = (Button) findViewById(R.id.button_sayhello);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
EditText edt = (EditText) findViewById(R.id.edittext_name);
TextView tv = (TextView) findViewById(R.id.textview_display);
tv.setText("Hello," [...]
Recent Comments