Activity是可以包含用户界面的组件,主要用于和用户进行交互。一个APP中可以包含0个或多个Activity。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.first_layout)
}
在AndroidManifest.xml中找到要设置的活动,在< activity >内部加入
android:label可指定Activity的标题栏。
<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>
如果一个APP没有主Activity,这个APP仍然可以正常安装,只是无法在启动器中看到或者打开这个程序。这种程序一般是作为第三方服务供其他应用在内部进行调用的。
Toast.makeText(this,"内容",Toast.LENGTH_SHORT).show
findViewById方法常用来获取布局文件中控件的实例。不过Kotlin编写的Android项目在app/build.gradle文件头部默认引入了一个kotlin-android-extensions插件,这个插件会根据布局文件中定义的控件id自动生成一个具有相同名称的变量。因此,该方法在Kotlin中几乎不再被使用。
Menu即菜单栏,新建menu文件后按照需要编码。有两种标签,item和group,前者即菜单项,后者是不可见容器,用来对菜单项进行分组,使一组菜单项共享可用性、可见性等属性。
以下是将Menu和Activity绑定的代码:
//将Menu和Activity绑定
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
//自动调用了父类的getMenuInflater方法
menuInflater.inflate(R.menu.main, menu)
return true
}
//为菜单项绑定回调动作
override fun onOptionsItemSelected(item: MenuItem): Boolean {
//自动调用了getItemId方法
when(item.itemId){
R.id.add_item -> Toast.makeText(this,"Add",Toast.LENGTH_SHORT).show()
R.id.remove_item -> Toast.makeText(this,"Remove",Toast.LENGTH_SHORT).show()
R.id.update_item -> Toast.makeText(this,"Update",Toast.LENGTH_SHORT).show()
R.id.baidu -> {
val intent = Intent(Intent.ACTION_VIEW)
//自动调用了intent.setData方法
intent.data = Uri.parse("https://www.baidu.com")
startActivity(intent)
}
}
return true
}
这其中含有一个Kotlin语法糖,即:我们可以直接获取对象的字段,并进行修改或读取,Kotlin会为我们自动调用访问器方法和修改器方法。
Intent的一个构造函数是:
> Intent(Context packageContext, Class> cls)
第一个参数是上下文,第二个参数用于指定想要启动的目标Activity。
用显式Intent启动Activity的代码如下:
val intent = Intent(this,SecondActivity::class.java)
startActivity(intent)
//这里的SecondActivity::class.java 就相当于Java中的SecondActivity.class
隐式Intent并不直接写出要启动哪一个Activity,而是指定一系列action、category等信息,交由系统去分析,如果有完全匹配的活动则启动,没有程序则崩溃。
每个Intent只能指定一个action,但能指定多个category。
用隐式Intent启动Activity的代码如下:
val intent = Intent("com.example.activitytest.ACTION_START")
intent.addCategory("com.example.activitytest.MY_CATEGORY")
startActivity(intent)
此外,隐式Intent还可以启动其他程序的Activity,这就使多APP之间的功能共享成为了可能。如调用系统浏览器打开网页:
val intent = Intent(Intent.ACTION_VIEW)
//自动调用了intent.setData方法
intent.data = Uri.parse("https://www.baidu.com")
startActivity(intent)
与此对应,我们还可以在Activity中的
DeepLink,深度链接技术,主要应用场景是通过Web页面直接调用Android原生app,并且把需要的参数通过Uri的形式,直接传递给app,节省用户的注册成本。简单讲,就是你在手机上点击一个链接之后,可以直接链接到app内部的某个页面,而不是app正常打开时显示的首页。
比如:各种APP的购物分享链接。
用putExtra()将我们要传递的数据暂存在Intent中,在启动另一个Activity后从Intent中取出数据。注意,Intent中的数据以键值对的形式传入传出。
如:
//FirstActivity中传入数据
val intent = Intent(this,SecondActivity::class.java)
intent.putExtra("extra_data",data)//第一个参数是键,第二个参数才是值
startActivity(intent)
//SecondActivity中取出数据
text.text = intent.getStringExtra("extra_data")
如果一个Activity希望在启动的另一个Activity销毁之后传递一个数据,则应用startActivityForResult方法。代码如下:
//在FirstActivity中启动SecondActivity
val intent = Intent(this,SecondActivity::class.java)
startActivityForResult(intent,1)
//这里的1是请求码,用于在之后的回调中判断数据的来源
//在SecondActivity中向FirstActivity传递数据
val intent = Intent()
intent.putExtra("data_return","Hello FirstActivity")
setResult(Activity.RESULT_OK,intent)
回调代码如下:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode){
1 -> if (resultCode == Activity.RESULT_OK){
val returnedData = data?.getStringExtra("data_return")
Toast.makeText(this,returnedData,Toast.LENGTH_SHORT).show()
}
}
}
Activity在其生命周期中最多有四种状态:
Activity类定义了7种回调方法,覆盖了Activity生命周期的每一个环节。
以上7个方法除OnRestart外两两相对,又可分为三种生存期:即。完整生存期、可见生存期和前台生存期。
onCreate和onDestroy方法之间是完整生存期,一般情况下:在onCreate进行初始化操作,在onDestroy方法中释放内存。
onStart和onStop方法之间是可见生存期,一般情况下:在onStart方法中加载资源,在onStop方法中释放资源。
onResume和onPause方法之间是前台生存期,此时的Activity总是处于运行状态,可与用户交互。
A.onPause, B.onCreate, B.onStart,B.onRusume,A.onStop
B.onPause, A,onRestart, A.onStart, A.onResume, B.onStop, B.onDestroy
A.onPause, B.onCreate, B.onStart,B.onRusume
B.onPause, A.onResume, B.onStop, B.onDestroy
如果一个返回栈中的Activity被系统回收了,当返回到该Activity界面的时候会重新创建一个Activity。
如果需要保存数据,可以通过onSaveInstanceState()回调方法。这个方法可以保证Activity被回收之前一定会被调用。该方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,如putString()方法保存字符串,putInt()方法保存整型数据,以此类推。每个保存方法需要传递两个参数,第一个参数是键,第二个参数是值。代码如下:
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val tempData = "Something you just typed"
outState.putString("data_key",tempData)
}
然后,再修改onCreate方法,恢复数据:
if (savedInstanceState!=null){
val tempData = savedInstanceState.getString("data_key")
Log.d(tag,tempData)
}
此外,Intent还可以结合Bundle一起用于传递数据。首先吧需要传递的数据都保存早Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标Activity之后,先从Intent中取出Bundle,再从Bundle中一一取出数据。
Activity启动模式一共有四种:standard、singleTop、singleTask 和singleInstance。
standard是Activity的默认启动模式,在该模式下,每当启动一共新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。
当Activity的启动模式指定为singleTop,在启动Activity时如果发现返回栈的栈顶已经是该Activity,则认为可以直接使用,不会再创建新的Activity实例。
当Activity的启动模式指定为singleTask,每次启动Activity时,系统首先会在返回栈中检查是否存在该Activity,如果发现已经存在则直接使用该实例吗,并把该Activity之上的所有其他Activity统统出栈,如果没有发现就会创建一个新的Activity实例。
不同于以上几种模式,指定为singleInstance模式的Activity会启用一个新的返回栈来管理这个Activity。在这种模式下,会有一个单独的返回栈来管理这个Activity,不管是哪个APP来访问这个Activity,都共用一个返回栈,也就解决了共享Activity实例的问题。
新建一个类继承自AppCompatActivity,在onCreate()中加入以下代码,
Log.d("BaseActivity",javaClass.simpleName)
//这里,我们先获取当前实例的Class对象,然后调用simpleName获取当前实例的类名
注意,Kotlin中的JavaClass表示获取当前实例的Class对象,相当于在Java中调用getClass()方法;
而Kotlin中的ActivityName::class.java表示获取ActivityName类的Class对象,相当于在Java中调用ActivityName.class。
然后再将其他Activity的父类改为该类,重启项目运行程序时观察Logcat即可。
新建一个单例类ActivityCollector作为Activity的集合,每当Activity的onCreate方法调用时就将自己添加到集合中,而当onDestroy方法调用时就将自己从集合中删除。如果我们需要退出程序,就对集合中所有Activity调用finish方法即可。最后还可以杀掉进程。
ActivityCollector代码如下:
object ActivityCollector {
private val activities = ArrayList<Activity>()
fun addActivity(activity: Activity){
activities.add(activity)
}
fun removeActivity(activity: Activity){
activities.remove(activity)
}
fun finishAll(){
for (activity in activities){
if (!activity.isFinishing)
activity.finish()
}
activities.clear()
android.os.Process.killProcess(android.os.Process.myPid())
}
}
如果对一个Activity需要传递哪些数据感动迷惑,可采用下列代码的方法启动该Activity。
//在SecondActivity中加入
companion object {//该关键字用来模拟Java中静态方法的调用
fun actionStart(context : Context, data1 : String, data2: String){
val intent = Intent(context,SecondActivity::class.java)
intent.putExtra("param1",data1)
intent.putExtra("param2",data2)
context.startActivity(intent)
}
}
这样的写法最大的好处就是一目了然,该活动需要哪些数据一眼就看出来了。
被我并入Chapter2中。