探究Android Activity
1、活动是什么
Activity是最容易吸引 用户 的东西
Activiy是一种包含 用户界面的组件
主要用于和用户进行交互
一个application可以包含零个或多个活动
2、活动的基本用法(1-3与活动相关,4介绍Toast,5介绍Menu,6介绍一种活动销毁方式)
手动创建 Android 项目
:
Android Studio 在一个工作区间内只允许打开一个项目
关闭当前项目 File->Close Project
新建Android 项目
->Add NoActivity
click Finish->等待Gradle构建项目
Ps:此时只是构建完项目 没有创建Activity 既没有布局(layout)也没有在AndroidManifest.xml中注册Activity
1、手动创建活动Activity
首先更改项目结构 Android模式->Project模式
com.eample.administrator.activitytest(Activity所在的包)->New->Activity->Empty Activity
不勾选Generate Layout File 和 Launcher Activity:
Generate Layout File会自动为Activity创建一个相应的布局文件
Launcher Activity会自动将Activity设置为当前项目的主活动(可以在AndroidManifest.xml中单独设置)
勾选Backwards Compatibility 表示会为项目启用 向下兼容 的模式 ->完成
项目中的任何活动都要重写 Activity 的 onCreate() 方法
Android Studio会自动完成简单的onCreate() 方法重写,里面添加 父类onCreate() 方法的调用:
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
}
}
2、创建和加载布局
Android程序讲究 逻辑 和 视图 分离。最好一个活动对应一个布局
布局用来显示界面内容
创建布局文件:
app/src/main/res ->New->Directory 创建一个名为layout的目录
layout->New->Layout resource file 创建一个名为first_layout的布局文件。根元素默认LinearLayout
布局编辑器:
Desin:
可视化布局编辑器
可以预览当前布局还可以通过拖放的方式编辑布局
Text:
通过XML文件的方式来编辑布局
通过XML在布局文件中添加按钮:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
<Button
android:id=“@+id/button_1"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:text="Button 1"
/>
</LinearLayout>
android:id 是给当前元素定义一个唯一标识符
Ps:@xx/xxxx是在XML中引用资源的方法 @+xx/xxxx实在XML中定义一个属性的方法
android:layout_width是指定当前元素的宽度
android:layout_height是指定当前元素的高度
match_parent表示与父元素一样宽/高
wrap_content表示当前元素的高度/宽度刚好包含里面的内容
android:text指定了元素中显示的文字内容
LinearLayout 表示线性布局
android:orientation="
vertical"垂直线性布局,"
horizontal"水平线性布局
加载布局:
在FirstActivity中 onCreate()方法中加入setContentView(R.layout.first_layout);
setContentView()方法需要一个参数-布局文件的id-R.layout.first_layout
关于R.layout.first_layout:
项目中添加的任何资源都会在R文件中生成一个相应的 资源id !资源!id!
所以创建的first_layout.xml布局的id被自动添加到R文件中
所以可以直接在方法中引用布局的id
3、在AndroidManifest.xml文件中注册
所有活动都要在AndroidManifest.xml中进行注册才能生效
Android Studio一般会自动帮我们在AndroidManifest.xml中注册号Activity
注册Activity:
在<activity>标签内注册
<activity android:name=".FirstActivity"></activity>
android:name 指定注册的活动
.FirstActivity 实际是com.example.administrator.activitytest.FirstActivity的缩写 因为:
AndroidManifest.xml文件最外层标签<manifet>标签中已经通过package属性指定了程序的包名com.example.adminstrator.activitytest
配置主活动:
主活动就是程序运行起来时首先启动的活动
<activity>标签中加入<intent-filter>标签 具体如下:
<activity android:name=".FirstActivity"
android:label="This is FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
android:label指定活动中标题栏内容 使用方法见上方代码
标题栏是显示在活动最顶部的
给 主活动 指定的 label 还会成为启动器(Launcher)中应用程序显示的名称
如果没有给application声明主活动,那么该application可以正常安装,只是无法再Launcher中看到或者打开这个程序。
这种application一般作为第三方服务供其他应用在内部调用
4、在活动中使用Toast
Toast是Android系统提供的一种 提醒方式
通知用户短小信息
短时间自动消失
不占用任何屏幕空间
Toast使用:
定义弹出Toast的触发点:在onCreate()方法中
protected void onCreate(Bundel savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.id.first_layout);
Button button1 = (Button) findeViewById(R.id.button_1);
button1.setOnclickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(FirstActivity.this, "You clicked Button 1", Toast.LENGTH_SHORT).show();
}
});
}
findViewById()方法获取布局文件中定义的元素(此处是R.id.button_1)
findeViewById()方法获取的是一个View对象,向下转型为Button对象
调用setOnclickListener()为按钮注册监听器
实现一个匿名内部监听类
在监听类的onClick()方法中实现弹出Toast:
静态方法makeText()创建一个Toast对象,调用show()将Toast显示出来
makeText()的三个参数:
第一个Context:Toast要求的上下文,由于活动本身就是一个Context对象,这里直接传入FirstActivity.this
第二个是Toast显示的文本内容
第三个是Toast显示的时长,有两个内置常量Toast.LENGTH_SHORT和Toast.LENGTH_LONG
5、在活动中使用Menu
菜单项创建:
Android提供一种方式,让菜单能得到展示的同时不占用任何屏幕空间
Menu创建:
res目录下创建一个menu文件夹:res目录->New->Directory->输入文件名menu->OK
在menu文件夹下信件一个叫main的菜单文件:menu目录->New->Menu resource file->输入文件名main->OK
main.xml:
<menu xmln:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add_item"
android:title="Add"/>
<item
android:id=:@+id/remove_item"
android:title="Remove"/>
</menu>
<item>标签创建具体菜单项,android:id给出唯一标识符,android:title给这个菜单项指定名称
在FirstActivity中覆写onCreateOptionsMenu()方法(可使用Ctrl+O快捷键):
public boolean onCreatOptionMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
getMenuInflater()得到MenuInflater对象
调用MenuInflater的inflate()方法创建菜单。
inflate()两个参数:
第一个参数用于指定通过那个资源文件来创建菜单
第二个指定我们的菜单项将添加到那个Menu对象中
返回值true表示允许创建的菜单显示出来,false表示无法显示
菜单功能创建:
在FirstActivity中覆写onOptionsItemSelected()方法:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_item:
Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this, "Yout clicked Remove", Toast.LENGTH_SHORT).show();
break;
default:
}
return true;
}
在onOptionsItemSelected()方法中通过item.getItemId() 判断点击的菜单项
6、销毁一个活动
Back键可以销毁当前活动
在活动中调用finish()方法也可以销毁当前活动,效果与Back一样
3、使用Intent在活动中穿梭
活动之间的跳转与数据的传输
Intent大致分为两种:显式Intent和隐式Intent
1、使用显式Intent
Intent有多个构造函数的重载
构造函数:Intent(Context packageContext, Class<?>cls):
第一个参数Context指启动活动的上下文,比如FirstActivity.this
第二个参数Class是指想要启动的目标活动,比如SecondActivity.class
Intent的使用:
startActivity(intent):intent是先构造完成的Intent对象
Activty类中的方法:startActivity()方法
具体代码:
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
}
以上就实现了由FirstActivity跳转到SecondeActivity的功能
2、使用隐式Intent
不明确指定要启动哪一个活动
隐式Intent的本质是通过XxxxActivity.java文件中的Intent对象所携带的action与category等与清单文件AndroidManifest.xml中
<intent-filter>标签里的<action>、<category>等相匹配来决定哪些活动能响应.java文件中的Intent对象
Ps:另有博客讲述隐式Intent匹配原则。
AndroidManifest.xml代码如下:
<activity android:name=".SecondeActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.categoty.DEFAULT"/>
</intent-filter>
</activity>
FirstActivity中按钮点击事件代码:
button1.setOnclickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);
}
});
另一种Intent构造函数
android.intent.category.DEFAULT是Intent的默认category,在调用startActivity()时会自动加入这个category
每个Intent只能指定一个action,能指定多个category
3、更多隐式Intent用法
使用隐式Intent不仅可以启动自己程序内的活动,还可以启动其他程序的活动
这帮助Android实现了多应用之间的功能共享
应用程序中展示一个网页:
FristActivity按钮点击事件onClick中代码:
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,只是一个Android系统内置动作,其常量值为android.intent.action.VIEW
Uri.parse()方法把字符串解析为Uri对象
setData方法接收Uri对象
在<intent-filter>标签中再配置<data>标签,用于更精确的指定当前活动能够响应什么类型的数据
<data>标签中主要配置以下内容:
android:scheme 用于指定数据的协议部分,如http
android:host 用于指定数据的主机名, 如www.baidu.com
android:port 用于指定数据的端口邠,一般紧随主机名之后
android:path
android:mineType
只有<data>标签中指定内容和Intent中携带的Data完全一致时,活动才能响应该Intent
一般在<data>标签中不会指定过多内容,如上面只指定android:scheme为http,就可以响应所有http协议的Intent:
<data android:scheme="http"/>
除了http协议外还有geo表示地理位置、tel表示拨打电话:
intent.setData(Uri.parse("tel:10086"));
4、向下一个活动传递数据
Intent提供 putExtar()方法的重载,可以把数据暂存在Intent中
intent.putExtra("extra_data", data):
第一个参数是键,第二个参数是真正要传递的数据
public void onClick(View v) {
String data = "xxxx";
Intent intent = new Intent(FirstActivit.this, SecondActivity.class);
intent.putExtra("extra_data", data);
startActivity(intent);
}
取出数据:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
}
getIntent()方法获取Intent对象
getXxxExtra()传入参数为键,获得相应的数据,具体有getIntExtra()、getBooleanExtra()等
5、返回数据给上一个活动
主要通过startActivityForResult()启动活动,通过setResult()设置返回的请求码与Intent,通过onActivityResult()获得返回的数据
a、Activity中有一个startActivityForResult()方法也可以启动活动,这个方法 期望 在 活动销毁 的时候能返回一个结果给上一个活动
startActivitForResult(intent, 1)方法的参数:第一个参数是Intent,第二个参数是请求码requestCode,用于判断数据来源
b、SecondActivity中onClick()://Ps:这是通过按钮回到启动活动,所以覆写按钮监听方法
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("data_return", "Hello FirstActivity");
setResult(RESULT_OK, intent);
finish();
}
setResult()方法专门用于向上一个活动返回数据,接收两个参数:
第一个用于返回处理结果resultCode,一般是RESULT_OK或RESULT_CANCELED
第二个把带有数据的Intent对象传递回去
c、使用startActivityForResult()启动活动时,在活动被销毁后会回调上一个活动的onActivityResult()方法,所以需要在第一个活动中覆写这个方法来得到数据
@Override
protected void onActivityResult(int requestCode, inte resultCode, Intent data) {
switch (reqeustCode) {
case 1:
if (resultCode == RESULT_OK) {
String returnedData = data.getStringExtar("data_return");
}
break;
default:
}
}
Ps:关于break的问题:break可用可不用,没有break时顺序执行,直到遇到break跳出或者执行到末尾
onActivityResult()三个参数:
第一个参数requestCode,即我们在启动活动中传入的请求码
第二个参数resultCode,即我们在返回数据时传入的处理结果
第三个参数,即携带返回数据的Intent对象
requestCode判断数据来源,resultCode判断处理结果是否成功
如果用户是通过Back键回到启动活动,可以通过覆写onBackPressed()方法来解决问题
4、活动的生命周期
1、返回栈
Android中的活动是可以层叠的,每启动一个新的活动,就会覆盖在原来的活动智商,点击Back会销毁最上面的活动,下面的活动就会重新显示出来
Android使用任务(Task)来管理活动,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)
默认情况下,每当我们启动一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。每当我们按下Back或者调用finish()方法销毁一个方法时,处于栈顶的活动就会出栈,前一个活动就会重新处于栈顶,系统总是会显示处于栈顶的活动给用户。
返回栈管理活动入栈出栈操作:
2、活动状态
每个活动在其生命周期中最多可能会有4种状态
运行状态:
当活动位于栈顶时,活动就处于运行状态。
系统最不愿意回收运行状态的活动
暂停状态:
当一个活动不再位于栈顶,但仍然可见时,活动就处于暂停状态。
仍然可见是指:并不是所有的活动都占满整个屏幕,比如对话框形式的活动,它只占用屏幕中间一部分。
暂停状态活动仍然是完全存活着的,系统次不愿意回收
停止状态:
当一个活动不再处于栈顶,并且完全不可见的时候,活动处于停止状态。
系统仍然为该活动保存相应的状态和成员变量。
系统第三不愿意回收
销毁状态:
活动从返回栈中移除后,活动就变成销毁状态。
系统最倾向于回收销毁状态的活动
3、活动的生存期
Activity类有7个回调方法,覆盖了活动生命周期的每一个环节。
7个回调方法:
onCreate():
活动第一次被创建时调用
完成活动的初始化操作
onStart():
活动由不可见变为可见时调用
onResume():
活动准备好和用户交互时调用
活动一定是位于栈顶的,并处于运行状态
onPause():
系统准备去启动或者恢复另一个活动的时候调用
释放小号的CPU资源,保存一些关键数据
执行速度要快,不能影响新的栈顶活动
onStop():
活动完全不可见的时候调用
与onPasue()的区别在于,新启动的活动如果是对话框形式的话,onPause()会被调用,而onStop()不会被执行
onDestroy():
活动被销毁前调用,之后活动的状态变为销毁状态
onRestart():
活动由停止状态变为运行状态时调用
三种生存期:
完整生存期:
onCreate()和onDestroy()方法之间所经历的。onCreate()完成初始化,onDestroy()完成内存释放
可见生存期:
onStart()和onStop()方法之间所经历的。
在此期间,活动对于用户总是可见的
onStart()中对资源进行加载,onStop()中对资源进行释放
前台生存期:
onResume()和onPause()方法之间所经历的。
此时,活动总是处于运行状态,可以和用户进行交互
活动生命周期示意图:
3、体验活动的生命周期
android:theme=”@style/Theme.AppCompat.Dialog”:
android:theme属性,是用于给当前活动指定主题的,Android系统内置很多主题
4、活动被回收的解决办法
Activity提供了一个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类型参数,一般为null,但如果onSaveInstanceStae()方法被调用过,则该Bundle参数就会带有之前保存的全部数据
Bundle与Intent保存和取出数据的方法对比:
Bundle保存:bundle.put类型名(“键名”, 值)
Intent保存:intent.putExtra(“键名”, 值)
Bundle取出:bundle.get类型名(“键名”)
Intent取出:intent.get类型名(“键名”)
Intent还可以和Bundle结合使用:
使用Bundle保存数据,再用Intent保存Bundle
5、活动的启动模式
四种启动模式:
standard
singleTop
singleTask
singleInstance
在AndroidManifest.xml中通过<activity>标签指定android:launcherMode属性来选择启动模式
1、Standard
默认启动模式
每次启动都会创建该活动的一个新的实例
示意图:
2、singleTop
android:launchMode=”singleTop”
启动活动时,如果发现返回栈的栈顶已经是该活动,则直接使用它,不会再创建新的活动实例
如果活动不是在栈顶,则会再创建新的活动实例
示意图:
3、singleTask
每次启动该活动时系统首先检查返回栈中是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把这个活动智商的所有活动统统出栈,如果没有发现则重新创建一个实例
保证每次只有一个实例
示意图:
4、singleInstance
活动会启用一个新的返回栈来管理这个活动
使不同的程序共享一个活动实例成为可能
示意图:
6、活动的最佳实践
技巧也很重要
1、知晓当前在哪一个活动
在AppCompatActivity和具体Activity之间加一个普通java类,使其打印当前实例类名
实现如下:
创建BaseActivity继承AppCompatActivity
修改具体Activity的继承结构,不再继承自AppCompatActivity,改为继承自BaseActivity
覆写BaseActivity的onCreate()方法:
加入一行Log.d(“BaseActivity”, getClass().getSimpleName());
2、随时随地退出程序
新建一个ActivityCollector类作为活动管理类
通过静态List管理活动,有三个方法:静态添加活动类、静态删除活动类、静态销毁所有活动类
在BaseActivity的onCreate()方法中使用ActivitCollector.add(this);
在BaseActivity的onDestroy()方法中使用ActivitCollector.remove(this);
在任何想直接退出的地方加一个按钮,在按钮的OnClick方法中加入:
ActivityCollector.finishAll();
Ps:android.os.Process.killProcess(android.os.Process.myPid());杀死当前进程,该方法只能杀死当前进程,不能杀掉其他进程
3、启动活动的最佳写法
在要被启动的活动中加入一个方法:
public static void actionStart(Context context, String data1,String data 2) {
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra(“param1”, data1);
intent.putExtra(“param2”,data2);
context.startActivity(intent);
}
需要多少启动参数,就加入多少启动参数,然后在启动活动中直接一行启动就行
SecondActivity.actionStart(FirstActivity.this, “data1”, “data2”);
这个方法可以作为代码编写规范使用