作为Android的四大组件之一,活动最先走进我们的视野,其重要性不言而喻,今天就抽出时间来专门对Android活动一探究竟。
什么是活动
活动即Activity,是一种可以包含用户界面的组件,Android系统中所有我们能看到的内容都在活动里,它主要用于和用户进行交互。一个应用程序可以包含零个或多个活动,活动与活动之间可以通过Intent来相互联系,当一个应用程序中至少有一个活动时,那么就一定存在一个主活动,它就是我们刚打开应用程序时所进入到的那个界面,可以在AndroidManifest.xml中进行设置。
创建活动的步骤
- 1.新建一个活动类,继承自Activity。
- 2.重写onCreate方法,并在其中通过setContentView()来加载布局文件。
- 布局文件在app/src/main/res/layout中,文件后缀为.xml。
- 如果要在xml文件中定义一个id,则使用@+id/id_name语法;如果要在xml文件中引用一个id,则使用@id/id_name语法。 - 3.活动注册,所有活动必须在AndroidManifest.xml中注册,给主活动指定的label不仅会成为标题栏中的内容,还会成为启动器(Launcher)中应用程序显示的名称。在<activity>标签的内部我们加入了<intent-filter>标签,并在这个标签里添加了
<actionandroid:name="android.intent.action.MAIN"/> <category android:name ="android.intent.category.LAUNCHER" />
这两句声明,这样这个活动将成为主活动,在点击桌面应用程序的时候首先打开的就是这个活动。如果程序中没有声明任何主活动,程序仍然正常安装,但是无法再启动器中看到或者打开该程序。
隐藏标题栏
有时为了全屏显示活动或者显示开发者自定义的标题栏,需要在活动中不显示标题栏,这时需要如下代码
requestWindowFeature(Window.FEATURE_NO_TITLE)
这句代码一定要在setContentView()之前执行,不然会报错。
在活动中使用Toast
Toast译为吐司,是一种简单灵活的提醒方式,可以把一些短小的消息通知给用户,并在一段时间后消失,并且不会占据任何屏幕空间。
Toast.makeText(KUActivity.this,"在线功能稍后上线,敬请期待!", Toast.LENGTH_LONG).show();
其中,第一个参数Context,第二个参数要提示的内容,第三个参数为显示Toast的时间长短,其中LENGTH_LONG(长)和LENGTH_SHORT(短),记得别忘了调用show()方法来将Toast显示出来。
在活动中使用Menu
- 1.在res中新建menu文件夹:res-->new-->Directory,输入文件名menu,然后就可以在menu文件夹下新建菜单文件了,后缀为.xml。
- 2.菜单文件内容格式参考如下:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_refresh"
app:showAsAction="always"
android:title="@string/refresh"/>
</menu> - 3.在活动中重写onCreateOptionsMenu()方法。
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);//给当前活动创建菜单,inflate方法第一个参数通过哪个资源文件来创建菜单,第二个参数指定我们的菜单项将添加到哪一个Menu对象中
return true;//允许创建的菜单显示出来
} - 4.响应菜单事件:重写onOptionsItemSelected()方法。
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId())
{
case R.id.menu_refresh:
{
//如果该菜单项被选中,则在此执行业务逻辑
}
break;
case ....
} - 注:标题栏的右侧有个三点符号,这个就是菜单按钮,点击该按钮们就能看到所有的菜单项了,当点击某菜单项时,会回调到onOptionsItemSelectd()方法中。
销毁活动
- Activity中提供了一个finish()方法,在活动中调用该方法即可销毁当前活动。
Intent启动活动
Intent是android程序中各个组件之间进行交互的重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动、启动服务以及发送广播等场景。Intent一般分为显式Intent和隐式Intent。
- 显式Intent
(1)Intent有多个构造方法,其中最常接触的是
Intent(Context packageContext, Class<?>cls)
这个构造函数接收两个参数,第一个参数Context要求提供一个启动活动的上下文Context,第二个参数Class则是指定想要启动的目标活动。
(2)Activity类中提供了一个startActivity()方法,这个方法是专门用于启动活动的,它接收一个Intent参数。
(3)显式Intent的示例如下:
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//在FirstActivity上启动SecondActivity
intent.putExtra("path",path);//把路径path保存到“path”中,传递给SecondActivity
startActivity(intent);//启动活动SecondActivity - 隐式Intent
隐式Intent并不明确指出我们想要启动哪一个活动,而是指定了一系列更为抽象的action(动作)和category(种类)等信息。只有<activity>的<intent-filter>下的<action>和<category>中的内容能够同时匹配上Intent中指定的action和category时,这个活动才能响应该Intent。
android.intent.category.DEFAULT是一种默认的category,在调用startActivity()方法的时候会自动将这个category添加到Intent中。每个Intent中只能指定一个action,但却能指定多个category。
隐式Intent的用法示例:
(1)在FirstActivity的<activity></activity>中添加action和category。
<activity
android:name=".FirstActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.activitytest.MY_CATEGORY" />
</intent-filter>
</activity>
(2)在SecondActivity活动中,指定要启动的action和category。
locatVideoBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new
Intent("com.example.activitytest.ACTION_START");
intent.add("com.example.activitytest.MY_CATEGORY");
startActivity(intent);
}
});//由于FirstActivity的action和category能和SecondActivity中Intent中指定的一样,因此即可在SecondActivity中启动FirstActivity
(3)使用隐式Intent,我们不仅可以启动自己程序内的活动,还可以启动其他程序的活动,这使得Android多个应用程序之间的功能共享成为了可能。例如,如果我想在应用程序中展示一个百度网页,则只需要调用系统的浏览器来打开网页即可:
Btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);//Android系统内置动作
intent.setData(Uri.parse("http://www.baidu.com"));//将网址解析成一个Uri对象并将其传递到Intent中
startActivity(intent);
}
});
Intent传递数据
- 向下一个活动传递数据
Intent中提供了一系列putExtra()方法的重载,可以把我们想要传递的数据暂存在Intent中,启动了另一个活动后,只需要把这些数据再从Intent中取出就可以了。
(1)在启动下一个活动时存储数据
Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String data = "hello";
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);
startActivity(intent);
}
});
(2)在下一个活动中取出数据
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");//取出数据
Log.d("SecondActivity",data);
} - 返回数据给上一个活动
Activity中还有一个startActivityForResult(intent, 1)方法也是用于启动活动的,但这个方法期望在活动销毁的时候能够返回一个结果给上一个活动。请求码只要是一个唯一值就可以了,这里传入了1。
(1)在FirstActivity中使用startActivityForResult()方法来启动SecondActivity。
Btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);
}
});
(2)在SecondActivity中通过Intent将要返回给FirstActivity的数据保存起来。
Btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("return_data",“hello FirstActivity”);
setResult(RESULT_OK,intent);
finish();//销毁当前活动SecondActivity
}
});
setResult()方法专门用于向上一个活动返回数据,它接收两个参数,第一个参数用于向上一个活动返回处理结果,一般只使用RESULT_OK或RESULT_CANCELED这两个值,第二个参数则是把带有数据的Intent传递回去。
(3)当SecondActivity被销毁之后会回调上一个活动的onActivityResult(),因此在FirstActivity中重写该方法。
@Override
protceted void onActivityResult(int requestCode,int
resultCode,Intent data){
switch(requestCode){
case 1:
if(resultCode==RESULT_OK){
String resultData = data.getStringExtra("return_data");
Log.d("FirstActivity",resultData);
}
}
}
(4)如果用户在SecondActivity中没有通过点击按钮而是通过Back键返回到了FirstActivity,则我们可以通过重写onBackPressed()方法来解决这个问题,即将返回数据的代码放在此方法中即可。
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("return_data",“hello FirstActivity”);
setResult(RESULT_OK,intent);
finish();//销毁当前活动SecondActivity
}
活动的生命周期
在了解活动的生命周期之前,我们还需要再弄清楚两部分概念:返回栈和活动的状态。
- 返回栈
Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动Activity的集合,这个栈也被称作返回栈(BackStack)。在返回栈中,活动是按照后进先出的规则进出栈的,每当我们按下Back键或调用finish()方法去销毁一个活动时,处于栈顶的活动会出栈。
从上图可看出,当从Activity1中启动Activity2时,Activity1被推到栈底,Activity2变成栈顶,同理,Activity3进栈的过程和Activity2是一样的,如果我们按back按钮,则Activity3被弹出,Activity2则被推到栈顶,以此类推。当一直按back,返回到主页面,则所有的activity被全部弹出,则task不复存在。
Two tasks.png
其实,task有两种状态:Foreground前景和Background背景。当处于Background时,所有的activity都是停止的,当处于Foreground时,则其栈顶的Activity可与用户进行交互。因此Android是一个多任务的系统,不同任务是可以被互相切换的。
- 活动状态
每个活动在其生命周期中最多可能会有四种状态:
1)运行状态:当一个活动位于返回栈的栈顶时,活动处于运行状态
- 暂停状态:当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态,处于暂停状态的活动仍然是完全存活着的
停止状态:当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。
销毁状态:当一个活动从返回栈中移除后就变成了销毁状态
- 活动的生存期
完整生存期:活动在onCreate()方法和onDestroy()方法之间所经历的,就是完整生存期
可见生存期:活动在onStart()方法和onStop()方法之间所经历的,就是可见生存期。在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互
前台生存期:活动在onResume()方法和onPause()方法之间所经历的,就是前台生存期。在前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行相互的
- 活动的生命周期
- 活动被回收前保存临时数据
试想,活动A启动了活动B,假设活动B全部覆盖了活动A,此时的活动A处于停止状态,若此时系统内存不足将活动A回收了,那么当我们按Back键回到活动A时,此时活动A肯定是要执行onCreate的方法的,这也就意味着之前活动A的数据肯定已经找不到了,那么如何来解决这个问题呢?
其实,Activity中还提供了一个onSaveInstanceState()回调方法,这个方法会保证一定在活动被回收之前调用,因此我们可以通过这个方法来解决活动被回收时临时数据得不到保存到的问题。
(1)通过onSaveInstanceState(Bundle outState)的Bundle参数保存临时数据。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState)
String tempData = "something";
outState.putString("data_key",tempData);
}
(2)在onCreate(Bundle saveInstanceState)方法中,将数据从Bundle参数中取出。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(saveInstanceState != null){
String tempData = savedInstanceState.getString("data_key");
Log.d("MainActivity",tempData);
}
}
(3) Intent还可以结合Bundle一起用于传递数据的,首先可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标活动之后先从Intent中取出Bundle,再从Bundle中一一取出数据。
活动的启动模式
活动的启动模式一共有四种,分别是standard、singleTop、singleTask、singleInstance,可以在AndroidManifest.xml中通过<activity>标签android:launchMode属性来选择启动模式。
-
standard
活动默认的启动模式,在该模式下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
-
singleTop
在该模式下,在启动活动时如果返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例,但如果发现栈顶不是该活动,则创建该活动的新的实例,因此在返回栈中可能会存在该活动的多个实例。
-
singleTask
每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例,因此在返回栈中只会存在该活动的一个实例。
-
singleInstance
指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈),这样做就解决了共享活动实例的问题,因为不管是哪个应用程序来访问这个活动,都共用的是同一个返回栈。
List的一点小疑惑
我们在活动中会经常看到这句代码List list = new ArrayList()
?
List是一个接口,而ListArray是一个类,ListArray继承并实现了List。所以List不能被构造,但可以向上面那样为List创建一个引用,而ListArray则可以被构造。
那为什么不是List list = new ArrayList()
?
因为List有多个实现类,可能现在我们用的是ArrayList,也许哪一天就需要换成其它的实现类,如LinkedList或者Vector等等,因此生成List引用会更好一点。
知晓当前在哪个活动
新建一个BaseActivity extends AppCompatActivity,但不需要在AndroidManifest.xml中注册,让其成为所有活动的父类
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleNmae());//获取当前实例的类名,并通过Log打印了出来
}
随时随地退出程序
新建一个ActivityCollector类做为活动管理器,在其中通过一个List来暂存活动,然后提供了一个addActivity()方法用于向List中添加一个活动,提供了一个removeActivity()方法用于从List中移除活动,最后提供了一个finishAll()方法用于将List中存储的活动全部都销毁掉,无论在哪个地方想退出程序,只需要调用ActivityCollector.finishAll()方法就可以了。
活动的最佳启动方法
在一个团队完成一项工程时,为了方便队友启动我们编写的活动界面,可以在我们编写的活动中新添加一个actionStart()方法,这样队友通过该方法就知道启动该活动所需要的参数类型,从而很方便地在他的活动界面启动我们编写的活动。如需要在FirstActivity中启动SecondActivity,则代码如下
-
在SecondActivity中添加了一个actionStart()方法
publicstatic void actionStart(Context context, String data1, String data2) { Intentintent = new Intent(context, SecondActivity.class); intent.putExtra("param1",data1); intent.putExtra("param2",data2); context.startActivity(intent); }
-
在FristActivity中调用该方法启动SecondActivity
SecondActivity.actionStart(FirstActivity.this,"data1", "data2");
网友评论