《第一行代码》 第三版 - 第三章(笔记)

先从看得到的入手,探究Activity

1.Activity

Activity是一个可以包含应用户界面的组件,主要用于和用户进行交互。
一个应用可以包含 零个或多个Activity,虽然应用可以没有Activity,但是最好还是要有,以为应用就是为了和用户交互,而交互就要用到Activity

2.创建Activity

创建项目的时候可以默认创建一个主Activity
也可以右键包名,创建一个Activity

创建Activity有几个选项
第一个是Activity的名字
第二个是Generate Layout File,选择是否自动添加一个布局文件
第三个是布局文件xml的名字,只有第二个选择了,才会有这个
第四个是Launcher Activity,是否设置为主Activity,也就是应用启动的第一个Activity
第五个是包名,一般不改,因为你在哪个包名创建,一般就要创在那,
第六个就是选择编程语言,有两种,Java和Kotlin,现在我们学第一行代码,所以就使用Kotlin叭
在这里插入图片描述

3.注册Activity

创建Activity后,在AndroidManifest中就会自动注册 MainActivity2,这就是注册Activity的方法,而如果创建Activity时选择了Launcher Activity则会生成MainActivity的样子,因为里面那两句标签就是用于声明该activity为主Activity。
在这里插入图片描述

4.在Activity中使用Toast与Menu

//Toast
override fun onCreate(savedInstanceState: Bundle?) {
    
    
	super.onCreate(savedInstanceState)
	setContentView(R.layout.activity_main)
	val button1 : Button = findViewById(R.id.button1)
	//第一个参数是context, 第二个参数是弹出的内容,第三个是弹出的时间,有LENGTH_SHORT和LENGTH_LONG两种选择
	button.setOnclickListener {
    
    
		Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
	}
}

//在res文件夹下创建一个menu文件夹,在menu文件夹中创建一个xml文件
在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns: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>

Kotlin小特色

在Java中,设置一个简单类,可以自动生成Getter和Setter方法,我们通过这些方法访问类里面的字段,如:

public class Book{
    
    
	private int pages;
	public int getPages(){
    
    
		return pages;
	}
	public void setPages(int pages){
    
    
		this.pages = pages;
	}
}

但在Kotlin中,有更简便的方法

val book = Book()
book.pages = 500 //设置book的pages为500
val bookPages = book.pages //获取book的pages的页数

所以在Activity中的写法可以简写

//创建才菜单栏
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    
    
	//这里的menuInflater 就是通过getMenuInflater()获取的对象的简写
    menuInflater.inflate(R.menu.main_menu, menu)
    return true
}
//菜单的点击事件的监听
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    
    
     when(item.itemId){
    
    
         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
    }

5.Intent

Intent是用于交互与通讯的,而在Activity中Intent就是用于Activity之间的跳转。
Intent分为显示Intent和隐式Intent

5.1显式Intent

显式的意思就是,明确的告诉你,我要找谁。
在第一个Activity中添加一个Button,在设置Button的监听事件,只要按了就触发下面代码跳转Activity

class MainActivity : AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btn.setOnClickListener{
    
    
        	//指明我要跳转的地方就是SecondActivity,意图是非常的明显
            val intent = Intent(this, SecondActivity::class.java)
            startActivity(intent)
        }
    }
}

认真看代码的学霸们,应该知道,我上面的代码中,少了一个初始化Button的代码

val btn: Button = findViewById(R.id.btn1)

其实是我添加了一个插件,Kotlin就能自动帮我们编译和布局文件名字相同的变量
但他内部其实也还是使用findViewById来调用的

plugins {
    
    
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
}

在这里插入图片描述
但是但是,现在来说,这个插件Google已经不在默认使用了,这是因为Google已经废弃了该插件,而现在推荐使用的是ViewBinding

5.2隐式Intent

它并不指明我要启动的是谁,而是通过一系列的抽象信息,让系统自行去分析要启动的是哪个Activity
SecondActivity设置了两个标签,只有标签和标签能同时响应,那才可以进行intent跳转
一个是标签,设置为可以响应com.example.appactivity.ACTION_START
另一个是标签,包含一些附加信息,更精确的指明当前Activity能够响应的。一个Activity中可以有多个标签

<activity android:name=".SecondActivity">
      <intent-filter>
          <action android:name="com.example.appactivity.ACTION_START"/>
          <category android:name="android.intent.category.DEFAULT"/>
      </intent-filter>
  </activity>

这里虽然可以运行了,但是不是说了必须要两个标签都对应吗,怎么少了,其实是因为有一个默认标签 android.intent.category.DEFAULT

btn.setOnClickListener{
    
    
	val intent = Intent("com.example.appactivity.ACTION_START")
    startActivity(intent)
}

当我们添加category的标签,但运行的时候,发现,报错了,这是因为系统没找到有一个Activity的属性是 com.example.appactivity.MY_CATEGORY

btn.setOnClickListener{
    
    
	val intent = Intent("com.example.appactivity.ACTION_START")
	intent.addCategory("com.example.appactivity.MY_CATEGORY")
    startActivity(intent)
}

我们在SecondActivity的注册中添加一条

<activity android:name=".SecondActivity">
      <intent-filter>
          <action android:name="com.example.appactivity.ACTION_START"/>
          <category android:name="android.intent.category.DEFAULT"/>
          <category android:name="com.example.appactivity.My_CATEGORY"/>
      </intent-filter>
  </activity>

5.3更多隐式Intent

用于启动其他程序的Activity

val btn : Button = findViewById(R.id.btn)
btn.setOnClickListener{
    
    
	//打开浏览器
    val intent = Intent(Intent.ACTION_VIEW)
    intent.data = Uri.parse("https://www.baidu.com")
    startActivity(intent)
    //拨打电话
    val intent = Intent(Intent.ACTION_DIAL)
    intent.data = Uri.parse("tel:10086")
    startActivity(intent)
}

5.4 companion object

该关键字中的所有方法都类似于Java静态方法一样,可以直接通过UIUitl.actionStart()使用。

6.Activity传递数据

Activity传递数据,使用的是Intent附带数据,先构件好Intent意图,然后通过intent.putExtra()方法传递数据,两个参数,第一个是数据名,第二个是数据(键值对)

//FirstActivity
val btn : Button = findViewById(R.id.btn)
btn.setOnClickListener{
    
    
    val data = "Hello SecondActivity"
    val intent = Intent(this, SecondActivity::class.java)
    intent.putExtra("extra_data", data)
    startActivity(intent)
}
//SecondActivity中,一般在onCreate中获取数据
//这里只使用了getStringExtra,相对应其他是getIntExtra,等等,对应好传递的数据的类型
val extraData = intent.getStringExtra("extra_data")

返回数据给FirstActivity。即SecondActivity返回数据给FirstActivity

//FirstActivity跳转
//这里和上面的差不多,区别只在于上面使用startActivity(),这里使用了startActivityForResult(),这仅是进行Activity跳转
val btn : Button = findViewById(R.id.btn)
btn.setOnClickListener{
    
    
    val data = "Hello SecondActivity"
    val intent = Intent(this, SecondActivity::class.java)
    intent.putExtra("extra_data", data)
    startActivityForResult(intent, 1)
}
//SecondActivity的操作
btn.setOnClickListener{
    
    
    val data = "Hello FirstActivity"
    val intent = Intent()
    intent.putExtra("extra_data", data)
    setResult(2, intent) //返回结果
    finish()//注销了当前页面,才会返回上一个页面
}

//返回数据的处理
//为什么会有requestCode与resultCode,这是因为,一个页面可能有很多个不通的跳转,一个页面也有很多个不通的返回,所以这样才能精准的知道,你是哪里来的结果
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    
    
    super.onActivityResult(requestCode, resultCode, data)
    if(requestCode == 1){
    
     //这个是发送跳转的结果Code
        if(resultCode == 2){
    
     //这是返回的结果Code
            //处理逻辑,第三个data,就是第二个页面跳转时带的intent,里面可能有附带的数据
        }
    }
}

7.Activity

7.1 Activity的状态

  • 运行状态, 可以与用户互动,系统最不愿回收的Activity
  • 暂停状态,可以看到,但不能互动,例如在Activity上弹一个Dialog时,Activity就是暂停,看得到,你无法互动,你所有互动都会在Dialog上,只有内存极地的时候才可能会回收
  • 停止状态,无法互动,也无法看到,Activity不在栈顶,但会保存各种变量与状态,内存不够的时候,可能会被回收
  • 销毁状态,从任务栈中推出,系统会快速回收该Activity,以保证内存的足够

7.2 Activity的生命周期

  • onCreate(),Activity第一个被创建的时候会调用,一般用于初始化操作
  • onStart(), Activity不可见到可见的时候调用, 用户可见,但不可互动
  • onResume(), Activity准备好与用户互动的时候调用,当前Activity位于栈顶,可见可互动
  • onPause(), 准备去调用或回复其他Activity时调用,应释放一些资源与保存一些重要的数据
  • onStop(),Activity完全不可见的时候调用,与onPause()的主要区别在于,如果是调用一个对话框的Activity时,该方法不会被调用
  • onDestroy(),在Activity被销毁钱调用,之后Activity成为销毁状态,等待被回收
  • onRestart(),在从停止状态下变成运行状态下的时候调用,也就是被停止的Activity重新启动

7.3Activity生命周期图

在这里插入图片描述

7.4 生存周期

7个生命周期方法,对应着3个生存期
完整生存期:onCreate()与 onDestroy(),创建到销毁
可见生存期:onStart() 与 onStop(),该周期,可见,但不一定可互动
前台生存期:onResume() 与 onPause() ,该周期位于栈顶,可见可互动

7.5Activity被回收的问题

在上面生命周期中有将,Activity在停止下可能会被回收,相信大家也有这种情况:打开某个App的某个页面,切到后台,没删除,但下次进来,这个页面就重新启动了,这就是因为这个页面被回收了,再次点开的时候,就是重新启动这个Activity。这里就存在了数据的问题
所以Activity提供了一个onSaveInstanceState()来保存数据
该方法携带一个Bundle类型的参数,用于保存数据

//该方法可以保存参数
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
    
    
    super.onSaveInstanceState(outState, outPersistentState)
    val tempData = "Something you just type"
    outState.putString("data_key", tempData)
}
//在onCreate方法中传入的就是saveInstanceState
override fun onCreate(savedInstanceState: Bundle?) {
    
    
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    //这里判断是否有数据,因为第一次进入该Activity是没数据的,但你保存数据后重启的Activity是有数据的
    if(savedInstanceState != null){
    
    
        val tempData = savedInstanceState.getString("data_key")
    }
}

8. Activity的启动模式

Activity的启动模式有4种

8.1 standard

默认启动模式,只要创建了一个Activity,就会新添加一个activity到栈顶。无论该Activity之前是否有创建过一个实例

FirstActivity 启动一个SecondActivity,SecondActivity启动一个FirstActivity,FirstActivity启动一个FirstActivity
栈的情况:FirstActivity - FirstActivity - SecondActivity - FirstActivit(从高到底)

//不采取任何修改,默认即可
val btn : Button = findViewById(R.id.btn)
btn.setOnClickListener {
    
    
    val intent = Intent(this, MainActivity::class.java)
    startActivity(intent)
}

8.2 singleTop

栈顶复用模式,只要创建了一个新的Activity实例,但栈顶的activity实例是该类的activity就会复用栈顶的activity

FirstActivity 启动一个SecondActivity,SecondActivity启动一个FirstActivity,FirstActivity启动一个FirstActivity
栈的情况:FirstActivity - SecondActivity - FirstActivit(从高到底)

<activity android:name=".MainActivity"
    android:launchMode="singleTop"> 
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

8.3 singleTask

栈中复用模式,只要创建的Activity实例在栈中存在,那么就将该实例置顶,上面的全部弹出

FirstActivity 启动一个SecondActivity,SecondActivity启动一个ThirdActivity,ThirdActivity启动一个SecondActivity
栈的情况:SecondActivity - FirstActivit(从高到底)

<activity android:name=".MainActivity"
    android:launchMode="singleTask"> 
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

8.4 singleInstance

单例模式,显而易见,单独一个Activity使用一个栈,这个栈也只有这一个Activity

<activity android:name=".MainActivity"
    android:launchMode="singleInstance"> 
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

9.Kotlin 小课堂

9.1with函数

with()函数的作用,可以使连续使用同一个对象的多个方法时让代码变得更加简单
with()函数接受两个参数,一个是任意类型的对象,第二个是Lambda表达式,Lambda提供第一个参数对象的上下文,最后一行作为返回值返回

val list = listOf("Apple", "Apple", "Apple", "Apple", "Apple")
val builder = StringBuilder()
builder.append("Start eating fruits.\n")
for(fruit in list){
    
    
    builder.append(fruit).append("\n")
}
builder.append("Ate all fruits.")
val result = builder.toString()
println(result)
//with简写后
//StringBuilder()获取一个StringBuilder对象,并传入Lambda表达式,最后返回对象的toString方法
val list = listOf("Apple", "Apple", "Apple", "Apple", "Apple")
val result = with(StringBuilder()){
    
    
    append("Start eating fruits.\n")
    for(fruit in list){
    
    
        append(fruit).append("\n")
    }
    append("Ate all fruits.")
    toString()
}
println(result)

9.2run函数

run与with差不多,但不同的是,run是通过对象调用的,同时只有一个参数:Lambda表达式,表达式上下文就是该对象

val list = listOf("Apple", "Apple", "Apple", "Apple", "Apple")
val result = StringBuilder().run {
    
    
    append("Start eating fruits.\n")
    for(fruit in list){
    
    
        append(fruit).append("\n")
    }
    append("Ate all fruits.")
    toString()
}
println(result)

9.3apply函数

apply与run也很想,都是通过对象来调用的,同样只接受一个Lambda对象,也会再Lambda表达式中提供对象作为上下文,但apply是不返回值的,而是自动返回对象本身

val list = listOf("Apple", "Apple", "Apple", "Apple", "Apple")
val result = StringBuilder().apply {
    
    
    append("Start eating fruits.\n")
    for(fruit in list){
    
    
        append(fruit).append("\n")
    }
    append("Ate all fruits.")
}
println(result.toString())

9.4 静态方法

在Java中,在方法上通过static 声明就可以成为一个静态方法

//静态方法
public class Util{
    
    
	public static void doSomething(){
    
    
		System.out.println("嘻嘻嘻")
	}
} 
//使用方法
Util.doSomething()

在Kotlin中,弱化了静态方法的概念,这是因为在Kotlin中,有一种更好的使用办法,那就是创建一个单例类。

object Util{
    
    
	fun doSomething(){
    
    
		println("嘻嘻嘻")
	}
}
//使用办法
Util.doSomething
//使用了object让类变成单例类后,所有方法都会变成类静态方法的使用
//那么久有companion object的关键字出现,使得该类只有这个代码段中的方法是类静态方法
class Util{
    
    
	fun doSomething(){
    
    
		println("嘻嘻嘻")
	}
	companion object{
    
    
		fun doAction(){
    
    
			println("哈哈哈")
		}
	}
}
//使用办法:
doSomething()方法必须创建该类的实例才能使用
doAction()方法使用类静态方法调用既可Util.doAction()

companion object 关键字实际会在类的内部创建一个伴生类,doAction()方法会定义在这个伴生类里面的实例方法,而Kotlin会保证该类只有一个伴生类对象,所以Util.doAction()方法的调用,其实就是调用伴生类的doAction()方法

真正的静态方法创建:注解与顶层方法
注解:
在单例类或companion object中的方法加上@JvmStatic注解,那么该方法就会编译成静态方法。注意:如果注解加在其他普通的方法上,会报错

class Util{
    
    
	fun doSomething(){
    
    
		println("嘻嘻嘻")
	}
	companion object{
    
    
		@JvmStatic
		fun doAction(){
    
    
			println("哈哈哈")
		}
	}
}

顶层方法
创建一个Kotlin文件,不创建类,直接在里面定义方法,该方法就顶层方法

//创建一个Test.kt的文件内容只有包名 + 下面三行
package com.example.text
fun doAction(){
    
    
	println("哈哈哈")
}

那么,不用任何修改,直接在任何地方都可以直接使用doAction()来使用该方法

override fun onCreate(savedInstanceState: Bundle?) {
    
    
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    doAction()
}

Kotlin会自动将Test.kt文件编译成TestKt的Java类,因此,这样在Java中也可以这样使用:

public void test(){
    
    
	TestKt.doAction()
}

9.5 intent的最佳使用方式

如果你要跳转到SecondActivity时,通常都是这样调用,这样调用没问题,但建议写在SecondActivity的上边,作为一个类静态方法,同时使用上apply函数:

//原:
 val intent = Intent(this, MainActivity::class.java)
 intent.putExtra("data1", "text")
 intent.putExtra("data2", 10086)
 startActivity(intent)
 //更新后,简单明了
 class SecondActivity: AppCompatActivity() {
    
    
	companion object{
    
    
		val intent = Intent(context, SecondActivity::class.java).apply{
    
    
			putExtra("data1", "text")
			putExtra("data2", 10086)
		}
		context.startActivity(intent)
	}
}

猜你喜欢

转载自blog.csdn.net/pp520henni/article/details/120854515