四大组件之——活动Activity
作为安卓四大组件之一,活动是一种包含用户应用界面的组件,可用于和用户交互
一.手工创建活动
(1)创建一个project,name自己起,比如activitytest
(2)java/com.example.activitytest,右键单击,出现New->Activity->EmptyActivity,弹出对话框,将活动命名,比如FirstActivity,此时不要勾选Generate Layout File(自动产生布局文件)和 Launcher Activivty(将当前活动创建为主活动)这两个控件,勾选Backwards Compatibility会向下兼容
(3)创建和加载布局res->New->Directory,创建layout目录,然后再在此目录下右键layout resource file,创建一个自己的布局文件,用setContentView(R.layout.layoutid)把布局文件传入FirstActivity的onCreate方法中
(4)在AndroidManifest中注册活动,采用标签在application中完成
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme" >
<activity
android:name=".NoAuto"
android:label="This is my actrivity">
<intent-filter>//配置主活动
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
在活动中使用Toast的步骤
Toast是一种交互消息提醒方式,通知短小信息,显示一段时间消失,不占用屏幕空间
使用时一般在需要提示信息的按钮上增加点击事件
Button button=(Button)findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"hhhh",Toast.LENGTH_SHORT).show();
}
});
makeText函数通过传入参数创建一个对象,第一个参数Context,toast所要求的上下文,可以是本身这个活动,第二个参数是要显示的内容,第三个参数是显示时间Toast.LENGTH_SHORT,Toast.LENGTH_LONG两种形式可选,一短一长。
在活动中使用菜单的步骤
菜单的使用可以节省屏幕空间
(1)res目录下新建一个Menu文件,res->New->Dictionary->menu,然后menu->new->menu resource file->新建的menu文件名字,这里我写了main
(2)在此文件中加入menu的一些参数
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add_item"
android:title="Add"></item>
<item
android:id="@+id/remove_item"
android:title="Remove"></item>
</menu>
id是唯一标识符号,title是用于显示的内容,还有icon,还有,里面包裹item使用可以实现一个分组,这个显示出来和只用item没啥区别,主要是可以对这个分组进行一个组内集中处理
(3)继续操作MainActivity,进行重写onCreateOptionsMenu()方法
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
getMenuInflater()获取MenuInflater对象,再调用inflate()方法就可以给当前活动创建菜单了,这个方法传入两个参数,一个是创建菜单的资源文件,另一个用来指定我们创建的菜单项将加入到哪一个资源文件中,直接使用传入的menu即可,返回值true表示可以将菜单显示出来,false表示不显示
(4)为每个item添加响应事件
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.add_item:
Toast.makeText(this,"You click add",Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this,"You click Remove",Toast.LENGTH_SHORT).show();
break;
default:break;
}
return true;
}
item.getItemId()判断点击是哪一个菜单项,然后给每个菜单项加入相应处理事件
二.Intent穿叉活动
intent是安卓各大组件交互的一种方式,不仅可以指明当前活动所要执行的动作,还可以在不同组件之间传递数据。一般被用作启动活动,服务,发送广播。
1.活动之间跳转
(1)显式Intent
Intent有多个构造函数的重载,其中一个是Intent(Context packageContext,Class<?> cls),第一个参数是要求提供一个启动活动的上下文,第二个活动表示想要启动的目标活动,这个构造函数就实现了构造意图。使用它要运用Activity中的startActivity()方法,他接受一个启动对象,将构造好的intent传入此方法中,就可以启动活动了。这种启动方式,活动意图明显,故称为显式启动。具体实现
首先在MainActivity同级目录下创建一个Main2Activity活动,具体做法包名->New->Activity->Empty Acivity->Main2Activity(活动名称),此时系统会自动创建他的布局文件注册等。为了简单,两个活动的布局文件都用一个按钮替换。然后修改MainActivity中代码如下,即给button1一个按钮响应事件
package com.example.tingyu.myactivity;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button button1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1=(Button)findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(MainActivity.this,Main2Activity.class);
startActivity(intent);
}
});
}
}
当想回到上一个活动,只需要按back键。
(2)隐式Intent
相比显式Intent,多用一些抽象的catagory和action等信息,交给系统去分析这个Intent进而确定启动合适的活动。
首先在注册文件中添加如下代码
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
标签中明确指出当前活动可以响应com.example.activitytest.ACTION_START这个action,而则精确指明了当前活动能响应的Intent还可能带有的catagory,只有和同时匹配上述的action和catagory,这个活动才能响应此Intent。
在MainActivity中响应事件改为如下:
每个Intent只能指定一个action,但是却可以有多个category,如果注册文件中不含有对应catagory,就会报错,例如,将注释下面的语句与注释语句换位,这时可以在注册文件中添加如下语句
<category android:name="com.example.activitytest.MY_CATEGORY"/>
public void onClick(View v) {
Intent intent=new Intent("com.example.activitytest.ACTION_START");
//intent.addCategory("android.intent.category.DEFAULT");
intent.addCategory("com.example.activitytest.MY_CATEGORY");
startActivity(intent);
}
});
}
更多隐式Intent用法
除了可以启动相同项目下的活动,还可以启动其他程序的活动,例如
public void onClick(View v) {
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
}
首先指定Intent的action是Intent.ACTION_VIEW,这是一个系统内置的action,然后通过Uri.parse()方法解析成一uri对象,再调用Intent的setData()方法将这个uri对象传进去。我们还可以在intent-filter标签再配置标签,更精确指明当前活动应该响应什么类型的数据。但data标签一般不宜携带过多的数据,对于上述浏览器,只需指定<Data android:scheme=“http”/>就可以相应http中的协议了
![1534250454587.png](https://i.loli.net/2018/08/14/5b72cdf7f2db2.png)
2.活动间数据传递
向下一个活动传递数据
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String data="hello main2Activity";
Intent intent=new Intent(MainActivity.this,Main2Activity.class);
intent.putExtra("Extra",data);
startActivity(intent);
}
});
}
putExtra()方法第一个参数是键,第二个参数是真正要传递的值
第二个活动用来接收参数
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Intent intent=getIntent();
String data=intent.getStringExtra("Extra");
TextView textView=(TextView)findViewById(R.id.text1);
textView.setText(data);
}
先用getIntent()来接收启动本活动的intent,然后调用getStringExtra()方法,传入相应的键值,就可以得到传递的数据了,还可以有getBooleanExtra() getIntExtra()根据传入的值不同。
返回数据给上一个活动
只需要按back键就行了
(1)在第一个活动中加如下代码
Intent intent=new Intent(MainActivity.this,Main2Activity.class);
startActivityForResult(intent,1);
在活动销毁时返回数据给上一个活动 startActivityForResult()方法,,接收两个参数,第一个是Intent,第二个参数是一个唯一标识的状态码,这里写成了1
(2)接下来给第二个活动给按钮增加点击事件
Button button=(Button)findViewById(R.id.button2);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent();
intent.putExtra("data_return","helloActivity");
setResult(RESULT_OK,intent);
finish();
}
});
}
构造一个空的Intent,专门用来传递值,然后putExtra()方法传递,接下来setResult()方法有两个参数,第一个是向上一个活动返回处理结果,RESULT_OK和RESULT_CANCELED两种,第二个是带有数据的intent,然后调用finish()方法销毁活动
(3)在第一个活动中重写onActivityResult()方法
由于startActivityForResult()方法来启动第二个活动,第二个活动被销毁后会回调上一个活动的onActivityResult()方法,因此需要在第一个活动中重写这个方法来得到返回的数据。
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
switch(requestCode){
case 1:
if(resultCode==RESULT_OK){
String returnedData=data.getStringExtra("data_return");
Log.d("FirstActivity",returnedData);
}
break;
default:
}
}
onActivityResult()一共有三个参数,第一个参数requestCode是我们启动活动时传入的请求码,第二个参数resultCode是返回数据时传入的处理结果,第三个参数是携带着返回数据的Intent
因为一个活动可能调用startActivityForResult()方法启动很多不同活动,所以要先通过requestCode来判断是不是要找的活动;然后再通过resultCode判断数据处理结果是不是成功的
这样打开程序,按下活动2的按钮就可以回到活动1并返回值,但是如果是按back键,而不是点击按钮,就不能返回值给活动1了,这个时候可以通过重写onBackPressed()方法解决这个问题。
@Override
public void onBackPressed() {
Intent intent=new Intent();
intent.putExtra("data_return","helloActivity");
setResult(RESULT_OK,intent);
finish();
}
就和上面的按钮代码一样就行。
三.活动的生命周期
1.活动状态
每个活动在其生命周期会出现四种状态:
(1)运行状态
返回栈栈顶位置,最不愿回收的状态
(2)暂停状态
不在栈顶,但仍然可见,一般不愿回收,除非内存极低
(3)停止状态
不在栈顶,也不可见,系统仍然为其保存状态和成员变量,但不可靠,内存低立马收回
(4)销毁状态
不在返回栈中,系统最愿回收
执行finish()方法可以手动销毁活动
2.活动生存期
七个回调方法贯穿活动生命周期始终
onCreate(),活动第一次创建时使用,初始化
onStart(),活动由不可见变为可见
onResume(),活动处于栈顶准备好和用户交互时使用,此时活动一定处于返回栈栈顶,并且处于运行状态
onPause(),活动准备去启动或恢复另一个活动时启用,方法要快,不然影响新栈顶活动的使用。
onStop(),活动完全不可见时启用,与onPause区别在于当启动一个对话框的活动(不在栈顶,仍然可见,在注册文件中样式采用Dialog,即标签中 添加android:theme="@style/Theme.AppCompat.Dialog",系统有很多主题,也可以定制自己的主题),使用onPause而不是onStop
onDestory(),活动被销毁的时候调用,释放内存
onRestart(),活动由停止变为运行状态调用,活动重启
(1)完整生存期 onCreate()->onDestory()
(2)可见生存期onStart()->onStop()
(3)前台生存期onResume()->onPause()
onSaveInstanceState()方法:
用于保存一些临时数据,可以保证系统回收活动之前该活动一定会被调用,因此可以解决活动被回收时候临时数据不能得到保存的问题。onSaveInstanceState()方法携带一个Bundle类型的参数,Bundle提供了一系列方法用来保存参数,比如putString(),putInt()等等,每个方法都有两个参数,第一个是键,第二个是值
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String tempData="Something you just typed";
outState.putString("data_key",tempData);
}
恢复数据使用onCreate()方法,它平常Bundle带有的是空的值,但是如果有调用onSaveInstanceState()方法保存数据,它就可以一把值全部传递过去
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"----onCreate");
setContentView(R.layout.activity_main);
if(savedInstanceState!=null){
String tempData=savedInstanceState.getString("data_key");
Log.d(TAG,tempData);
}
}
Bundle和Intent可以结合使用,存取数据,先放在Bundle里,然后放在Intent里,到了取出时候先取出Intent的Bundle,再一一取出Bundle中的数据
四.活动的四种启动模式
(1)standard
系统默认的启动模式,每次启动,不管栈顶是否有活动,都创建一个该活动的实例,并把他放到返回栈的栈顶
(2)singleTop
对于栈顶已经有的活动,不再创建该活动的实例,直接使用
(3)singleTask
查找返回栈,里面有要用的活动,直接返回,把此活动上面的所有活动出栈
(4)singleInstance
为其启用一个新的返回栈来管理活动,(每一应用程序有一个返回栈)从而实现多个应用程序可以共享此活动的实例
通过更改注册文件中的android:launchMode 属性可以选择不同的启动模式·
五.活动的最佳实践
1.知晓当前是哪一个活动
新建一个普通类,就在活动的相同目录下,他继承AppCompatActivity类,并重写onCreate()方法,打印内容,让所有活动都继承他,因为所有活动间接继承 AppCompatActivity,所以正常,不受影响
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
}
}
可以自己定义三个活动,用按钮点击事件进行跳转,然后观察日志文件就可以了。
2.随时随地退出程序
这里退出程序主要是针对返回栈过多,所以需要多次按back键,这是可以加一个对所有活动的统一管理,然后在需要退出所有活动的时候进行方法的调用
新建一个类ActivityCollector 作为活动的管理者,将每个活动存放在链表中
public class ActivityCollector {
public static List<Activity> activities=new ArrayList<>();
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();
}
}
activities.clear();
}
}
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
重写BaseActivity的onDestroy()方法,其实相当于自己构造了一个返回栈。然后当想退出程序的时候,只需要在需要的地方调用 ActivityCollector.finishAll()方法就可以了 。
也可以杀死当前进程以确保程序完全退出,调用android.os.Process.killProcess(android.os.process.myPid());
killProcess接受一个进程的id,杀死当前进程,不能杀死其他进程,myPid()方法可以获取到当前进程id。
3.活动的启动最佳方法
对比直接在点击事件里写Intent跳转逻辑,这样写更利于维护
public static void actionStart(Context context,String data1,String data2){
Intent intent=new Intent(context,ThirdActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
context.startActivity(intent);
}
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SecondActivity.actionStart(SecondActivity.this,"data1","data2");//替代写法
// Intent intent=new Intent(SecondActivity.this,ThirdActivity.class);
// intent.putExtra("param1","data1");
// intent.putExtra("param2","data2");
// startActivity(intent);之前的写法
}
});
参考:郭霖《第一行代码》第二版