本文为jetpack学习案例,来自于
https://www.bilibili.com/video/BV1w4411t7UQ/?p=13
涉及内容:
ViewModel
LiveData
Databinding
android studio 自带图标库
SavedStateHandle
篮球计分器:使用ViewModel+DataBinding来进行ui操作,使用SavedStateHandle保存数据
本文环境为:
android studio 3.6.2
gradle version 5.6.4
kotlin version 1.3.71
windows 10 pro
app build.gradle中开启databinding
dataBinding { // 开启 DataBinding
enabled true
}
导入viewModel依赖
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
新手注意
1.一定要转换布局,否则没办法进行绑定
2.注意绑定点击事件的方式
android:onClick="@{()->data.addScore2B(1)}"
3.绑定数值方式
android:text="@{data.getTeanAScore().toString()}"
具体布局文件如下
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="data"
type="com.example.scorer.MyViewModel" />
data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.15" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.05" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.35" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.65" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.8" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.9" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview1"
android:textSize="@dimen/TeamTextSize"
app:layout_constraintBottom_toTopOf="@+id/guideline5"
app:layout_constraintEnd_toStartOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline6" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview2"
android:textSize="@dimen/TeamTextSize"
app:layout_constraintBottom_toTopOf="@+id/guideline5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="@+id/guideline6" />
<TextView
android:id="@+id/scoreA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{data.getTeanAScore().toString()}"
android:textColor="@color/colorTeamA"
android:textSize="@dimen/scoreTextSize"
app:layout_constraintBottom_toTopOf="@+id/guideline7"
app:layout_constraintEnd_toStartOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline5" />
<TextView
android:id="@+id/scoreB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{data.getTeanBScore().toString()}"
android:textSize="@dimen/scoreTextSize"
android:textColor="@color/colorAccent"
app:layout_constraintBottom_toTopOf="@+id/guideline7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="@+id/guideline5" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button1"
android:textSize="@dimen/buttonSize"
android:background="@color/colorTeamA"
android:onClick="@{()->data.addScore2A(1)}"
app:layout_constraintBottom_toTopOf="@+id/guideline9"
app:layout_constraintEnd_toStartOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline7" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button1"
android:textSize="@dimen/buttonSize"
android:background="@color/colorAccent"
android:onClick="@{()->data.addScore2B(1)}"
app:layout_constraintBottom_toTopOf="@+id/guideline9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="@+id/guideline7" />
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button2"
android:textSize="@dimen/buttonSize"
android:background="@color/colorTeamA"
android:onClick="@{()->data.addScore2A(2)}"
app:layout_constraintBottom_toTopOf="@+id/guideline10"
app:layout_constraintEnd_toStartOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline9"
/>
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button2"
android:textSize="@dimen/buttonSize"
android:background="@color/colorAccent"
android:onClick="@{()->data.addScore2B(2)}"
app:layout_constraintBottom_toTopOf="@+id/guideline10"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="@+id/guideline9" />
<Button
android:id="@+id/button6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button3"
android:textSize="@dimen/buttonSize"
android:background="@color/colorTeamA"
android:onClick="@{()->data.addScore2A(3)}"
app:layout_constraintBottom_toTopOf="@+id/guideline11"
app:layout_constraintEnd_toStartOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline10" />
<Button
android:id="@+id/button7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button3"
android:textSize="@dimen/buttonSize"
android:background="@color/colorAccent"
android:onClick="@{()->data.addScore2B(3)}"
app:layout_constraintBottom_toTopOf="@+id/guideline11"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="@+id/guideline10" />
<ImageButton
android:id="@+id/imageButton_undo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/guideline12"
app:layout_constraintEnd_toStartOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline11"
android:contentDescription="@string/undo"
android:onClick="@{()->data.undo()}"
app:srcCompat="@drawable/ic_undo_black_24dp" />
<ImageButton
android:id="@+id/imageButton_refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/guideline12"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="@+id/guideline11"
android:contentDescription="@string/refresh"
android:onClick="@{()->data.reset()}"
app:srcCompat="@drawable/ic_loop_black_24dp" />
androidx.constraintlayout.widget.ConstraintLayout>
layout>
package com.example.scorer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.SavedStateViewModelFactory
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import com.example.scorer.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var mViewModel: MyViewModel
var binding: ActivityMainBinding ?= null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContentView(R.layout.activity_main)
//绑定视图
binding=DataBindingUtil.setContentView(this,R.layout.activity_main)
mViewModel=ViewModelProvider(this).get(MyViewModel::class.java)
//绑定数据
binding!!.data=mViewModel
//绑定生命周期
binding!!.lifecycleOwner = this
}
companion object {
const val SCOREA:String="scoreA"
const val SCOREB:String="scoreB"
const val LIST:String="List"
}
}
package com.example.scorer
import android.util.Log
import android.widget.Toast
import androidx.lifecycle.ViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import kotlin.math.log
class MyViewModel(handle: SavedStateHandle) : ViewModel() {
//声明变量:两队的分数,使用MutableLiveData
private var teamAScore:MutableLiveData<Int>?=null
private var teamBScore:MutableLiveData<Int>?=null
//撤回信息数组
private var list:MutableLiveData<ArrayList<Undo>>? = null
private var mhandle=handle
/**
* 获取A队分数
*/
fun getTeanAScore(): MutableLiveData<Int>{
if (teamAScore==null){
if (!(mhandle!!.contains(MainActivity.SCOREA))){
mhandle!!.set(MainActivity.SCOREA,0)
}
teamAScore= mhandle!!.getLiveData(MainActivity.SCOREA)
return teamAScore as MutableLiveData<Int>
}else{
return teamAScore as MutableLiveData<Int>
}
//
}
/**
* 获取撤销数组
*/
fun getList(): MutableLiveData<ArrayList<Undo>>? {
if (!(mhandle!!.contains(MainActivity.LIST))){
var mlist= arrayListOf<Undo>()
mhandle.set(MainActivity.LIST, mlist)
}
list=mhandle.getLiveData(MainActivity.LIST)
return list
}
/**
* 获取B队分数
*/
fun getTeanBScore():MutableLiveData<Int>{
if (teamBScore==null){
if (!(mhandle!!.contains(MainActivity.SCOREB))){
mhandle!!.set(MainActivity.SCOREB,0)
}
teamBScore= mhandle!!.getLiveData(MainActivity.SCOREB)
return teamBScore as MutableLiveData<Int>
}else{
return teamBScore as MutableLiveData<Int>
}
}
/**
* A队加分
*/
fun addScore2A(n:Int){
if (list==null){
getList()
}
list!!.value!!.add(Undo("A", teamAScore?.value!!))
teamAScore?.value = teamAScore?.value?.plus(n)
}
/**
* B队伍加分
*/
fun addScore2B(n:Int){
if (list==null){
getList()
}
list!!.value!!.add(Undo("B", teamBScore?.value!!))
teamBScore?.value = teamBScore?.value?.plus(n)
}
/**
* 重置
*/
fun reset(){
list!!.value!!.add(Undo("A", teamAScore?.value!!))
list!!.value!!.add(Undo("B", teamBScore?.value!!))
teamAScore.also {
it!!.value=0
}
teamBScore.also {
it!!.value=0
}
}
/**
* 撤回
*/
fun undo(){
if (list!!.value!!.size>0){
var undo=list!!.value!![list!!.value!!.size-1]
when(undo.team){
"A"->{
teamAScore?.value = undo.n
Log.i("score","A 加分:${teamAScore?.value!!}")
}
"B"->{
teamBScore?.value = undo.n
Log.i("score","B 加分:${teamBScore?.value!!}")
}
}
list!!.value!!.removeAt(list!!.value!!.size-1)
}
}
}
package com.example.scorer
import android.os.Parcel
import android.os.Parcelable
import java.io.Serializable
/**
* 保存撤回所用的信息
*/
data class Undo(val team:String,val n:Int):Serializable{
}
1.android studio自带图标库
项目
https://gitee.com/lee_gitee/Jetpack_DataBinding_kotlin