2.活动(Activity)
2.1 概念
活动是Android的基本组成单位,每个Activity都被定义为一个单独的类,且都继承自基类:android.app.Activity;它是一种包含用户界面的组件,主要用来与用户进行交互(通俗点讲,可以先理解为一个单独的屏幕、一个交互的窗口,但不完全是这样)。
2.2 基本使用
2.2.1 手动创建活动
新建一个类FirstActivity,并继承自基类Activity,重写Activity的onCreate()方法(项目中的任何活动都要重写此方法)。
public class FirstActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
2.2.2 创建加载布局
Android应用讲究逻辑和视图分离,所以最好一个Activity对应一个XML布局文件。
创建activity_first.xml文件(在res-->layout目录下),并添加一个按钮:
<?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"
android:orientation="vertical">
<!--
android:id==>>给当前的元素定义一个唯一标识符,可在代码中调用。
@+id/btn_1==>>需要引用一个id,则使用@id/id_name这种语法;需要定义一个id,则使用@+id/id_name这种语法,其他资源一致。
android:layout_width==>>指定当前元素宽度,同理height为高度
android:text==>>指定元素显示的文字内容,其实文字应该放在字符串文件中并在这里引用。
-->
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:id="@+id/btn_1" />
</LinearLayout>
在代码中调用此视图显示到activity中:
setContentView(R.layout.activity_first);
这里我们使用自己的R文件,AndroidSDK还会提供一个android包下的R文件,不要弄错。2.2.3 在Androidmanifest.xml中注册
所有的活动都要在AndroidManifest.xml中进行注册才能生效:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jastar.activitytest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--
1.所有活动都需通过activity标签来注册,且放在application标签中
2.android:name:指定注册哪一个活动;.FirstActivity是缩写,因为包名在manifest标签中已经定义了
3.android:label:活动标题栏的内容,显示在最顶部
4.给主活动指定的label还会成为启动器(Launcher)中应用程序显示的名称
-->
<activity android:name=".FirstActivity" android:label="This is the firstActivity">
<intent-filter>
<!--让活动成为主活动(main入口)就要声明这两句话-->
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2.2.4 隐藏标题栏
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//隐藏标题栏,一定要写在setContentView方法前面
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_first);
}
注意:
使用AS创建的activity隐藏标题栏时无效,是因为AS默认继承的是AppCompatActivity类,换成Activity类即可。
2.2.5 使用Toast悬浮提示
Toast是Android系统提供的提示方式,可将信息展示给用户,并在一段时间后消失。
可给按钮添加点击事件,并提示:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//隐藏标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_first);
/**
* 1.通过findViewById()来获得界面上的元素,返回View对象,并向下转型
* 2.通过实现OnClicklistener接口的onClick()来给按钮添加点击事件
*/
Button btn = (Button) findViewById(R.id.btn_1);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
* 别忘了调用.show()方法
* param1:Context上下文,由于活动本身就是,所以传入活动对象即可
* param2:提示内容
* param3:提示显示时长,还有LENGTH_LONG
*/
Toast.makeText(FirstActivity.this, "You click the button!", Toast.LENGTH_SHORT).show();
}
});
}
2.2.6 使用menu菜单
(1)在res下新建menu文件夹
(2)在menu下新建一个名为main的菜单文件(在AS中直接new一个Menu resource file)
(3)AS创建之后默认是一个空的menu标签,需要添加菜单项代码:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/item_add"
android:title="Add" />
<item
android:id="@+id/item_remove"
android:title="Remove" />
</menu>
(4)在活动中重写onCreateOptionsMenu()使得菜单展示
/**
* 重写展示菜单的方法
* @param menu
* @return
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
/**
* 通过此方式给当前活动创建菜单
* param1:指定哪个资源文件创建菜单
* param2:指定菜单项将添加到哪个菜单中,直接使用方法中传入的参数即可
* return:true允许菜单展示,false则不显示
*/
getMenuInflater().inflate(R.menu.main,menu);
return true;
}
(5)在活动中重写onOptionsItemSelected()来响应菜单点击事件
/**
* 重写响应菜单点击事件的方法
*
* @param item
* @return
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//item.getItemId()用来获取选中菜单的id
switch (item.getItemId()) {
case R.id.item_add:
Toast.makeText(FirstActivity.this, "You click add menu!", Toast.LENGTH_SHORT).show();
break;
case R.id.item_remove:
Toast.makeText(FirstActivity.this, "You click remove menu!", Toast.LENGTH_SHORT).show();
break;
}
return true;
}
2.2.7 销毁活动
返回键可以销毁;当然代码也能:finish();即可
2.3 意图(Intent)
2.3.1 概念
Intent 是 Android 程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent 一般可被用于启动活动、启动服务、以及发送广播等场景。
Intent的用法大概分为两种:显式意图和隐式意图。
2.3.2 显式意图
同样方法创建第二个Activity——SecondActivity,并在FirstActivity中按钮点击事件中编写如下代码:
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
* param1:为当前活动上下文
* param2:要启动的活动
*/
Intent intent =new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);
}
});
2.3.3 隐式意图
指定了一系列更为抽象的 action和 category等信息,然后交由系统去分析这个 Intent,并帮我们找出合适的活动去启动。
隐式意图需要在activity标签下的intent-filter标签中指定action和category,只有action和category同时匹配的情况下(除去当category配置为默认 android:name="android.intent.category.DEFAULT" 时),该活动才能响应。如:
<activity android:name=".SecondActivity" >
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
java代码如下:
Intent intent=newIntent("com.example.activitytest.ACTION_START");
startActivity(intent);
每个 Intent 中只能指定一个 action,但却能指定多个 category。
intent.addCategory("...");
2.3.4 隐式意图更多用法
隐式意图不但能打开自己应用的activity,还能打开系统的activity,如浏览器、拨号等等。
@Override
public void onClick(View v) {
/**
* 1.Intent.ACTION_VIEW 这是系统内置动作,其常量值为android.intent.action.VIEW
* 2.通过Uri.parse()方法将网址解析为Uri对象
* 3.调用setData将Uri对象传递。
*/
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
//打电话
//Intent intent=new Intent(Intent.ACTION_DIAL);
//intent.setData(Uri.parse("tel:10086"));
//startActivity(intent);
}
2.3.5 向下一个Activity传递数据
(1)第一个activity:
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
//使用putExtra()方法传递数据
intent.putExtra("data", "这是我传递的数据");
startActivity(intent);
(2)第二个activity:
//通过getIntent()方法获取启动当前activity的意图对象
Intent intent = getIntent();
//什么类型的数据就get类型Extra("");
String data = intent.getStringExtra("data");
Log.d("SecondActivity", data);
2.3.6 返回数据给上一个Activity
Activity中提供了一个startActivityForResult()方法,此方法期望在活动销毁后能够返还一个数据给上一个活动。
(1)在第一个activity中如下编写:
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("data", "这是我传递的数据");
//startActivity(intent);
/**
* param1:仍然是意图对象
* param2:请求码,用于在之后的回调中判断数据来源,只要是唯一值就可以了,这里传入1
*/
startActivityForResult(intent, 1);
(2)第二个activity中重写onBackPressed()方法,此方法表示用户点击了返回键:
@Override
public void onBackPressed() {
//声明意图,但此处的意图仅仅是为了传递数据,并不是要启动Activity
Intent intent=new Intent();
intent.putExtra("data_back","这是返回的数据~~~");
/**
* 通过setResult方法来返回数据,非常重要
* param1:返回处理结果,常用的是RESULT_OK,RESULT_CANCELED
* param2:意图对象,传递数据
*/
setResult(RESULT_OK,intent);
//销毁活动
finish();
}
(3)在第一个activity中重写回调方法onActivityResult():
/**
* 第二个活动处理完成后返回第一个活动的回调函数
* @param requestCode 启动第二个活动时传入的请求码,即1
* @param resultCode 第二个活动返回的处理结果
* @param intent 携带着返回数据的intent
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
switch (requestCode){
// 判断请求码是不是我们所传递的
case 1:
if (resultCode==RESULT_OK){
String dataBack= intent.getStringExtra("data_back");
Toast.makeText(FirstActivity.this, "返回:"+dataBack, Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
2.4 生命周期
2.4.1 返回栈
Android中的活动是层叠的,每启动一个活动,就会覆盖在原来的活动上面,然后点击Back键就会销毁最上面的。
Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的集合,这个栈被称作返回栈(Back Stack)
2.4.2 状态
- 运行:当活动处于返回栈的栈顶时,则这个活动是运行状态
- 暂停:当一个活动不处于栈顶、但仍然可见时,这个活动处于暂停状态,
- 停止:当一个活动不在处于栈顶时,而且完全不可见时,则进入了停止状态。当其他地方需要内存时,停止状态的活动极有可能被回收
- 销毁:当一个活动从返回栈中移除后就变成了销毁状态。
2.4.3 生存期
- onCreate():活动初次创建时调用,做初始化工作
- onStart():对用户即将可见的时候调用
- onResume():将要与用户交互的时候调用(此时活动一定处于栈顶,且运行状态)
- onPause():准备去启动或者恢复另一个活动时调用
- onStop():完全不可见时调用
- onDestroy():被销毁之前调用,调用后则变为销毁状态
- onRestart():活动由停止状态变为运行状态之前调用(重新启动)
三种生存期:
(1)完整生存期:onCreate()-->onDestroy()的过程
(2)可见生存期:onStart()-->onStop()的过程
(3)前台生存期:onResume()-->onPause()
2.4.4 活动被回收如何处理
活动在停止状态是容易被系统回收的(比如系统内存不足情况下),如何解决活动被回收时临时数据得不到保存的问题?
Activity 中提供了一个onSaveInstanceState()回调方法,这个方法会保证一定在活动被回收之前调用。
/**
* Bundle类型的参数提供了一系列的方法用于保存数据
* @param outState
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/**
* put系列方法,依次类推,如putInt....
* param1:数据的键值
* param2:数据
*/
outState.putString("data_key","something you want to save.");
}
保存完成后可以在onCreate方法里面获取数据(因为活动被回收之后再运行此活动就会重新创建),onCreate方法也有一个Bundle类型的参数savedInstanceState:
if(savedInstanceState!=null){
String data=savedInstanceState.getString("data_key");
//其他操作....
}
还可以结合intent传递数据(把数据放在Bundle中,再把Bundle放到Intent中)。
2.5 启动模式
- standard
活动默认的启动模式,在此模式下,每当启动一个新的活动,他就会在返回栈中入栈,并处于顶部,系统不会在乎这个活动在返回栈中是否已存在,每次启动都会创建一个活动的实例。
- singleTop
启动活动时如果发现返回栈栈顶已经是该活动(注意是栈顶),则认为可直接使用它,不会再创建活动的实例。但不是在栈顶时,则还会创建实例。
- singleTask
在整个应用程序的上下文中,只存在一个该活动的实例。当启动活动时,首先检查返回栈中是否已存在,若存在,则把该活动之上的活动统统移除出栈,如果没有发现就会创建一个新的实例。
- singleInstance
该模式下的活动会启用一个新的返回栈来管理,这样做的意义就是可以和其他程序之间共享这个活动的实例(用以上三种模式的话,每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈入栈时必然是创建了新的实例,做不到共享的效果)。
2.6 实践技巧
2.6.1 识别当前是在哪一个活动
新建一个BaseActivity继承Activity,然后重写onCreate()方法,让其他的Activity类继承BaseActivity类,在BaseActivity类的onCreate方法中打印:
Log.d("BaseActivity", getClass().getSimpleName());
2.6.2 随时退出应用
(1)新建一个活动管理类:
package com.jastar.activitylifecycletest;
import android.app.Activity;
import java.util.ArrayList;
import java.util.List;
/**
* Created by admin on 2016/9/29.
*/
public class ActivityCollector {
//通过list来暂存活动
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)在BaseActivity中重写onCreate()和onDestroy()方法实现向list中添加和移除activity
ActivityCollector.addActivity(this);
ActivityCollector.removeActivity(this);
(3)在想退出应用的地方调用ActivityCollector.finishAll()即可。
2.6.3 启动活动的最佳写法
(1)常规的思路:
在FirstActivity中创建意图,存放数据,然后启动SecondActivity——合理且完全没问题,但是不知道需要传递什么数据,需要哪些参数。
(2)更好的思路:
在SecondActivity中添加下面的方法,然后在FirstActivity中调用即可——传递哪些数据参数一目了然,一行代码即可启动活动。
public static void actionStart(Context context, String param1, String param2) {
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra("param1", param1);
intent.putExtra("param2", param2);
context.startActivity(intent);
}