《第一行代码》 第三版 - 第三章(笔记)
Posted 小柴的回忆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《第一行代码》 第三版 - 第三章(笔记)相关的知识,希望对你有一定的参考价值。
先从看得到的入手,探究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会保证该类只有
以上是关于《第一行代码》 第三版 - 第三章(笔记)的主要内容,如果未能解决你的问题,请参考以下文章
Android 学习之《第一行代码》第三版 笔记Kotlin 继承时的括号到底写不写