首页 > 代码库 > 第 2 章 先从看得到的入手,探究活动

第 2 章 先从看得到的入手,探究活动

2.1 活动是什么

活动(Activity),包含用户界面的组件,主要用于和用户进行交互

2.2 活动的基本用法

一个完整活动的创建流程:

2.2.1 手动创建活动

New→Class,继承自 Activity,点击 Finish 完成创建。

2.2.2 创建和加载布局

1)创建布局文件

右击 res/layout目录→New→Android XMLFile

2)在Activity的onCreate()方法中加载布局文件

setContentView(R.layout.first_layout);

2.2.3 在 AndroidManifest 文件中注册Activity

<activity

android:name=".FirstActivity"

android:label="This is FirstActivity" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

2.2.4 隐藏标题栏

requestWindowFeature(Window.FEATURE_NO_TITLE);

2.2.5 在活动中使用 Toast

Toast一种短小的弹窗信息提示

Toast.makeText(FirstActivity.this, "You clicked Button 1",Toast.LENGTH_SHORT).show();

2.2.6 在活动中使用 Menu

1)首先在 res 目录下新建一个 menu 文件夹,右击 res 目录→New→Folder;接着在文件夹下再新建一个名叫 main 的菜单文件,右击 menu 文件夹→New→Android XML File,然后编写菜单选项:

<item

android:id="@+id/add_item"

android:title="Add"/>

<item

android:id="@+id/remove_item"

android:title="Remove"/>

2)在Activity中部署菜单,通过onCreateOptionsMenu()方法实现

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.main, menu);

return true;

}

3)为菜单定义响应事件,通过onOptionsItemSelected(MenuItem item)实现

public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case R.id.add_item:

Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show();

break;

case R.id.remove_item:

Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show();

break;

default:

}

return true;

}

2.2.7 销毁一个活动

button1.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

finish();

}

});

2.3 使用 Intent 在活动之间穿梭

Intent(意图),定义一个动作,组件之间进行交互的一种重要方式,同时还可进行数据传递,分为显式 Intent 和隐式 Intent

2.3.1 使用显式 Intent

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);

startActivity(intent);

2.3.2 使用隐式 Intent

1)在配置文件中配置Activity的过滤语句

<intent-filter>

<action android:name="com.example.activitytest.ACTION_START" />

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

<action/>指明了该Activity能够响应哪些动作,当某个Activity的Intent的action条件与之匹配,则将唤起该Activity。

2)在Activity中设置与之匹配的Intent动作

Intent intent = new Intent("com.example.activitytest.ACTION_START");

startActivity(intent);

a.一个 Intent Filter 可以包含多个 Action,可以指定一个 Action列表用于标识 Activity 所能接受的“动作”,并且能够指定多个 category,调用 Intent 中的 addCategory()方法来添加。

b.如果使用的是DEFAULT这种默认的category,在稍后调用startActivity()方法的时候会自动将这个category添加到Intent中,否则需要通过addCategory()方法来手动添加Category,如

(intent.addCategory("com.example.activitytest.MY_CATEGORY");)

c.如果有多个组件匹配成功,就会以对话框列表的方式让用户进行选择。

d. 每个Intent中只能指定一个action,但却能指定多个category;类别越多,动作越具体,意图越明确(同时要求被匹配的组件必须同时满足这多个类别,才能匹配成功)。

e. 当Intent匹配成功的组件有多个时,显示优先级高的组件,如果优先级相同,显示列表让用户自己选择

f.优先级从-1000至1000,并且其中一个必须为负的才有效。通过来android:priority设置优先级

<activity

android:name=".SecondActivity">

<intent-filter android:priority="-1">

<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />

<data android:scheme="http" android:host="www.baidu.com"/>

</intent-filter>

</activity>

2.3.3 更多隐式 Intent 的用法

1)调用系统浏览器

Intent intent = new Intent(Intent.ACTION_VIEW);

intent.setData(Uri.parse("http://www.baidu.com"));

startActivity(intent);

<data>标签,在<intent-filter>中配置,指明能够响应哪些数据请求,只有Intent 中携带的 Data与之完全一致时,当前活动才能够响应该 Intent。

2)拨打电话

Intent intent = new Intent(Intent.ACTION_DIAL);

intent.setData(Uri.parse("tel:10086"));

startActivity(intent);

2.3.4 向下一个活动传递数据

Intent中提供了一系列 putExtra()方法,把想要传递的数据暂存在 Intent 中,启动了另一个活动后,把这些数据再从 Intent 中取出。

1)  传递数据

String data = "http://www.mamicode.com/Hello SecondActivity";

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);

intent.putExtra("extra_data", data);

startActivity(intent);

putExtra()方法第一个参数是键,用于后面从 Intent中取值

2)  取出数据

Intent intent = getIntent();

String data = http://www.mamicode.com/intent.getStringExtra("extra_data");

通过 getIntent()方法获取到用于启动当前 Activity 的 Intent。

2.3.5 返回数据给上一个活动

1)通过startActivityForResult()方法来启动下一个Activity

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);

startActivityForResult(intent, 1);

startActivityForResult()方法接收两个参数,第一个参数还是 Intent,第二个参数是请求码,用于在之后的回调中判断数据的来源,请求码只要是一个唯一值就可以了。

2)向上一个活动返回数据

Intent intent = new Intent();

intent.putExtra("data_return", "Hello FirstActivity");

setResult(RESULT_OK, intent);

这里的Intent仅仅用于传递数据,setResult()方法专门用于向上一个活动返回数据,同时也需要在onBackPressed()方法中重写该段代码。

3)读取返回的数据

在该活动销毁之后会回调上一个活动的onActivityResult()方法,所以需要重写该方法来取得回传的数据

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

switch (requestCode) {

case 1:

if (resultCode == RESULT_OK) {

String returnedData = http://www.mamicode.com/data.getStringExtra("data_return");

Log.d("FirstActivity", returnedData);

}

break;

default:

}

}

2.4 活动的生命周期

2.4.1 返回栈

Android 是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下 Back 键或调用 finish()方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。

2.4.2 活动状态

1)运行状态

活动位于栈顶。

2)暂停状态

当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。

3)停止状态

当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。

4)销毁状态

当一个活动从返回栈中移除后就变成了销毁状态。

2.4.3 活动的生存期

对应七个回调方法,覆盖了活动生命周期的每一个环节。

1)onCreate()

在活动第一次被创建的时候调用,完成活动的初始化操作,比如加载布局、绑定事件等。

2)onStart()

活动由不可见变为可见的时调用。

3)onResume() (Resume:重新开始、恢复)

准备好和用户进行交互的时候调用,此时的活动一定位于返回栈的栈顶,并且处于运行状态。

4)onPause() (Pause:暂停)

在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗 CPU 的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。

5)onStop()

在活动完全不可见的时候调用,当启动的是一个对话框,活动并非完全不可见,则只会启动onPause()而不会启动onStop()。

6)onDestroy()

在活动被销毁之前调用,之后活动的状态将变为销毁状态。

7)onRestart()

由停止状态变为运行状态之前调用,也就是活动被重新启动了。

2.4.4 活动的生存期

除了 onRestart()方法,其他都是两两相对的,从而又可以将活动分为三种生存期:

1)完整生存期

在 onCreate()方法和 onDestroy()方法之间所经历的,就是完整生存期。一般情况下,在 onCreate()方法中完成各种初始化操作,在 onDestroy()方法中完成释放内存的操作。

2)可见生存期

onStart()方法和 onStop()方法之间,就是可见生存期。我们可以通过这两个方法,合理地管理那些对用户可见的资源,如在 onStart()方法中对资源进行加载,而在onStop()方法中对资源进行释放。

3)前台生存期

在 onResume()方法和 onPause()方法之间,就是前台生存期。活动总是处于运行状态的,此时的活动是可以和用户进行交互。

2.4.5 活动被回收了怎么办

为防止填写的数据丢失,可通过onSaveInstanceState(Bundle outState)方法将该Activity目前的数据状态保存起来,并在回调时通过onCreate(Bundle savedInstanceState)方法将保存的数据还原。

1)保存数据

protected void onSaveInstanceState(Bundle outState) {

super.onSaveInstanceState(outState);

String tempData = "http://www.mamicode.com/Something you just typed";

outState.putString("data_key", tempData);

}

Bundle 提供了一系列的方法用于保存数据,每个保存方法需要传入两个参数,第一个参数是键,第二个参数是真正要保存的内容。

2)取出数据

protected void onCreate(Bundle savedInstanceState) {

if (savedInstanceState != null) {

String tempData = http://www.mamicode.com/savedInstanceState.getString("data_key");

Log.d(TAG, tempData);

}

……

}

onCreate()方法有一个 Bundle类型的参数,这个参数在一般情况下都是null,但是当活动被系统回收之前有通过 onSaveInstanceState()方法来保存数据的话,这个参数就会带有之前所保存的全部数据,通过相应的取值方法将数据取出即可。

2.5 活动的启动模式

在 AndroidManifest.xml 中通过给<activity>标签指定android:launchMode属性来选择启动模式。

2.5.1 standard模式

standard 是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置,不在乎是否已经在返回栈中存在该活动,每次启动都会创建该活动的一个新的实例。

2.5.2 singleTop模式

在启动活动时如果发现返回栈的栈顶已经是该活动, 则认为可以直接使用它,不会再创建新的活动实例。

2.5.3 singleTask模式

每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。

2.5.4 singleInstance模式

会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。

2.6 活动的最佳实践

2.6.1 知晓当前是在哪一个活动

编写一个BaseActivity类,并让其他所有的Activity类都继承自该类

public class BaseActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Log.d("BaseActivity", getClass().getSimpleName());

}

}

当进入任何一个活动时都会打印该活动的名称。

2.6.2 随时随地退出程序

解决思路:用一个专门的集合类对所有的活动进行管理

1)新建一个 ActivityCollector类作为活动管理器,通过集合进行管理

public class ActivityCollector {

        public static List<Activity> activities = new ArrayList<Activity>();

        public static void addActivity(Activity activity) {

                 activities.add(activity);

        }

        public static void removeActivity(Activity activity) {

                 activities.remove(activity);

        }

        public static void finishAll() {

                 for (Activity activity : activities) {

                         if (!activity.isFinishing()) {

                                 activity.finish();

                         }

                 }

        }

}

2)在其他类的公共父类的相应方法中进行调用,

public class BaseActivity extends Activity {

        @Override

        protected void onCreate(Bundle savedInstanceState) {

                 super.onCreate(savedInstanceState);

                 Log.d("BaseActivity", getClass().getSimpleName());

                 ActivityCollector.addActivity(this);

        }

        @Override

        protected void onDestroy() {

                 super.onDestroy();

                 ActivityCollector.removeActivity(this);

        }

}

3)当在某个Activity中想直接退出程序时,只需调用ActivityCollector.finishAll()就行了。

2.6.3 启动活动的最佳写法

1)在被启动Activity中添加一个actionStart()方法

public static void actionStart(Context context, String data1, String data2) {

        Intent intent = new Intent(context, SecondActivity.class);

        intent.putExtra("param1", data1);

        intent.putExtra("param2", data2);

        context.startActivity(intent);

}

2)当需要调用该Activity时便可直接调用actionStart()方法

button1.setOnClickListener(new OnClickListener() {

        @Override

        public void onClick(View v) {

                 SecondActivity.actionStart(FirstActivity.this, "data1", "data2");

        }

});

第 2 章 先从看得到的入手,探究活动