活动是什么
活动(Activity)是最容易吸引用户的地方,它是一种可以包含用户界面的组件,主要用于 和用户进行交互。一个应用程序中可以包含零个或多个活动,但不包含任何活动的应用程序很少见。
具体操作实例
Button元素
这是一个最简单的Button属性设置
- android:id:给与Button一个名字
- android:layout_width 指定了当前元素的宽 度,这里使用 match_parent 表示让当前元素和父元素一样宽。
- android:layout_height 指定 了当前元素的高度,这里使用 wrap_content 表示当前元素的高度只要能刚好包含里面的内容就行。
- android:text 指定了元素中显示的文字内容。
这是一个比较复杂的Button属性设置
具体参考
Toast元素
Toast 是 Android 系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息 通知给用户,这些信息会在一段时间后自动消失,并且不会占用任何屏幕空间,我们现在就尝试 一下如何在活动中使用 Toast。
1 |
|
在活动中,可以通过 findViewById()方法获取到在布局文件中定义的元素。
我们传入R.id.testbutton1 即在layout中定义的Button的id。
得到按钮的实例之后,我们通过调用 setOnClickListener()方法为按钮注册一个监听器, 点击按钮时就会执行监听器中的 onClick()方法。
Toast 的用法非常简单,通过静态方法 makeText()创建出一个 Toast 对象,然后调用 show() 将 Toast 显示出来就可以了。这里需要注意的是,makeText()方法需
要传入 3 个参数。
第一个参 数是 Context,也就是 Toast 要求的上下文,由于活动本身就是一个 Context 对象,因此这里直 接传入MainActivity.this即可。
第二个参数是 Toast 显示的文本内容。
第三个参数是 Toast 显示的时长,有两个内置常量可以选择 Toast.LENGTH_SHORT 和 Toast.LENGTH_LONG。
Menu元素
首先在 res 目录下新建一个 menu 文件夹
名称为menu,再右击 menu 文件夹
文件名输入main。
创建完成后再main.xml中输入如下代码
1 |
|
这里我们创建了两个菜单项,其中标签就是用来创建具体的某一个菜单项,然后通 过 android:id 给这个菜单项指定一个唯一的标识符,通过 android:title 给这个菜单项指定 一个名称。
随后重新回到MainActivity重写onCreateOptionsMenu()方法,重写方法可以使用 Ctrl + O 快捷键(Mac 系统是 control + O)
1 |
|
通过 getMenuInflater()方法能够得到 MenuInflater 对象,再调用它的 inflate()方法 就可以给当前活动创建菜单了。
inflate()方法接收两个参数,第一个参数用于指定我们通过哪 一个资源文件来创建菜单,这里当然传入 R.menu.main。第二个参数用于指定我们的菜单项将添加到哪一个 Menu 对象当中,这里直接使用 onCreateOptionsMenu()方法中传入的 menu 参数。
然后给这个方法返回 true,表示允许创建的菜单显示出来,如果返回了 false,创建的菜单将无法显示。
当然,仅仅让菜单显示出来是不够的,我们定义菜单不仅是为了看的,关键是要菜单真正可 用才行,因此还要再定义菜单响应事件。在 MainActivity中重写 onOptionsItemSelected()方法:
1 |
|
这时启动程序就会有不同
你会发现在标题栏的右侧多了一个三点的符号,这个就是菜单按钮了
点击不同的菜单按钮 就会出现不同的 Toast弹窗提示
销毁一个活动
最简单的一个方法就是按一下back键就可以销毁档前的活动。
不过如果你不想通过按 键的方式,而是希望在程序中通过代码来销毁活动,当然也可以,Activity 类提供了一个 finish() 方法,我们在活动中调用一下这个方法就可以销毁当前活动了。
修改按钮监听器中的代码
1 |
|
重新运行程序,这时点击一下按钮,当前的活动就被成功销毁了,效果和按下 Back 键是一 样的。
使用Intent在活动之间穿梭
Intent 是 Android 程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想 要执行的动作,还可以在不同组件之间传递数据。Intent 一般可被用于启动活动、启动服务以及 发送广播等场景。
Intent 大致可以分为两种:显式 Intent 和隐式 Intent。
Intent 有多个构造函数的重载,其中一个是 Intent(Context packageContext, Class cls)。这个构造函数接收两个参数,第一个参数 Context 要求提供一个启动活动的上下文,第 二个参数 Class 则是指定想要启动的目标活动。
Activity 类中提供了一个 startActivity()方法,这 个方法是专门用于启动活动的,它接收一个 Intent 参数,这里我们将构建好的 Intent 传入 startActivity()方法就可以启动目标活动了。
使用显式 Intent
我们现在存在两个活动,现在要由主活动跳转导其他活动。
先在主活动的布局文件中创建一个Button用于进行跳转操作
1 | <Button |
在主活动的Java中对Button按钮进行设置
1 | Button button3 =findViewById(R.id.testbutton3); |
我们首先构建出了一个 Intent,传入 MainActivity.this 作为上下文,传入 MainActivity2.class作为目标活动,这样我们的“意图”就非常明显了,即在MainActivity这个活 动的基础上打开 MainActivity2 这个活动。然后通过 startActivity()方法来执行这个 Intent。
使用这种方式来启动活动,Intent 的“意图”非常明显,因此我们称之为显式 Intent。
使用隐式 Intent
相比于显式 Intent,隐式 Intent 则含蓄了许多,它并不明确指出我们想要启动哪一个活动, 而是指定了一系列更为抽象的 action 和 category 等信息,然后交由系统去分析这个 Intent, 并帮我们找出合适的活动去启动。
通过在标签下配置的内容,可以指定当前活动能够响应的 action 和 category
1 | <activity |
在标签中我们指明了当前活动可以响应 com.example.activitytest.ACTION_ START 这个 action,而标签则包含了一些附加信息,更精确地指明了当前的活动能够响应的 Intent 中还可能带有的 category。只有和中的内容同时能够匹配 上 Intent 中指定的 action 和 category 时,这个活动才能响应该 Intent。
1 | //隐式intent |
我们使用了 Intent 的另一个构造函数,直接将 action 的字符串传了进去,表明 我们想要启动能够响应 com.example.activitytest.ACTION_START 这个 action 的活动。android.intent.category.DEFAULT 是一种默认的 category,在调 用 startActivity()方法的时候会自动将这个 category 添加到 Intent 中。
每个 Intent 中只能指定一个 action,但却能指定多个 category。
更多隐式 Intent 的用法
使用隐式 Intent,我们不仅可以启动自己程序内的活动,还可以启动其他程序的活动,这使得 Android多个应用程序之间的功能共享成为了可能。
1 | Button button2 =findViewById(R.id.testbutton2); |
上面这段代码可以调用浏览器进入指定的网站
这里我们首先指定了 Intent 的 action 是 Intent.ACTION_VIEW,这是一个 Android 系统内 置的动作,其常量值为 android.intent.action.VIEW。然后通过 Uri.parse()方法,将一个 网址字符串解析成一个 Uri 对象,再调用 Intent 的 setData()方法将这个 Uri 对象传递进去。
与此对应,我们还可以在标签中再配置一个标签,用于更精确地 指定当前活动能够响应什么类型的数据。标签中主要可以配置以下内容。
- android:scheme。用于指定数据的协议部分,如上例中的 http 部分。
- android:host。用于指定数据的主机名部分,例如 www.baidu.com 。
- android:port。用于指定数据的端口部分,一般紧随在主机名之后。
- android:path。用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。
- android:mimeType。用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
只有标签中指定的内容和 Intent 中携带的 Data 完全一致时,当前活动才能够响应该 Intent。
这里我们自己建立一个活动,让它也可以响应打开网页的Intent
在 AndroidManifest.xml 中修改此活动的注册信息
1 | <activity |
重新运行程序 此时点击上个活动的链接就会多出一个选项,说明我们成功了
此时如果你选择chrome打开,就可以正常打开。
但如果你选择我们的写的活动,就会进入我们写的活动。
除了 http 协议外,我们还可以指定很多其他协议,比如 geo 表示显示地理位置、tel 表示拨打 电话。
1 | Button button4 =findViewById(R.id.testbutton4); |
这一段代码就可以让我们调用系统拨号界面,并自动填写电话号码。
向下一个活动传递数据
在启动活动时传递数据的思路很简单,Intent 中提供了一系列 putExtra()方法的重载,可 以把我们想要传递的数据暂存在 Intent 中,启动了另一个活动后,只需要把这些数据再从 Intent 中取出就可以了。
1 | Button button3 =findViewById(R.id.testbutton3); |
这里我们还是使用显式 Intent 的方式来启动 MainActivity,并通过 putExtra()方法传递了 一个字符串。注意这里 putExtra()方法接收两个参数,第一个参数是键,用于后面从 Intent 中 取值,第二个参数才是真正要传递的数据。
接下来我们在MainActivity2中将传递的数据取出,并打印出来
1 |
|
首先可以通过 getIntent()方法获取到用于启动 SecondActivity 的 Intent,然后调用 getStringExtra()方法,传入相应的键值,就可以得到传递的数据了。这里由于我们传递的是 字符串,所以使用 getStringExtra()方法来获取传递的数据。如果传递的是整型数据,则使 用 getIntExtra()方法;如果传递的是布尔型数据,则使用 getBooleanExtra()方法,以此 类推。
活动的生命周期
返回栈
Android 是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集 合,这个栈也被称作返回栈(Back Stack)。栈是一种后进先出的数据结构,在默认情况下,每当 我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下 Back 键 或调用 finish()方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会 重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。
活动状态
每个活动在其生命周期中最多可能会有 4 种状态。
- 运行状态
当一个活动位于返回栈的栈顶时,这时活动就处于运行状态。 - 暂停状态
当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。 - 停止状态
当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为 这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于 停止状态的活动有可能会被系统回收。 - 销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。
活动的生存期
Activity 类中定义了 7 个回调方法,覆盖了活动生命周期的每一个环节,下面就来一一介绍这7个方法。
- onCreate()。它会在活动第一次被创建的时候调用。你应该在这个方法中完成活动的初始化操作,比如 说加载布局、绑定事件等。
- onStart()。这个方法在活动由不可见变为可见的时候调用。
- onResume()。这个方法在活动准备好和用户进行交互的时候调用。此时的活动一定位于 返回栈的栈顶,并且处于运行状态。
- onPause()。这个方法在系统准备去启动或者恢复另一个活动的时候调用。
- onStop()。这个方法在活动完全不可见的时候调用。它和 onPause()方法的主要区别在 于,如果启动的新活动是一个对话框式的活动,那么 onPause()方法会得到执行,而 onStop()方法并不会执行。
- onDestroy()。这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。
- onRestart()。这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。
以上 7 个方法中除了 onRestart()方法,其他都是两两相对的,从而又可以将活动分为 3 种生存期。
完整生存期。活动在 onCreate()方法和 onDestroy()方法之间所经历的,就是完整生存期。一般情况下,一个活动会在 onCreate()方法中完成各种初始化操作,而在 onDestroy()方法中完成释放内存的操作。
可见生存期。活动在 onStart()方法和 onStop()方法之间所经历的,就是可见生存期。 在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互。我们可 以通过这两个方法,合理地管理那些对用户可见的资源。比如在 onStart()方法中对资源进行加载,而在 onStop()方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
前台生存期。活动在onResume()方法和 onPause()方法之间所经历的就是前台生存期。 在前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行交互的,我 们平时看到和接触最多的也就是这个状态下的活动。
用图文表示
onSaveInstanceState()回调方法
onSaveInstanceState()回调方法可以保证在活动被回收之前一定会被调用,因此我们可以通过这个方法来解决活动被回收时临 时数据得不到保存的问题。
onSaveInstanceState()方法会携带一个 Bundle 类型的参数,Bundle 提供了一系列的方 法用于保存数据,比如可以使用 putString()方法保存字符串,使用 putInt()方法保存整型数 据,以此类推。
每个保存方法需要传入两个参数,第一个参数是键,用于后面从 Bundle 中取值,第二个参数是真正要保存的内容。
在 MainActivity 中添加如下代码就可以将临时数据进行保存:
1 | //暂存数据 |
在onCreate方法中获得数据,并打印出来
1 | //获得销毁前的数据 |
取出值之后再做相应的恢复操作就可以了,比如说将文本内容重新赋值到文本输入框上。
活动的启动模式
standard模式
standard 是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。
在 standard 模式(即默认情况)下,每当启 动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用 standard 模式的活动, 系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。singleTop 模式
当活动的启动模式指定为 singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
每当想要再启动一个 FirstActivity 时都会直接使用栈顶的活动,因此 FirstActivity 也只会有一个实例,仅按一次 Back 键就可以退出程序。
不过当 FirstActivity 并未处于栈顶位置时,这时再启动 FirstActivity,还是会创建新的实例的。singleTask模式
当活动的启动模式指定为 singleTask,每次启动该活动时系统首先会在返回栈中检查是否 存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。singleInstance 模式
指定为singleInstance 模式的活动会启用一个新的返回栈来管理这个活动。
- singleInstancePerTask模式
Android12版本新增运行模式
和singleTask几乎一样,不过singleInstancePerTask不需要为启动的Activity设置一个特殊的taskAffinity才能创建一个新的Task。
小技巧
知晓当前是在哪一个活动
新建一个BaseActivity类,让BaseActivity继承AppCompatActivity,并重写onCreate()方法。
1 | public class BaseActivity extends AppCompatActivity { |
接下来我们需要让 BaseActivity 成为 ActivityTest 项目中所有活动的父类。
而由于 BaseActivity 又是继承自 AppCompatActivity 的,所以 项目中所有活动的现有功能并不受影响,它们仍然完全继承了 Activity 中的所有特性。
现在每当我们进入到一个活动的界面,该活动的类名就会被打印出来,这样我们就可以时时 刻刻知晓当前界面对应的是哪一个活动了
随时随地退出程序
用一个专门的集合类对所有的活动进行管理
1 | public class ActivityCollector { |
我们通过一个 List 来暂存活动,然后提供了一个 addActivity()方法用 于向 List 中添加一个活动,提供了一个 removeActivity()方法用于从 List 中移除活动,最后提 供了一个 finishAll()方法用于将 List 中存储的活动全部销毁掉。
还要修改BaseActivity类中的代码
1 | public class BaseActivity extends AppCompatActivity { |
在 BaseActivity 的 onCreate()方法中调用了 ActivityCollector 的 addActivity()方 法,表明将当前正在创建的活动添加到活动管理器里。然后在 BaseActivity 中重写 onDestroy() 方法,并调用了 ActivityCollector 的 removeActivity()方法,表明将一个马上要销毁的活 动从活动管理器里移除。
从此以后,不管你想在什么地方退出程序,只需要调用 ActivityCollector.finishAll() 方法就可以了。
前提是:所有类都继承了BaseActivity。
- 本文标题:先从看得到的入手.Android学习.02
- 本文作者:萧禾财
- 创建时间:2022-07-26 14:22:20
- 本文链接:https://ipartmentxhc.github.io/2022/07/26/先从看得到的入手-Android学习-02/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!