Android鬼點子 Kotlin+DataBinding+LiveData+Lifecycle的奇妙反應

Kotlin是谷歌官方最新支持的一級語言,新版的Android Studio3.0 Beta 版已經原聲支持Kotlin。昨天的新聞說J神也加入了谷歌的Kotlin組,以爲Kotlin會成爲大勢啊!php

kotlin.jpg

LiveData 是一個數據持有者類,它持有一個值並容許觀察該值。它還能夠與Lifecycle綁定,與觀察者的生命週期同步。簡單的說就是,把數據放到LiveData裏面,而後給LiveData設置監聽器,當數據改變的時候,會自動調用監聽器。與Lifecycle綁定,當Activity被回收以後不會被觸發監聽。配合單例模式,能夠很輕鬆的實如今一處修改數據,多處activity,fragment收到通知。java

DataBinding能夠讓你的的UI代碼變得至關乾淨利落。它能夠把頁面邏輯從你的代碼中提取出來。讓你的代碼更加專一處理其餘事情。android

咱們把DataBinding的ViewModel設置成LiveData,在LiveData的監聽器中調用DataBinding的刷新。這項在任何地方修改ViewModel,UI就會自動更新。bash

接下來,舉個例子。咱們在上面的輸入框輸入文本。下面的文本框會實時反映輸入的內容。app

tt2.gif

首先是配置環境,我使用的是AndroidStudio3.0 Beta1 Project的gradle以下:maven

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.1.3-2'
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0-beta1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

ext.arch_version = "1.0.0-alpha7"

allprojects {
    repositories {
        google()
        jcenter()
        mavenCentral()
        maven { url 'https://maven.google.com' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

複製代碼

Module的gradle以下:ide

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.1"
    defaultConfig {
        applicationId "com.greendami.gdm"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    dataBinding {
        enabled = true
    }
}

dependencies {
    kapt 'com.android.databinding:compiler:3.0.0-beta1'
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:26.0.1'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:0.5'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2'

    compile "android.arch.persistence.room:runtime:$arch_version"
    compile "android.arch.lifecycle:runtime:$arch_version"
    compile "android.arch.lifecycle:extensions:$arch_version"

    annotationProcessor "android.arch.persistence.room:compiler:$arch_version"
    annotationProcessor "android.arch.lifecycle:compiler:$arch_version"

}

kapt {
    generateStubs = true
}

複製代碼

比較重要的是dataBinding {enabled = true}開啓databing佈局

下面開始擼代碼。 首先是ViewModel類,也是一個LiveData類。gradle

package com.greendami.gdm

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel

/**
 * Created by GreendaMi on 2017/8/10.
 */
class SearchViewModel : ViewModel() {
     val show_string = MutableLiveData<String>()
     val input_string = MutableLiveData<String>()
}
複製代碼

這個類裏面只有2個字段,分別對應界面上的輸入框的內容,和文本框的顯示內容。 而後是佈局文件acticity_main.xmlui

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools">

    <data>
        <import type="com.greendami.gdm.SearchViewModel" />
        <variable name="model" type="SearchViewModel" />
    </data>

    <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.greendami.gdm.MainActivity">
        <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/input" android:inputType="textNoSuggestions" android:imeOptions="actionSearch" tools:text="google"/>

        <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{model.show_string.value}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />

    </android.support.constraint.ConstraintLayout>
</layout>
複製代碼

首先最頂層的佈局是layout,而後下面第一個是data域,是聲明數據的地方。後面就是正常的佈局。咱們首先聲明瞭一個SearchViewMode類型的對象model。而後再TextView中進行綁定android:text="@{model.show_string.value}"。這裏的**.value**是LiveData的取值的方式。

而後是MainActivity.kt

package com.greendami.gdm

import android.arch.lifecycle.LifecycleActivity
import android.arch.lifecycle.Observer
import android.databinding.DataBindingUtil
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import com.greendami.gdm.databinding.ActivityMainBinding

class MainActivity : LifecycleActivity() {
    var searchViewModel: SearchViewModel = SearchViewModel()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        searchViewModel.show_string.value = "等待輸入"
        binding.model = searchViewModel

        binding.input.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(p0: Editable?) {
                binding.model?.show_string?.value = p0.toString()
            }

            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }

            override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }

        })

        binding.model?.show_string?.observe( this@MainActivity, Observer{
            Log.e("TAG",binding.model?.show_string?.value)
            Log.e("TAG",binding.hasPendingBindings().toString())
            binding.invalidateAll()
        })
    }
}

複製代碼

說明一下**var binding = DataBindingUtil.setContentView(this, R.layout.activity_main)**把佈局加載進來,這裏要注意的是ActivityMainBinding這個類可能一開始沒有生成,能夠嘗試rebuild一下,或者重啓IDE。 獲取到binding對象以後,就能夠給對象綁定ViewModel了,也就是代碼中的searchViewModel

咱們使用binding對象.控件ID來拿到控件對象。好比:

binding.input.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(p0: Editable?) {
                binding.model?.show_string?.value = p0.toString()
            }

            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }

            override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }

        })
複製代碼

在監聽器中修改LiveData的值。 由於咱們設置了LiveData的觀察者

binding.model?.show_string?.observe( this@MainActivity, Observer{
            Log.e("TAG",binding.model?.show_string?.value)
            Log.e("TAG",binding.hasPendingBindings().toString())
            binding.invalidateAll()
        })
複製代碼

當數據修改後這裏就會被調用。這裏的第一個參數this,是LifecycleActivity,因此這個監聽器綁定了MainActvity的生命週期。這裏使用了binding.invalidateAll(),而沒有使用binding.executePendingBindings()是由於這裏改變LiveData的value後,binding.hasPendingBindings()返回false,也就是說,databinding沒有感知到數據的改變,因此不會刷新界面。

相關文章
相關標籤/搜索