一、活动的基本用法
1.1创建和加载布局
1.android:id="@+id/button"给当前元素定义一个唯一标识符,之后可在代码中对这个元素进行操作
2.android:layout_width="match_parent"表示让当前元素和父元素一样宽
3.android:layout_heigth="wrap_content"表示当前元素的高度只要能刚好包含里面的内容就行
4.android:text="Button 1"指定元素中显示的文字内容
5.android:name=".FirstActivity"指定具体注册哪一个活动
方法:
1.setContentView(R.layout.first_layout);给当前活动加载一个布局,一般都会传入一个布局文件名
1.2在AndroidManifest文件中注册
所有的活动都需要在AndroidManifest.xml中进行注册才能生效
活动的注册声明要放在<application>标签内,通过<activity>标签来对活动进行注册
在<activity>中使用android:name来指定具体注册哪个活动
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.uibestpractice">//包名
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">//注册活动
android:label="This is FirstActivity"//指定活动中标题栏的内容
<intent-filter>//配置主活动
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
1.2在活动中使用Toast
在程序中可以使用它将一些短小的信息通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间。
方法:
1.findViewById(R.id.button)获取到在布局文件中定义的元素id
Button button1 = (Button) findViewById(R.id.button);
2.setOnClickListener()为按钮注册一个监听器,点击按钮时就会执行监听器中的onClick()方法
button1.setOnClickListener(new View.OnClickListener())
3.makeText()创建出一个Toast对象,需要传入三个参数,第一个参数是Context——活动本身,直接传入FirstActivity.this;第二个参数是Toast显示的文本内容;第三个参数是Toast显示时长,有两个内置常量可以选择Toast.LENGTH_SHORT和Toast.LENGTH_LONG
Toast.makeText(FirstActivity.this, "You clicked Button 1", Toast.LENGTH_SHORT).show();
4.调用show()将Toast显示出来
1.3在活动中使用Menu
1、<item>标签用来创建具体的某一个菜单项,然后通过android:id 给这个菜单项指定一个唯一的标识符,通过android:title 给这个菜单项指定一个名称,在main.xml中添加如下代码:
2、在FirstActivity中重写onCreateOptionsMenu()方法
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
通过getMenuInflater() 方法能够得到MenuInflater 对象
再调用它的inflate() 方法就可以给当前活动创建菜单
inflate() 方法接收两个参数,第一个参数用于指定我们通过哪一个资源文件来创建菜单,这里当然传入R.menu.main ;第二个参数用于指定我们的菜单项将添加到哪一个Menu 对象当中,这里直接使用onCreateOptionsMenu() 方法中传入的 menu 参数
然后给这个方法返回true ,表示允许创建的菜单显示出来;如果返回false,创建的菜单将无法显示
3、定义菜单响应事件
在onOptionsItemSelected() 方法中,通过调用item.getItemId() 来判断我们点击的是哪一个菜单项,然后给每个菜单项加入自己的逻辑处理,这里我们就活学活用,弹出一个刚刚学会的Toast
1.4销毁一个活动
button1.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
finish();
}
});
二、使用Intent在活动之间穿梭
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可被用于启动活动、启动服务以及发送广播等场景。
Intent大致可以分为两种:显式Intent 和隐式Intent
2.1显式Intent
Intent有多个构造函数的重载,其中一个是Intent(Context packageContext, Class cls)
这个构造函数接收两个参数,第一个参数Context 要求提供一个启动活动的上下文, 第二个参数Class 则是指定想要启动的目标活动,通过这个构造函数就可以构建出Intent 的“意图”。
Activity类中提供了一个startActivity() 方 法,这个方法是专门用于启动活动的,它接收一个Intent 参数
button1.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
例: Intent intent = new Intent(FirstActivity.this, SecondActivity.class);//上下文
目标活动
startActivity(intent); //启动活动
}
});
2.2隐式Intent
不明确指出我们想要启动哪一个活动,而是指定了一系列更为抽象的action和category 等信息,然后交由系统去分析这个Intent,并帮我们找出合适的活动去启动。
①通过在标签下配置的内容,可以指定当前活动能够响应的 action 和category ,打开AndroidManifest.xml,添加如下代码:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
//默认的category,在调用startActivity() 方法的时候会自动将这个category 添加到Intent中
</intent-filter>
</activity>
只有<action>和<category>中的内容同时能够匹配上Intent中指定的action和category 时,这个活动才能响应该Intent
②修改FirstActivity中按钮的点击事件,代码如下所示:
button1.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
Intent intent = new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);
}
});
※每个Intent中只能指定一个action,但却能指定多个category
可以通过addCategory()方法在onClick()方法中添加category
例:intent.addCategory("com.example.activitytest.MY_CATEGORY");
并在<intent-filter>中再添加一个category的声明:
例:<category android:name="com.example.activitytest.MY_CATEGORY"/>
2.3更多隐式Intent的用法
使用隐式Intent,我们不仅可以启动自己程序内的活动,还可以启动其他程序的活动,这使得 Android多个应用程序之间的功能共享成为了可能,比如应用程序中需要展示一个网页:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
//指定action是Intent.ACTION_VIEW
//是一个Android内置动作,其常量值为android.intent.action.VIEW
intent.setData(Uri.parse("http://www.baidu.com"));
//通过Uri.parse()方法,将一个网址字符串解析成一个Uri对象
//调用Intent的setData()方法将这个Uri对象传递进去
startActivity(intent);
}
});
我们还可以在 标签中再配置一个<data>标签,用于更精确地指定当前活动能够响应什么类型的数据。标签中主要可以配置以下内容:
android:scheme 用于指定数据的协议部分,如上例中的http部分。
android:host 用于指定数据的主机名部分,如上例中的www.baidu.com部分。
android:port 用于指定数据的端口部分,一般紧随在主机名之后。
android:path 用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。 android:mimeType 用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
<activity android:name=".ThirdActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" />
</intent-filter>
</activity>
除了http协议外,我们还可以指定很多其他协议,比如geo表示显示地理位置、tel表示拨打电话
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
});
2.4向下一个活动传递数据
①在启动活动时传递数据的思路很简单,Intent中提供了一系列putExtra() 方法的重载,可以把 我们想要传递的数据暂存在Intent中,启动了另一个活动后,只需要把这些数据再从Intent中取出 就可以了。比如说FirstActivity中有一个字符串,现在想把这个字符串传递到SecondActivity中, 你就可以这样编写:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String data = "Hello SecondActivity";
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("extra_data", data);
startActivity(intent);
}
});
使用显式Intent的方式来启动SecondActivity,并通过putExtra()方法传递了一个字符串。注意这里putExtra() 方法接收两个参数,第一个参数是键,用于后面从Intent中取值,第二个参数才是真正要传递的数据。
②在SecondActivity中将传递的数据取出,并打印出来,代码如下所示:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
Intent intent = getIntent();//获取到用于启动SecondActivity的Intent
String data = intent.getStringExtra("extra_data");
//如果传递的是整型数据,则使用getIntExtra()方法;
//如果传递的是布尔型数据,则使用getBooleanExtra()方法
Log.d("SecondActivity", data);
}
}
2.5返回数据给上一个活动
Activity中还有一个startActivityForResult()方法也是用于启动活动的,但这个方法期望在活动销毁的时候能够返回一个结果给上一个活动。
startActivityForResult()方法接收两个参数,第一个参数还是Intent,第二个参数是请求码(一个唯一值),用于在之后的回调中判断数据的来源。修改FirstActivity中按钮的点击事件,代码如下所示:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivityForResult(intent, 1);
}
});
接下来我们在SecondActivity中给按钮注册点击事件,并在点击事件中添加返回数据的逻辑,代码如下所示:
......
Button button2 = (Button) findViewById(R.id.button_2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("data_return", "Hello FirstActivity");
setResult(RESULT_OK, intent);
finish();
}
});
setResult()方法非常重要,是专门用于向上一个活动返回数据的。setResult() 方法接收两个参数,第一个参数用于向上一个活动返回处理结果,一般只使用RESULT_OK 或RESULT_CANCELED 这两个值,第二个参数则把带有数据的Intent传递回去。
由于我们是使用startActivityForResult() 方法来启动SecondActivity的,在 SecondActivity被销毁之后会回调上一个活动的onActivityResult() 方法,因此我们需要在 FirstActivity中重写这个方法来得到返回的数据,如下所示:
@Override
protected void onActivityResult(int requestCode, int resultCode, 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 ,即我们在返回数据时传入的处理结果。第三个参 数data ,即携带着返回数据的Intent。
由于在一个活动中有可能调 用startActivityForResult() 方法去启动很多不同的活动,每一个活动返回的数据都会回调到onActivityResult() 这个方法中,因此我们首先要做的就是通过检查requestCode 的值来判断数据来源。确定数据是从SecondActivity返回的之后,我们再通过resultCode 的值来判断处理结果是否成功。最后从data 中取值并打印出来,这样就完成了向上一个活动返回数据的工作。
如果用户在SecondActivity中并不是通过点击按钮,而是通过按下Back键回到FirstActivity,这样数据不就没法返回了吗?没错,不过这种情况还是很好处理的,我们可以通过在SecondActivity中重写onBackPressed()方法来解决这个问题,代码如下所示:
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("data_return", "Hello FirstActivity");
setResult(RESULT_OK, intent);
finish();
}
三、活动的生命周期
3.1返回栈
Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合, 这个栈也被称作返回栈(Back Stack)。栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish() 方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。
3.2活动状态
01. 运行状态当一个活动位于返回栈的栈顶时,这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。
02. 暂停状态当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。你可能会觉得既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域,你很快就会在后面看到这种活动。处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。
03. 停止状态当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。
04. 销毁状态当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。
3.3活动的生存期
Activity类中定义了7个回调方法,覆盖了活动生命周期的每一个环节,下面就来一一介绍这7个方法:
onCreate() :这个方法你已经看到过很多次了,每个活动中我们都重写了这个方法,它会在活动第一次被创建的时候调用。你应该在这个方法中完成活动的初始化操作,比如说加载布局、绑定事件等。
onStart() :这个方法在活动由不可见变为可见的时候调用。
onResume() :这个方法在活动准备好和用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态。
onPause() :这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
onStop() :这个方法在活动完全不可见的时候调用。它和onPause() 方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么onPause() 方法会得到执行, 而onStop() 方法并不会执行。
onDestroy() :这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。
onRestart() :这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。
四、活动的启动模式
启动模式一共有4种,分别是standard、singleTop、singleTask和 singleInstance,可以在AndroidManifest.xml中通过给标签指定 android:launchMode 属性来选择启动模式。
4.1standard(点一次创建一次实例)
standard是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。
4.2singleTop
当活动的启动模式指定为 singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例,当其并未处于栈顶时,再启动它,还是会创建新的实例。
4.3singleTask
当活动的启动模式指定为singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
4.4singleInstance
指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。