第一行代码 Android 阅读笔记 第三章(仅自用)

Activity

主要负责与用户的交互
新建activity时选项有三:Generate Layout File、Launcher Activity、Backwards Compatibility。

  • Generate Layout File : 自动为新Activity创建对应布局文件。
  • Launcher Activity : 是否将新Activity设置为主Activity。
  • Backwards Compatibility : 是否让项目启用向下兼容的模式。

设计布局

<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/button1"  //引用id则无+,定义id则需要
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="Button 1"/>
</LinearLayout>
//在setContentView()中传入布局id来为activity加载布局
setContentView(R.layout.first_layout)

注册Activity

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.example.activitytest">
 <application
 ...>
 //表明是为FirstActivity注册,因为前面已经给定包名,因此省略
 <activity android:name=".FirstActivity"
 //指定标题栏内容
 android:label="This is FirstActivity">
 <intent-filter>
 //将此Activity作为主Activity,在应用被点击打开时先打开此Activity
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>
 </application>
</manifest>

Toast

提供短小信息提醒工具

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.first_layout)
    //使用Toast前需要为其找到一个触发点,通过findViewById()方法获取布局文件中的元素的实例
    //findViewById返回一个继承自View的泛型对象,因此需要显式声明为Button对象
    //但由于已经引入了kotlin-android-extensions插件,
    //此插件根据布局文件中定义的控件id自动生成了一个具有相同名称的变量
    //因此可直接使用该变量,无需使用findViewById
    val button1: Button = findViewById(R.id.button1)
    //注册监听器
    button1.setOnClickListener {
       //创建出一个Toast对象 传入上下文、文本内容、显示时间作为参数,通过show方法显示
       //Activity为Context对象,即上下文,传入this
       //显示时间有内置常量可选:Toast.LENGTH_SHORT,Toastt.LENGTH_LONG
       Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
  }
}

添加菜单

override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {//同样隐含语法糖 背后调用getItemId
            R.id.add_item -> Toast.makeText(this, "You clicked Add",
                Toast.LENGTH_SHORT).show()
            R.id.remove_item -> Toast.makeText(this, "You clicked Remove",
                Toast.LENGTH_SHORT).show()
        }
        return true
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        //添加菜单,此处隐含kotlin语法糖,即背后调用get和set方法。
        //menuInflater实际上是调用了父类的getMenuInflater()方法得到的MenuInflater对象,
        //再调用inflate方法,
        //参数1表示使用哪个资源文件来创建菜单,
        //参数2表示将菜单项添加到哪个Menu对象当中。
        menuInflater.inflate(R.menu.main,menu)
        return true
    }

销毁Activity

button1.setOnClickListener {
 finish()//效果同按back
}

使用Intent穿梭Activity

指明当前组件想要执行的动作,还可在不同组件间传递数据、
启动Activity、启动Service、发送广播等...

Intent又分显式和隐式

显式使用Intent

button1.setOnClickListener {
//Intent有多个构造函数的重载,以下为其中一种,接受两个参数,
//参数1为启动Activity的上下文,
//参数2为目标Activity,
//此构造函数构建出Intent的“意图”
 val intent = Intent(this, SecondActivity::class.java)
 startActivity(intent)
}

隐式使用Intent
不明确指定想要启动哪个Activity,而是指定了Action、Category等信息,让系统判断应该启动哪个Activity。

<activity android:name=".SecondActivity" >
 <intent-filter>
 //指明SecondActivity可以响应
 //com.example.activitytest.ACTION_START这个action
 <action android:name="com.example.activitytest.ACTION_START" />
 //<category>标签则包含了一些附加信息,
//更精确地指明了当前Activity能够响应的Intent中还可能带有的category。
 <category android:name="android.intent.category.DEFAULT" />
 </intent-filter>
</activity>

只有当Intent中的action和category都精准命中时,才能启动该Activity

button1.setOnClickListener {
//此时仍可命中,因为category是default,
//而startActivity则是自带默认category
//1个intent只可指定一个action,但可指定多个category
 val intent = Intent("com.example.activitytest.ACTION_START")
 //没有满足条件的activity可被启动
 intent.addCategory("com.example.activitytest.MY_CATEGORY")
 startActivity(intent)
}

隐式Intent还可打开其他应用的Activity或者打开网页

button1.setOnClickListener {
//Intent.ACTION_VIEW为系统内置的一个动作
 val intent = Intent(Intent.ACTION_VIEW)
 //Uri.parse将字符串转化为Uri对象,再通过setData方法传入Intent
 intent.data = Uri.parse("https://www.baidu.com")
 startActivity(intent)
}

指定Activity能够响应的数据

//data标签内可添加内容
android:scheme。用于指定数据的协议部分,如上例中的https部分。
android:host。用于指定数据的主机名部分,如上例中的www.baidu.com部分。
android:port。用于指定数据的端口部分,一般紧随在主机名之后。
android:path。用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内
容。
android:mimeType。用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
<activity android:name=".ThirdActivity">
 <intent-filter tools:ignore="AppLinkUrlError">
 <action android:name="android.intent.action.VIEW" />
 <category android:name="android.intent.category.DEFAULT" />
 //使得ThirdActivity可以响应一个打开网页的intent,但as认为所有能响应action.VIEW的activity需要在category添加BROWSABLE(实现deep link)
 <data android:scheme="https" />
 </intent-filter>
</activity>

使用Intent传输数据

//发送数据
button1.setOnClickListener {
 val data = "Hello SecondActivity"
 val intent = Intent(this, SecondActivity::class.java)
 //作为键值对,参数1是键值,参数2为数据
 intent.putExtra("extra_data", data)
 startActivity(intent)
}
//传输数据
class SecondActivity : AppCompatActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.second_layout)
 //此处调用父类的getIntent,获取启动SecondActivity的intent,再通过getExtra获取数据
 val extraData = intent.getStringExtra("extra_data")
 Log.d("SecondActivity", "extra data is $extraData")
 }
}

返回数据到上一个Activity

//startActivityForResult()
button1.setOnClickListener {
 val intent = Intent(this, SecondActivity::class.java)
 //参数1为intent,参数2为请求码,在回调中判断数据的来源,且值唯一
 startActivityForResult(intent, 1)
}
//返回数据
class SecondActivity : AppCompatActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.second_layout)
 button2.setOnClickListener {
 val intent = Intent()
 intent.putExtra("data_return", "Hello FirstActivity")
 //向上一个Activity返回数据
 //参数1一般为RESULT_OK或RESULT_CANCELED
 //参数2为intent
 setResult(RESULT_OK, intent)
 //销毁Activity
 finish()
 }
 }
}
//在Activity被销毁后,会回调发起跳转的Activity中的onActivityResult(),
//参数1为请求码,参数2为处理结构
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 super.onActivityResult(requestCode, resultCode, data)
 when (requestCode) {
    1 -> if (resultCode == RESULT_OK) {
    val returnedData = data?.getStringExtra("data_return")
    Log.d("FirstActivity", "returned data is $returnedData")
   }
  }
}
//以上操作为按压按钮时才能出现,如果按返回键则无法触发,因此重写onBackPressed()
override fun onBackPressed() {
 val intent = Intent()
 intent.putExtra("data_return", "Hello FirstActivity")
 setResult(RESULT_OK, intent)
 finish()
}

Activity生命周期

  • 返回栈
    android中的activity是可以层叠的,每启动一个就会启动一个新的activity覆盖在旧的activity上,按back则可销毁,旧activity则会重新出现。
    实际上android使用task(任务)来管理activity的,一个任务即一组存放在栈内的activity的集合,该栈被称为返回栈。
    每当按下Back或调用finish,就会销毁一个activity,使得处在栈顶的activity就会出栈,前一个入栈的activity就会重新处于栈顶位置,系统总是显示位于栈顶的activity给用户
    在这里插入图片描述
  • 状态
    运行状态:当一个activity处于栈顶时即是处于运行状态。
    暂停状态:activity不处于栈顶,但仍可视,仍存活。除非内存极低,不然不会被回收。
    停止状态:activity不处于栈顶,且不可视。系统为该activity保留相应状态和成员变量。在其他地方需要内存时,该activity可能会被收回。
    销毁状态:activity从返回栈中被移除就进入销毁状态
  • 生存期
    onCreate(): 在activity第一次被创建时调用,用于完成activity的初始化操作,如加载布局,绑定事件等。
    onStart():在activity由不可见变为可见时调用。
    onPause():在系统启动或恢复另一个activity时调用此方法,该方法通常会将一些消耗cpu的资源释放掉,并保存一些关键数据。该方法执行速度需要快,避免影响新的栈顶activity的使用。
    onStop(): 在activity完全不可见时调用,当启动的activity是对话框式的activity,则调用onPause,而onStop不会被调用
    onDestory():在activity被销毁前调用,之后activity会变为销毁状态
    onRestart():在activity由停止状态变为运行状态前调用,即activity被重新启动

activity还能分为三种生存期

  • 完整生存期:在onCreate()到onDestory()之间经历的就是完整生存期,onCreate完成各种初始化操作,onDestory完成释放内存的操作。
  • 可见生存期:onStart()到onStop()之间经历的就是可见生存期,在此期间,activity总是对用户可见,即使无法与用户交互,但仍可通过这两个方法分别进行加载资源和释放资源。
  • 前台生存期:onResume和onPause之间为前台生存期,在此期间activity总为运行状态,与用户进行交互。

在这里插入图片描述
以上各种on方法调用顺序:
当打开activity1时,依次调用onCreate、onStart、onResume,
跳转打开activity2,a1依次调用onPause、onStop,
返回a1,a1依次调用onRestart、onStart、onResume,
在a1打开对话框式activity3,a3仅部分遮挡,因此a1仅调用onPause,
关闭a3后,a1仅调用onResume,
按back 销毁a1,a1调用onPause、onStop、onDestory。

恢复被销毁Activity的数据

activity调用onStop可能因为内存不够被销毁了,想要保存数据,需要重写onSaveInstanceState

//bundle有点雷同intent,以键值对形式保存数据
override fun onSaveInstanceState(outState: Bundle) {
 super.onSaveInstanceState(outState)
 val tempData = "Something you just typed"
 outState.putString("data_key", tempData)
}
//onCreate 参数内存bundle,默认为null
override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 Log.d(tag, "onCreate")
 setContentView(R.layout.activity_main)
 if (savedInstanceState != null) {
 //读出bundle中的数据,即可自行选择重新填入
 val tempData = savedInstanceState.getString("data_key")
 Log.d(tag, "tempData is $tempData")
 }
}

Activity的启动模式

  • standard:默认启动模式,新activity进入栈中并被置于栈顶,因此会出现不断创建同一个新的activity覆盖在旧的但完全相同的activity上。
  • single top:当需创建的新activity已经在栈顶有了,则不再创建,直接使用。但当被创建的activity不在栈顶则还是会出现同样的问题。
<activity
 android:name=".FirstActivity"
 //修改启动模式
 android:launchMode="singleTop"
 android:label="This is FirstActivity">
 <intent-filter>
 <action android:name="android.intent.action.MAIN"/>
 <category android:name="android.intent.category.LAUNCHER"/>
 </intent-filter>
</activity>
  • single task:创建新的activity时,会检查是否已经存在于栈中,如果存在,则直接使用,且将该activity上面的所有activity全部出栈。
    例:a1启动,跳转a2,在a2再跳转a1,a1调用的是restart,且此时a2调用destory被销毁。
  • single instance:使用一个新的栈来管理该activity,如a2使用single instance来启动activity,a1,a3为默认,
    从a1跳转至a2,则此时出现两个栈,栈1栈顶为a1,栈2栈顶为a2,
    在a2启动a3,a3则处于栈1栈顶,
    此时按back返回,销毁a3,a1为栈1栈顶,因此显示在最前,
    再按back,销毁a1,此时栈1为空,因此显示栈2栈顶的a2,
    再按back,销毁栈2,退出应用。

Activity的最佳实践

  • 知道当前处于哪个activity:自定义基类BasicActivity,在构造函数里添加:
//获取当前实例的class对象,再通过simpleName获取当前实例类名
Log.d("BaseActivity", javaClass.simpleName)

再使得所有activity继承该基类,则可每跳转一个activity则可自动打印其名字

  • 一次性清空所有activity
object ActivityCollector {
 private val activities = ArrayList<Activity>()
 //将新的activity加入唯一的arraylist中
 fun addActivity(activity: Activity) {
 activities.add(activity)
 }
 //将activity移出集合
 fun removeActivity(activity: Activity) {
 activities.remove(activity)
 }
 //销毁集合中所有activity,但清除前需要判断是否已经被销毁(back),最后销毁完还需要清空集合
 fun finishAll() {
 for (activity in activities) {
 if (!activity.isFinishing) {
 activity.finish()
 }
 }
 activities.clear()
 }
}
//在基类中添加activity单例类的方法
open class BaseActivity : AppCompatActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 Log.d("BaseActivity", javaClass.simpleName)
 ActivityCollector.addActivity(this)
 }
 override fun onDestroy() {
 super.onDestroy()
 ActivityCollector.removeActivity(this)
 }
}

class ThirdActivity : BaseActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 Log.d("ThirdActivity", "Task id is $taskId")
 setContentView(R.layout.third_layout)
 button3.setOnClickListener {
 //此时便可一键销毁所有activity
 ActivityCollector.finishAll()
 //杀死当前进程
 android.os.Process.killProcess(android.os.Process.myPid())
 }
 }
}
  • 启动Activity最佳方法:
    有时需要启动a2,需要同时传入的数据要求不清晰
class SecondActivity : BaseActivity() {
 ...
 //与java的静态方法雷同
 companion object {
 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)
 }
 }
}
//启动时就十分简洁
button1.setOnClickListener {
 SecondActivity.actionStart(this, "data1", "data2")
}

标准函数with

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
//with有两个参数,参数1为object,即任意对象,参数2为lambda,且会传入参数1的上下文
val result = with(StringBuilder()) {
 append("Start eating fruits.\n")
 for (fruit in list) {
 append(fruit).append("\n")
 }
 append("Ate all fruits.")
 toString()
}
//打印出所有string
println(result)

标准函数run

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
//与with不同在不需要接受对象,且run是在某个对象的基础上调用,但都会将该对象上下文传入lambda,
//并将最后一行作为返回值
val result = StringBuilder().run {
 append("Start eating fruits.\n")
 for (fruit in list) {
www.blogss.cn
 append(fruit).append("\n")
 }
 append("Ate all fruits.")
 toString()
}
println(result)

标准函数apply

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
//整体类似,但仅能返回对象本身,因此仍需自行调用toString
val result = StringBuilder().apply {
 append("Start eating fruits.\n")
 for (fruit in list) {
 append(fruit).append("\n")
 }
 append("Ate all fruits.")
}
println(result.toString())

静态方法和单例类

//单例类,同静态方法,可通过Util.doAction()调用
object Util {
 fun doAction() {
 println("do action")
 }
}
//非单例类,需要实例化才能使用doAction1,但仍可直接调用Util.doAction2
class Util {
 fun doAction1() {
 println("do action1")
 }
 //companion object实际上是在类的内部创建了一个伴生类,
 //而kotlin又保证每个类仅有一个伴生类对象
 companion object {
 fun doAction2() {
 println("do action2")
 }
 }
}

定义静态方法

以上方法不支持在java中使用,因为其并不是真正的静态方法

  • 注解:给单例类或companion中的方法加上@JvmStastic注解,kotlin则会将其编译成真正的静态方法
class Util {
 fun doAction1() {
 println("do action1")
 }
 companion object {
 @JvmStatic
 fun doAction2() {
 println("do action2")
www.blogss.cn
 }
 }
}
  • 顶层方法:没有声明在任何类中的方法,kotlin会将所有顶层方法编译成静态方法。kotlin中直接使用函数名调用,而java中需要以 文件名.函数名来调用。(创建file类型的kotlin文件,将方法写入其中即可)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值