第3章Activity 学习目标  掌握Activity的生命周期。  掌握Activity的常用方法。  掌握显式Intent和隐式Intent的使用。  掌握Activity中的数据传递方式。 在Android系统中,用户与程序的交互是通过Activity完成的,同时Activity也是Android四大组件中使用最多的一个,本章将详细讲解有关Activity的知识。 3.1Activity基础 3.1.1认识Activity Activity的中文意思是“活动”,它是Android应用中负责与用户交互的组件,相当于Swing编程中的JFrame组件。与其不同的是,JFrame本身可以设置布局管理器,不断地向其添加组件,而Activity只能通过setContentView(View)来显示布局文件中已经定义的组件。 在应用程序中,Activity就像一个界面管理员,用户在界面上的操作是通过Activity来管理的,下面是Activity的常用事件。  OnKeyDown(int keyCode,KeyEvent event): 按键按下事件。  OnKeyUp(int keyCode,KeyEvent event): 按键松开事件。  OnTouchEvent(MotionEvent event): 单击屏幕事件。 当用户按下手机界面上的按键时,就会触发Activity中对应的事件OnKeyDown()来响应用户的操作。3.1.2节会通过具体实例讲解Activity的常用事件。 3.1.2如何创建Activity 创建一个Activity的具体的步骤如下: (1) 定义一个类继承自android.app.Activity或其子类,如图31所示。 图31创建Activity (2) 在res/layout目录下创建一个XML文件,用于创建Activity的布局。 (3) 在app/manifests目录下的AndroidManifest.xml清单文件中注册Activity,如图32所示。 图32Activity注册 (4) 重写Activity的onCreate()方法,并在该方法中使用setContentView()加载指定的布局文件。新创建的Activity的具体代码如下所示: package com.jxust.cn.chapter3_1; import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { //项目的入口Activity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //设置Activity显示的布局 setContentView(R.layout.activity_main); } } 接下来将通过具体的例子讲解3.1.1节中提到的Activity中的几个常用事件,具体的Activity代码如下所示: public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } //响应按键按下事件 public boolean onKeyDown(int keycode, KeyEvent event){ Toast.makeText(this,"按键按下了!", Toast.LENGTH_SHORT).show(); return super.onKeyDown(keycode, event); } //响应按键松开事件 public boolean onKeyUp(int keycode,KeyEvent event){ Toast.makeText(this,"按键松开了!", Toast.LENGTH_SHORT).show(); return super.onKeyDown(keycode, event); } //响应屏幕触摸操作 public boolean onTouchEvent( MotionEvent event){ Toast.makeText(this,"触摸了屏幕!", Toast.LENGTH_SHORT).show(); return super.onTouchEvent(event); } } 运行效果如图33所示。 图33Activity常用事件 视频讲解 3.1.3Activity的生命周期 每一个Android应用程序在运行时,对于底层的Linux Kernel而言都是一个单独的进程,但是对于Android系统而言,因为局限于手机画面的大小与使用的考虑,不能把每一个运行中的应用程序窗口都显示出来。所以通常手机系统的界面一次仅显示一个应用程序窗口,Android使用了Activity的概念来表示界面。 Activity的生命周期分为3种状态,分别是运行状态、暂停状态和停止状态。下面将详细介绍这3种状态。  运行状态: 当Activity在屏幕最前端的时候,它是有焦点的、可见的,可以供用户进行单击、长按等操作,这种状态称为运行状态。  暂停状态: 在一些情况下,最上层的Activity没有完全覆盖屏幕,这时候被覆盖的Activity仍然对用户可见,并且存活。但当内存不足时,这个暂停状态的Activity可能会被杀死。  停止状态: 当Activity完全不可见时,它就处于了停止状态,但仍然保留着当前状态和成员信息,当系统内存不足时,这个Activity就很容易被杀死。 Activity从一种状态变到另一种状态时会经过一系列Activity类的方法。常用的回调方法如下:  onCreate(Bundle savedInstanceState)——该方法在Activity的实例被Android系统创建后第一个被调用。通常在该方法中设置显示屏幕的布局、初始化数据、设置组件被单击的事件响应代码。  onStart()——在Activity可见时执行。  onReStart()——回到最上边的界面,再次可见时执行。  onResume()——Activity获取焦点时执行。  onPause()——Activity失去焦点时执行。  onStop()——用户不可见进入后台时执行。  onDestroy()——Activity销毁时执行。 为了更好地理解Activity的生命周期以及在Activity不同状态切换时所调用的方法,接下来将通过Google公司提供的一个Activity生命周期图来更生动地展示,如图34所示。 从图34中可以看出,Activity在从启动到关闭的过程中,会依次执行onCreate()→onStart()→onResume()→onPause()→onStop()→onDestroy()方法。如果进程被杀死,会重新执行onCreate()方法。 为了更好地掌握Activity的生命周期中方法的执行过程,接下来将通过具体的例子来展现方法的执行顺序。 先创建一个布局界面,其中包含一个按钮,用来跳转到另一个Activity中使用,布局代码如下所示: ?xml version="1.0" encoding="utf8"? LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.jxust.cn.chapter_shengtime.MainActivity" Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="跳转到第二个Activity" / /LinearLayout 图34Activity的生命周期图 第一个Activity的代码如下所示: public class MainActivity extends Activity { //Activity1创建时调用的方法 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("Activity1", "onCreate()"); Button button=(Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent=new Intent(MainActivity.this, SecondActivity.class); startActivity(intent); }}); } //Activity1可见时调用的方法 @Override protected void onStart() { super.onStart(); Log.i("Activity1", "onStart()"); } @Override protected void onRestart() { super.onRestart(); Log.i("Activity1", "onRestart()"); } //Activity1获取到焦点时调用的方法 @Override protected void onResume() { super.onResume(); Log.i("Activity1", "onResume()"); } //Activity1失去焦点时调用的方法 @Override protected void onPause() { super.onPause(); Log.i("Activity1", "onPause()"); } //Activity1不可见时调用的方法 @Override protected void onStop() { super.onStop(); Log.i("Activity1", "onStop()"); } //Activity1被销毁时调用的方法 @Override protected void onDestroy() { super.onDestroy(); Log.i("Activity1", "onDestroy()"); } } 图35Activity1界面 第二个Activity中的代码和使用的布局代码与第一个类似。 上面代码写好以后,在AndroidManifest.xml中注册创建的Activity。完成上述步骤以后,运行界面如图35所示。 上述代码中使用了Log来打印日志信息。第二个Activity中的Log.i的tag是Activity2,因而Logcat中选择设置package name为当前项目的包名。打印的Activity生命周期的执行方法顺序如图36所示。 图36Activity1的生命周期 图37Activity2界面 从图36中的日志信息可以看出启动Activity1依次执行了onCreate()、onStart()、onResume()方法,这是Activity从创建到可供用户操作的过程。 接下来单击第一个布局界面的按钮跳转到第二个Activity,出现如图37所示的界面。 与此同时,Log打印的日志信息如图38所示。 从图38的日志信息可以看出,当跳转到Activity2的时候,Activity1会失去焦点,然后执行onPause()方法,此时Activity2创建会一次执行onCreate()、onStart()、onResume()方法。这时候Activity1会执行onStop()方法。 图38Activity1跳转到Activity2的生命周期 接下来从第二个界面返回到第一个界面,此时Log打印的日志信息如图39所示。 从图39中的日志信息可以看出,从Activity2再次返回到Activity1时,Activity2会先执行onPause()方法,然后Activity1会依次执行onRestart()方法、onStart()方法、onResume()方法,随后Activity2执行onStop()方法和onDestroy()方法。如果退出应用程序,则Activity1会执行onStop()方法,然后执行onDestroy()方法。 图39Activity2跳转到Activity1的生命周期 视频讲解 3.1.4Activity中的单击事件 3.1.3节学习了Activity中的生命周期,可以看到,从第一个界面到第二个界面使用了按钮的单击事件。在Android中View的单击事件共有4种,接下来详细讲解这4种方式。 第一种为在布局文件中设置按钮onClick属性为其指定Activity中的方法,代码如下所示: !--布局文件中添加单击事件为其指定方法名-- android:onClick="click" Activity中的方法: public void click(View view){ Intent intent=new Intent(MainActivity.this,SecondActivity.class); startActivity(intent); } 第二种是创建内部类的方式,创建一个内部类实现OnClickListener接口并重写onClick()方法,在方法中写入单击事件的逻辑。这一种方法不常用,所以不做详细介绍。 第三种就是主类实现OnClickListener接口,然后重写onClick()方法,并通过switch()语句判断哪个按钮被单击。具体的代码如下所示: public class MainActivity extends Activity implements View.OnClickListener { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); register=(Button)findViewById(R.id.register); register.setOnClickListener(this); @Override public void onClick(View view) { switch (view.getId()){ case R.id.register: break; } } } } 这里需要注意的是“register.setOnClickListener(this);”方法中这个this代表的是该Activity的引用,由于Activity实现了OnClickListener接口,所以这里就代表了OnClickListener的引用,在方法中传入this,就代表该组件绑定了单击事件的接口。 第四种就是匿名内部类的方式,适合按钮比较少的情况下使用。这种方式可以直接创建OnClickListener的匿名内部类传入按钮的setOnClickListener()方法和参数中。具体的代码如下所示: public class MainActivity extends Activity { //Activity1创建时调用的方法 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("Activity1", "onCreate()"); Button button=(Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intentintent=new Intent(MainActivity.this,SecondActivity.class); startActivity(intent); } }); } } 以上就是单击事件的处理过程,后两种匿名内部类与主类中实现OnClickListener接口的方式在平常的Android开发中使用得较为普遍,所以需要熟练掌握。 3.2Intent的使用 3.2.1Intent浅析 在Android系统中,组件之间的通信需要使用到Intent。Intent中文翻译为“意图”,Intent最常用的是绑定应用程序组件,并在应用程序之间进行通信。它一般用于启动 Activity、服务、发送广播等,承担了Android应用程序三大核心组件之间的通信功能。 使用Intent开启Activity时,对应的方法为startActivity(Intent intent)和startActivityForResult(Intent intent); 开启Service时,常用的有 ComponentName startService(Intent intent)和boolean bindService(Intent service,ServiceConnection conn,int flags)。开启BroadcastReceiver方法有多种,就不一一列举了。 Android中使用Intent的方式有两种,分别为显式Intent和隐式Intent,接下来将在3.2.2节和3.2.3节详细介绍这两种方式。 3.2.2显式Intent 显式Intent就是在通过Intent启动Activity时,需要明确指定激活组件的名称,例如通过一个Activity启动另外一个Activity时,就可以通过这种方式。具体代码如下所示: //创建Intent对象,指定启动的类名SecondActivity Intent intent=new Intent(MainActivity.this,SecondActivity.class); //启动Activity startActivity(intent); 通过上述代码可以看出,使用显式Intent时,首先需要通过Intent的构造方法来创建Intent对象。构造方法有两个参数,分别为启动Activity的上下文和需要启动的Activity类名。除了通过指定类名的方式启动组件外,显式Intent还可以根据目标组件的包名、全路径来指定开启的组件。具体代码如下所示: //setClassName("包名","类的全路径名称"); intent.setClassName("com.jxust.cn", "com.jxust.cn.chapter_shengtime"); //启动Activity startActivity(intent); Activity类提供了startActivity(Intent intent)方法,该方法专门用于启动Activity,它接收一个Intent参数,然后通过将构建好的Intent参数传入startActivity()方法中来启动Activity。 使用上述两种方式启动Activity,能够在程序中很清晰地看到,其“意图”很明显,因此称为显式Intent。 视频讲解 3.2.3隐式Intent 在程序中没有明确指定需要启动的Activity,Android系统会根据在AndroidManifest.xml文件当中设置的动作(Action)、类别(Category)、数据(Uri和数据类型)来启动合适的组件。具体代码如下所示: activity android:name=".MainActivity" intent-filter !--设置action属性,根据name设置的值来指定启动的组件-- action android:name="android.intent.action.MAIN" / category android:name="android.intent.category.LAUNCHER" / /intent-filter /activity 在上述代码中,标签指定了当前Activity可以响应的动作为android.intent.action.MAIN,而标签则包含了一些类别信息,只有当这两者中的内容同时匹配时,Activity才会启动。使用隐式Intent启动Activity的具体代码如下: Intent intent=new Intent(); intent.setAction("android.intent.action.MAIN"); StartActivity(intent); 通过以上的学习,已经初步了解了显式Intent和隐式Intent的使用。显式Intent启动组件时必须要指定组件的名称,一般只在本应用程序切换组件时使用。而隐式Intent使用的范围更广,不仅可以启动本应用程序内的组件,还可以开启其他应用的组件,如打开系统的照相机、图库等。 3.3Activity中的数据传递方式 在Android开发中,经常需要在Activity中进行数据传递,这里就需要使用3.2节讲到的Intent来实现Activity之间数据的传递。 使用Intent进行数据传递时只需要调用putExtra()方法把数据存储进去即可。这个方法有两个参数,是一种“键值对”的形式,第一个参数为key,第二个参数为value。实现传递参数的具体代码如下: //定义字符串变量存储一个值 String str="android"; Intent intent=new Intent(this, SecondActivity.class); //传递参数 intent.putExtra("receive_str", str); startActivity(intent); 上述代码中将一个字符串变量str传递到SecondActivity中,然后需要在SecondActivity中接收这个参数。具体代码如下所示: Intent intent=this.getIntent(); String receive_str=intent.getStringExtra("receive_st"); 上面就是通过Intent传递和接收参数的一种简单方式,如果需要传递的参数比较多,就需要使用putExtras()方法传递数据,该方法传递的是Bundle对象。具体代码如下所示: Intent intent=new Intent(this, SecondActivity.class); Bundle bundle=new Bundle(); bundle.putString("phone", "123456"); bundle.putString("sex", "男"); bundle.putString("age", "18"); intent.putExtras(bundle); startActivity(intent); 上述代码使用Bundle对象传递参数,在SecondActivity中取出这些参数的具体代码如下所示: Intent intent=this.getIntent(); Bundle bundle=intent.getExtras(); String phone=bundle.getString("phone"); 在上述代码中,在接收Bundle对象封装的数据时,需要先接收对应的Bundle对象,然后再根据key取出value。接下来将在3.4节讲解如何使用在布局文件中定义的各个组件以及如何进行数据的传递并显示。 视频讲解 3.4用户注册案例功能实现 本节的用户注册布局与第2章的用户注册案例布局是一样的,本节主要讲的是如何使用布局中定义的组件以及数据传递与接收。 (1) Activity1布局代码与第2章布局代码一样。 (2) Activity1代码如下所示: public class MainActivity extends Activity implements View.OnClickListener, RadioGroup.OnCheckedChangeListener{ //定义字符串用来保存各个信息 private String phone_str=""; private String paswd_str=""; //默认为选中"男性" private String sex_str="男性"; private String hobby_str="1"; private String city_str=""; //组件定义 EditText phone_edit, paswd_edit; RadioGroup sex_group; RadioButton nan_but, nv_but; CheckBox play, read, music; Button register; Spinner spinner; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //组件初始化 phone_edit=(EditText)findViewById(R.id.phone); paswd_edit=(EditText)findViewById(R.id.paswd); sex_group=(RadioGroup)findViewById(R.id.sex); //添加监听事件 sex_group.setOnCheckedChangeListener(this); nan_but=(RadioButton)findViewById(R.id.nan); read=(CheckBox)findViewById(R.id.read_book); play=(CheckBox)findViewById(R.id.play_ball); music=(CheckBox)findViewById(R.id.music); register=(Button)findViewById(R.id.register); //添加监听事件 register.setOnClickListener(this); spinner=(Spinner)findViewById(R.id.spinner); final String[] city=new String[]{"北京", "上海", "武汉", "南京", "南昌", "信阳"}; ArrayAdapterStringadapter=newArrayAdapterString(this, android.R.layout.simple_list_item_1, city); spinner.setAdapter(adapter); //在"城市"下拉列表添加监听事件 spinner.setOnItemSelectedListener(new Spinner.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView? adapterView, View view, int i, long l) { city_str=city[i]; } @Override public void onNothingSelected(AdapterView? adapterView) { } }); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.register: //获取手机号和密码 phone_str=phone_edit.getText().toString(); paswd_str=paswd_edit.getText().toString(); //获取兴趣爱好即复选框的值 hobby_str=""; //清除上一次已经选中的选项 if(read.isChecked()){ hobby_str+=read.getText().toString(); }if (play.isChecked()){ hobby_str+=play.getText().toString(); }if(music.isChecked()){ hobby_str+=music.getText().toString(); } Intent intent=new Intent(this,SecondActivity.class); Bundle bundle=new Bundle(); bundle.putString("phone", phone_str); bundle.putString("paswd", paswd_str); bundle.putString("sex", sex_str); bundle.putString("hobby", hobby_str); bundle.putString("city", city_str); intent.putExtras(bundle); startActivity(intent); break; } } @Override public void onCheckedChanged(RadioGroup radioGroup, @IdRes int i) { //根据用户选择来改变sex_str的值 sex_str=i==R.id.nan? "男性":"女性"; } } 上述代码中主要是对main_activity_layout.xml中的组件进行初始化以及添加监听事件,然后对选择的数据进行传递。 (3) SecondActivity负责接收数据并显示出来。在布局文件second_lay.xml中定义一个TextView负责显示数据。代码如下所示: ?xml version="1.0" encoding="utf8"? LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" TextView android:id="@+id/show_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="TextView" / /LinearLayout (4) SecondActivity的代码如下所示: public class SecondActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.second_layout); Intent intent=this.getIntent(); Bundle bundle=intent.getExtras(); String phone=bundle.getString("phone"); String paswd=bundle.getString("paswd"); String sex=bundle.getString("sex"); String hobby=bundle.getString("hobby"); String city=bundle.getString("city"); TextView show_text=(TextView)findViewById(R.id.show_content); show_text.setText("手机号为:"+phone+"\n"+"密码为:"+paswd+"\n"+"性别是:"+ sex+"\n"+"爱好是:"+hobby+"\n"+"城市是:"+city); } } (5) 在AndroidManifest.xml文件中注册SecondActivity,代码如下所示: activity android:name=".SecondActivity"/activity 上述代码编写完成以后,接下来输入手机号、密码,选择性别、爱好、城市,然后单击“注册”按钮,跳转到接收数据的界面。注册界面和接收数据界面分别如图310和图311所示。 图310注册界面 图311接收数据界面 以上就是用户注册的详细介绍,这里包含了组件的使用、数据的传递和接收、按钮的单击事件等,这些都是进行Android开发的基本知识,需要熟练掌握和应用。 3.5本章小结 本章首先讲解Activity的基本知识,包含从Activity的概念、生命周期,到后面的从一个Activity跳转到另外一个Activity生命周期中方法的执行过程; 然后讲解Intent的使用以及数据的传递和接收; 最后结合一个用户注册的实例讲解组件的使用以及监听事件的处理,这些都需要开发者熟练掌握。 3.6课后习题 1. 简述一个Activity跳转到另一个Activity时,两个Activity生命周期方法的执行过程。 2. 编写一个程序,要求在第一个界面中输入两个数字,在第二个界面中显示第一个界面中输入的两个数字的和、差、积、商。