ViewModel DataBinding SavedStateHandle(kotlin)

本文为jetpack学习案例,来自于

https://www.bilibili.com/video/BV1w4411t7UQ/?p=13

涉及内容
ViewModel
LiveData
Databinding
android studio 自带图标库
SavedStateHandle

篮球计分器:使用ViewModel+DataBinding来进行ui操作,使用SavedStateHandle保存数据

Screenshot_1586529229.png

一、配置

本文环境为:

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.界面

![界面布局](https://upload-images.jianshu.io/upload_images/16607784-8040f800637ede27.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/800)


**新手注意**

1.一定要转换布局,否则没办法进行绑定

![image-20200410220950393.png](https://upload-images.jianshu.io/upload_images/16607784-174d1eb572c18d7c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


2.注意绑定点击事件的方式

```xml
android:onClick="@{()->data.addScore2B(1)}"

3.绑定数值方式

android:text="@{data.getTeanAScore().toString()}"

具体布局文件如下

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

2.MainActivity

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"
    }
}


3.MyViewModel

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)
      }
    }
}

4.Undo

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自带图标库

image-20200410222657113.png
image-20200410222845366.png

项目

https://gitee.com/lee_gitee/Jetpack_DataBinding_kotlin

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容