13. Jetpack
1. 依赖
lifecycle
MainActivity2
package com.example.helloworld
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
class MainActivity2 : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.second_layout)
// toolbar 部分
val toolbar: Toolbar = findViewById(R.id.toolbar2)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) // 设置返回按钮
toolbar.setNavigationOnClickListener { finish() } // 设置结束 act2
ActivityCollector.addActivity(this)
// 从前一个 Activity 接收数据
val extraData1 = intent.getStringExtra("param1")
val extraData2 = intent.getStringExtra("param2")
Log.d("Activity2", "param1 is $extraData1, param2 is $extraData2")
// 返回数据(但是未实现)
val button2: Button = findViewById(R.id.button2) // 通过 id 找到 view, 并指明类型为 button
button2.setOnClickListener {
val intent = Intent().apply { putExtra("data_return", "Hello Activity1") }
setResult(RESULT_OK, intent)
finish()
}
// 关闭所有 Activity
val button3: Button = findViewById(R.id.button3)
button3.setOnClickListener {
ActivityCollector.finishAll()
}
val button4: Button = findViewById(R.id.button4)
button4.setOnClickListener {
startNewActivity(this, MainActivity3::class.java)
}
val button5: Button = findViewById(R.id.button5)
button5.setOnClickListener {
startNewActivity(this, MainActivity4::class.java)
}
val button7: Button = findViewById(R.id.button7)
button7.setOnClickListener {
startNewActivity(this, FragmentActivity::class.java)
}
val button8: Button = findViewById(R.id.button8)
button8.setOnClickListener {
startNewActivity(this, FileStorage::class.java)
}
val button9: Button = findViewById(R.id.button9)
button9.setOnClickListener {
startNewActivity(this, DataPreference::class.java)
}
val button10: Button = findViewById(R.id.button10)
button10.setOnClickListener {
startNewActivity(this, SqliteDB::class.java)
}
val button11: Button = findViewById(R.id.button11)
button11.setOnClickListener {
startNewActivity(this, Design::class.java)
}
val button12: Button = findViewById(R.id.button12)
button12.setOnClickListener {
startNewActivity(this, JetpackDemo::class.java)
}
}
}
second_layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Back" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Quit App" />
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Go third" />
<Button
android:id="@+id/button5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Go forth" />
<Button
android:id="@+id/button7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Go Fragment" />
<Button
android:id="@+id/button8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="File Storage" />
<Button
android:id="@+id/button9"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SharedPreferences" />
<Button
android:id="@+id/button10"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SQLite" />
<Button
android:id="@+id/button11"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="design" />
<Button
android:id="@+id/button12"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="jetpack" />
</LinearLayout>
2. 概念
- ViewModel
- LiveData
- 背景
- 在 Activity 中手动获取 ViewModel 中的数据
- 但是 ViewModel 却无法将数据的变化主动通知给 Activity
3. 代码
JetpackDemo
package com.example.helloworld
import android.arch.lifecycle.Transformations
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import androidx.core.content.edit
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class MainViewModel(countReserved: Int) : ViewModel() {
val counter: LiveData<Int> // 委托, val 表示引用不变
get() = _counter
private val _counter = MutableLiveData<Int>() // 可变的LiveData, 外部无法调用
init {
_counter.value = countReserved
}
fun plusOne() {
val count = _counter.value ?: 0 // getValue 可能为空,语法糖写法
_counter.value = count + 1
}
fun clear() {
_counter.value = 0
}
}
class MyObserver(val lifecycle: Lifecycle) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START) // 在 onStart 时触发
fun activityStart() {
Log.d("MyObserver", "activityStart")
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun activityStop() {
Log.d("MyObserver", "activityStop")
}
}
class MainViewModelFactory(private val countReserved: Int) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T { // 【理解】
return MainViewModel(countReserved) as T
}
}
class JetpackDemo : BaseActivity() {
lateinit var viewModel: MainViewModel
lateinit var sp: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.jetpack_layout)
sp = getPreferences(Context.MODE_PRIVATE) // 读取 sp 实例
val countReserved = sp.getInt("count_reserved", 0) // 如果没有则默认 0
// 不能直接创建 ViewMode 实例, 因为每次调用 onCreate 都会创建新的
// kotlin 中的生命周期不是栈,因此不存在因为自动销毁,根据引用计数销毁
viewModel = ViewModelProvider(this, MainViewModelFactory(countReserved))
.get(MainViewModel::class.java)
val plusOneBtn: Button = findViewById(R.id.plusOneBtn)
plusOneBtn.setOnClickListener {
viewModel.plusOne()
}
val clearBtn: Button = findViewById(R.id.clearBtn)
clearBtn.setOnClickListener {
viewModel.clear()
}
viewModel.counter.observe(this) { count -> // 观察者模式更新数据
val infoText: TextView = findViewById(R.id.infoText)
infoText.text = count.toString() // 数字保存在 viewModel 中
Log.d("lifecycle", "${lifecycle.currentState}") // 输出 resumed
}
lifecycle.addObserver(MyObserver(lifecycle)) // 是这么调用吗? 查看的哪个对象,好像没有参数这两句话都有输出
Log.d("lifecycle", "${lifecycle.currentState}") // 输出 initialized,因为还没完全创建
}
override fun onPause() { // 离开前台后保存数据
super.onPause()
sp.edit {
putInt("count_reserved", viewModel.counter.value ?: 0)
}
}
}
jetpack_layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/infoText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="32sp"/>
<Button
android:id="@+id/plusOneBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Plus One"/>
<Button
android:id="@+id/clearBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Clear"/>
</LinearLayout>