草庐IT

当你真的学会DataBinding后,你会发现“这玩意真香”!

编程的平行世界 2023-03-28 原文

前言

?DataBinding只是一种工具,用来解决View和数据之间的绑定。

Data Binding,顾名思义:数据绑定,它可以将布局页面中的组件和应用中的数据进行绑定,支持单向绑定和双向绑定,单向绑定就是如果数据有变化就会驱动页面进行变化,双向绑定就是除了单向绑定之外还支持页面的变化驱动数据的变化,如果页面中有一个输入框,那么我们就可以进行双向绑定,数据变化,它的显示内容就变了,我们手动输入内容也可以改变绑定它的数据。

?官方文档:https://developer.android.google.cn/jetpack/androidx/releases/databinding

?官方Demo地址:https://github.com/googlecodelabs/android-databinding

本文代码地址

如何使用DataBinding呢?

1.启用DataBinding

引用官方文档:

Databinding与 Android Gradle 插件捆绑在一起。您无需声明对此库的依赖项,但必须启用它。

注意:即使模块不直接使用数据绑定,也必须为依赖于使用数据绑定的库的所有模块启用数据绑定。

//在gradle的android下加入,然后点击sync
android {
    ...
    //android studio 4.0以下
    dataBinding{
    
    }
    //android studio4.1以后
    buildFeatures {
        dataBinding true
    }
}

2.生成DataBinding布局

在我们的布局文件中,选择根目录的View,按下Alt+回车键,点击Convert to data binding layout,就可以转换为DataBinding布局啦。

[图片上传失败...(image-e5b226-1656642536141)]
然后我们的布局就会变成这样:

<?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>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

我们可以发现,最外面变成了layout元素,里面有data元素。我们将在data元素中声明这个布局中使用到的变量,以及变量的类型。

举个例子:

<data>
    <import type="com.example...."/>
    <variable
        name="color"
        type="java.lang.String" />
</data>
  • data: 在标签内进行变量声明和导入等
  • variable: 进行变量声明
  • import: 导入需要的类

3.声明一个User实体类

class User() {
    var name = "Taxze"
    var age = 18
    fun testOnclick() {
        Log.d("onclick", "test")
    }
}

4.在xml中使用

然后在data中声明变量,以及类名

<data>
    <!-- <variable-->
    <!-- name="user"-->
    <!-- type="com.taxze.jetpack.databinding.User" />-->
    <import type="com.taxze.jetpack.databinding.User" />

    <variable
        name="user"
        type="User" />
</data>

然后在布局中使用@{}语法

//伪代码,请勿直接CV
<TextView
    ...
    android:text="@{user.name}"
/>

5.在Activity或Fragment中使用DataBinding

在Activity中通过DataBindingUtil设置布局文件,同时省略Activity的setContentView方法

class MainActivity : AppCompatActivity() {
    private lateinit var mainBinding: ActivityMainBinding
    private lateinit var mainUser: User
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mainBinding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        mainUser = User()
        mainBinding.user = mainUser
    }
}

在Fragment中使用:

class BlankFragment : Fragment() {
    private lateinit var mainFragmentBinding:FragmentBlankBinding
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        mainFragmentBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_blank,container,false)
        return mainFragmentBinding.root
    }
}

系统会为每个布局文件都生成一个绑定类。一般默认情况下,类的名称是布局文件名称转化为Pascal大小写形式,然后在末尾添加Binding后缀,例如:名称为activity_main的布局文件,对应的类名就是ActivityMainBinding

运行之后的效果:

[图片上传失败...(image-df5a21-1656642536141)]

注意:只有当布局文件转换为layout样式之后,databinding才会根据布局文件的名字自动生成一个对应的binding类,你也可以在build/generated/data_binding_base_source_out目录下查看生成的类

[图片上传失败...(image-b63679-1656642536141)]
最最最基础的使用就是这样,接下来我们来讲讲如何更好的使用DataBinding

如何在xml布局中更好的使用DataBinding

1.使用集合中的元素

  • 加入我们传入了一个集合books,我们可以通过以下方式使用:

    • 获取集合的值
    • android:text="@{books.get(0).name}"
      android:text="@{books.[0].name}"
      
    • 添加默认值(⚡默认值无需加引号,且只在预览视图显示)
    • android:text="@{books.pages,default=330}"
      
    • 通过??或?:来实现
    • android:text="@{books.pages != null ? book.pages : book.defaultPages}"
      
    • android:text="@{books.pages ?? book.defaultPages}"
      

2.使用map中的数据

  • map类型的结构也可以通过get和[]两种方式获取

    • //需要注意单双引号
      android:text="@{books.get('name')}"
      android:text="@{books['name']}"
      

3.转换数据类型

  • 因为DataBinding不会自动做类型转换,所有我们需要手动转换,例如在text标签内使用String.valueOf()转换为String类型,在rating标签内我们可以使用Float.valueOf()进行转换

    • android:text="@{String.valueOf(book.pages)}"
      
    • android:rating="@{Float.valueOf(books.rating),default=2.0}"
      

4.导入包名冲突处理

  • 如果我们导入的包名有冲突,我们可以通过alias为它设置一个别名

    • //伪代码,请勿直接CV
      <data>
            <import type="com.xxx.a.Book" alias="aBook"/>
            <import type="com.xxx.B.Book" alias="bBook"/>
            ...
      </data>
      

5.隐式引用属性

  • 在一个view上引用其他view的属性

    • //伪代码,请勿直接CV
      <import type="android.view.View"/>
      ...
      <LinearLayout
          android:layout_width="match_parent"
          android:layout_height="match_parent">
          <CheckBox android:id="@+id/checkOne" .../>
          <ImageView android:visibility="@{checkOne.checked ? View.VISIBLE : View.GONE}" .../>
      </LinearLayout>
      
  • include标签和ViewStub标签

    • includemerge标签的作用是实现布局文件的重用。就是说,为了高效复用及整合布局,使布局轻便化,我们可以使用includemerge标签将一个布局嵌入到另一个布局中,或者说将多个布局中的相同元素抽取出来,独立管理,再复用到各个布局中,便于统一的调整。 比如,一个应用中的多个页面都要用到统一样式的标题栏或底部导航栏,这时就可以将标题栏或底部导航栏的布局抽取出来,再以include标签形式嵌入到需要的布局中,而不是多次copy代码,这样在修改时只需修改一处即可。而我们同样可以通过DataBinding来进行数据绑定。
    • 例如:
    • //layout_title.xml
      <?xml version="1.0" encoding="utf-8"?>
      <layout xmlns:android="http://schemas.android.com/apk/res/android">
          <data>
              <import type="com.xxx.User" />
              <variable
                  name="userInfo"
                  type="User" />
          </data>
          <android.support.constraint.ConstraintLayout
              ...
              >
              <TextView
                  ...
                  android:text="@{userInfo.name}" />
          </android.support.constraint.ConstraintLayout>
      </layout>
      
    • 使用该布局,并传值
    • <include
          layout="@layout/layout_title"
          bind:test="@{userInfo}"/>
      
    • ViewStub也是类似的用法,这里就不说了。
    • DataBinding不支持merge标签

6.绑定点击事件

  • //伪代码,请勿直接CV
    onclick="@{()->user.testOnclick}"
    onclick="@{(v)->user.testOnclick(v)}"
    onclick="@{()->user.testOnclick(context)}"
    onclick="@{BindHelp::staticClick}"
    onclick="@{callback}"
    
    //例如:
    <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:onClick="@{()->user.testOnclick}"
        />
    

?文章到这里讲的都是DataBinding如何设置数据,以及通过DataBinding在xml中的一些基础使用。如果只是使用DataBinding这个功能,那就有点大材小用了。它还有一个很强大的功能我们还没有讲,那就是数据更新时自动刷新UI。

实现数据变化时自动更新UI

一个普通的实体类或者ViewModel被更新后,并不会让UI自动更新。而我们希望,当数据变更后UI要自动更新,那么要实现数据变化时自动更新UI,有三种方法可以使用,分别是BaseObservableObservableFieldObservableCollection

?单向数据绑定:

  • BaseObservable

    BaseObservable提供了两个刷新UI的方法,分别是 notifyPropertyChanged() 和 notifyChange() 。

    • 第一步:修改实体类

      将我们的实体类继承与BaseObservable。需要响应变化的字段,就在对应变量的get函数上加 @Bindable 。然后set中notifyChange是kotlin的写法,免去了java的getter setter的方式。成员属性需要响应变化的,就在其set函数中,notify一下属性变化,那么set的时候,databinding就会感知到。

      import androidx.databinding.BaseObservable
      import androidx.databinding.Bindable
      import androidx.databinding.library.baseAdapters.BR
      
      class User() : BaseObservable() {
          constructor(name: String, age: Int) : this() {
              this.name = name
              this.age = age
          }
          //这是单独在set上@bindable,name可以为声明private
          var name: String = ""
              set(value) {
                  field = value
                  notifyPropertyChanged(BR.name)
              }
              @Bindable
              get() = field
      
          //这是在整个变量上声明@bindable,所以必须是public的
          @Bindable
          var age:Int = 18
              set(value) {
                  field = value
                  notifyPropertyChanged(BR.age)
              }
              get() = field
      }
      
    • 第二步:在Activity中使用

      class MainActivity : AppCompatActivity() {
          private val TAG = "MainActivity"
          private lateinit var mainBinding: ActivityMainBinding
          private lateinit var mainUser: User
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              mainBinding =
                  DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
              mainUser = User("Taxze", 18)
              mainUser.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
                  override fun onPropertyChanged(sender: Observable, propertyId: Int) {
                      when {
                          BR.user == propertyId -> {
                              Log.d(TAG, "BR.user")
                          }
                          BR.age == propertyId -> {
                              Log.d(TAG, "BR.age")
                          }
                      }
                  }
              })
              mainBinding.user = mainUser
              mainBinding.onClickPresenter = OnClickPresenter()
          }
      
          inner class OnClickPresenter {
              fun changeName() {
                  mainUser.name = "Taxze2222222"
              }
          }
      }
      
    • 需要注意的点

      • 官方网站只是提示了开启DataBinding只需要在build.gradle中加入下面这行代码

      • buildFeatures {
            dataBinding true
        }
        

        但是,如果你想更好的使用DataBinding这是不够的,你还需要添加这些配置:

      • compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
        kotlinOptions {
            jvmTarget = '1.8'
        }
        
      • ?重点:在使用DataBinding得时候,BR对象,发现调用不了,生成也会报错,运行,需要在咱们build.gradle中进行一下配置:

        apply plugin: 'kotlin-kapt'
        kapt {
            generateStubs = true
        }
        

        然后重新运行一遍代码,你就会发现,BR文件自动生成啦!

  • ObservableField

    讲解了BaseObservable后,现在来将建最简单也是最常用的。只需要将实体类变化成这样即可:

    //注意observable的属性需要public权限,否则dataBinding则无法通过反射处理数据响应
    class User() : BaseObservable() {
        var name: ObservableField<String> = ObservableField("Taxze")
        var age:ObservableInt = ObservableInt(18)
    }
    
  • ObservableCollection

    dataBinding 也提供了包装类用于替代原生的 List 和 Map,分别是 ObservableList 和 ObservableMap

    实体类修改:

    //伪代码,请勿直接cv
    class User(){
        var userMap = ObservableArrayMap<String,String>()
    }
    //使用时:
    mainUser.userMap["name"] = "Taxze"
    mainUser.userMap["age"] = "18"
    

    使用ObservableCollection后,xml与上面的略有不同,主要是数据的获取,需要指定Key

    //伪代码,请勿直接cv
    ...
    <import type="android.databinding.ObservableMap" />
    <variable
             name="userMap"
             type="ObservableMap<String, String>" />
    
    //使用时:
    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="Name"
            android:text="@{userMap[`userName`]}" />
    

?双向数据绑定:

  • 只需要在之前的单向绑定的基础上,将布局文件@{}变为@={},用于针对属性的数据改变的同时监听用户的更新

DataBinding在RecyclerView中的使用

在RecyclerView中使用DataBinding稍有变化,我们在ViewHolder中进行binding对象的产生,以及数据对象的绑定。

我们通过一个非常简单的例子来讲解如何在RecyclerView中使用DataBinding。

效果图:

[图片上传失败...(image-f1d50d-1656642536141)]

  • 第一步:创建实体类

    就是我们之前的,使用了BaseObservable的那个实体类,这里就不放代码了

  • 第二步:创建activity_main用于存放recyclerview

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activty_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />
    
    </RelativeLayout>
    
  • 第三步:创建text_item.xml用于展示recyclerview中的每一行数据

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
            <import type="com.taxze.jetpack.databinding.User" />
            <variable
                name="user"
                type="User" />
        </data>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="40dp"
                android:background="#ffffff"
                android:orientation="horizontal"
                android:paddingStart="10dp">
    
                <TextView
                    android:id="@+id/tv_name"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:text="@{`这个人的姓名是` + user.name}" />
    
                <TextView
                    android:id="@+id/tv_age"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:layout_marginLeft="20dp"
                    android:text="@{String.valueOf(user.age)}" />
            </LinearLayout>
        </LinearLayout>
    </layout>
    
  • 第四步:创建Adapter

    有了之前的基础之后,大家看下面这些代码应该很容易了,就不做过多讲解啦

    import android.content.Context
    import android.view.LayoutInflater
    import android.view.ViewGroup
    import androidx.databinding.DataBindingUtil
    import androidx.recyclerview.widget.RecyclerView
    import com.taxze.jetpack.databinding.databinding.TextItemBinding
    
    class FirstAdapter(users: MutableList<User>, context: Context) :
        RecyclerView.Adapter<FirstAdapter.MyHolder>() {
        //在构造函数中声明binding变量,这样holder才能引用到,如果不加val/var,就引用不到,就需要在class的{}内写get函数
        class MyHolder(val binding: TextItemBinding) : RecyclerView.ViewHolder(binding.root)
    
        private var users: MutableList<User> = arrayListOf()
        private var context: Context
    
        init {
            this.users = users
            this.context = context
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
            val inflater = LayoutInflater.from(context)
            val binding: TextItemBinding =
                DataBindingUtil.inflate(inflater, R.layout.text_item, parent, false)
            return MyHolder(binding)
        }
    
        override fun onBindViewHolder(holder: MyHolder, position: Int) {
            //java 写法可以setVariable
            holder.binding.user = users[position]
            holder.binding.executePendingBindings()
        }
        //kotlin中,return的方式,可以简写
        override fun getItemCount() = users.size
    }
    
  • 第五步:在MainActivity中使用

    import android.os.Bundle
    import android.view.View
    import androidx.appcompat.app.AppCompatActivity
    import androidx.recyclerview.widget.LinearLayoutManager
    import androidx.recyclerview.widget.RecyclerView
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            initView()
        }
        //给RecyclerView设置数据
        private fun initView() {
            val recyclerView = findViewById<View>(R.id.recyclerView) as RecyclerView
            recyclerView.layoutManager = LinearLayoutManager(this)
            val users: MutableList<User> = ArrayList()
            for (i in 0..100) {
                val user = User()
                user.name = "Taxze"
                user.age = i
                users.add(user)
            }
            val adapter = FirstAdapter(users, this)
            recyclerView.adapter = adapter
        }
    }
    

这样就完成了在RecyclerView中使用DataBinding啦。

高级用法

第一个:用于appCompatImageView的自定义属性

//伪代码,请勿直接cv
/**
 * 用于appCompatImageView的自定义属性,bind:imgSrc,命名空间bind:可以省略,也就是写作 imgSrc亦可。可以用于加载url的图片
 * 函数名也是随意,主要是value的声明,就是新加的属性名了,可以多个属性同用,并配置是否必须一起作用
 * 函数名随意,方法签名才重要,匹配对象控件,以及属性参数。
 * 这里还可以添加old 参数,获取修改新参数 之前对应的值。
 * todo 加载网络图片,需要网络权限!!!
 */
@JvmStatic
@BindingAdapter(value = ["bind:imgSrc"], requireAll = false)
fun urlImageSrc(view: AppCompatImageView, /*old: String?, */url: String) {
    Glide.with(view)
        .load(url)
        .placeholder(R.drawable.img_banner)
        .centerInside()
        .into(view)
}

第二个:配合swipeRefreshLayout的刷新状态的感知

  • 第一步:单向的,数据变化,刷新UI

    //伪代码,请勿直接cv
    @JvmStatic
    @BindingAdapter("sfl_refreshing", requireAll = false)
    fun setSwipeRefreshing(view: SwipeRefreshLayout, oldValue: Boolean, newValue: Boolean) {
        //判断是否是新的值,避免陷入死循环
        if (oldValue != newValue)
            view.isRefreshing = newValue
    }
    
  • 第二步:ui的状态,反向绑定给数据变化

    //伪代码,请勿直接cv
    @JvmStatic
    @BindingAdapter("sfl_refreshingAttrChanged", requireAll = false)
    fun setRefreshCallback(view: SwipeRefreshLayout, listener: InverseBindingListener?) {
        listener ?: return
        view.setOnRefreshListener {
            //由ui层的刷新状态变化,反向通知数据层的变化
            listener.onChange()
        }
    }
    
  • 第三步: 反向绑定的实现

    //伪代码,请勿直接cv
    /**
     * 反向绑定的实现,将UI的变化,回调给bindingListener,listener就会onChange,通知数据变化
     * 注意这里的attribute和event,是跟上面两步配合一致才有效
     */
    @JvmStatic
    @InverseBindingAdapter(attribute = "sfl_refreshing", event = "sfl_refreshingAttrChanged")
    fun isSwipeRefreshing(view: SwipeRefreshLayout): Boolean {
        return view.isRefreshing
    }
    

DataBinding配合ViewModel&LiveData一起使用

我将通过一个简单的例子带大家学习他们如何一起使用,话不多说,先上效果图:

[图片上传失败...(image-c6034d-1656642536141)]

  • 第一步:创建UserModel

    //将其继承于AndroidViewModel(AndroidViewModel也是继承于ViewModel的,但是ViewModel本身没有办法获得 Context,AndroidViewModel提供Application用作Context,并专门提供 Application 单例)
    
    //UserName 使用MutableLiveData
    class UserModel(application: Application) : AndroidViewModel(application) {
        var UserName = MutableLiveData("")
    }
    
  • 第二步:创建activity_main和对应的MainActivity

    <?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>
            <variable
                name="loginModel"
                type="com.taxze.jetpack.databinding.model.UserModel" />
        </data>
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <LinearLayout
                android:id="@+id/linearLayout"
                style="@style/InputBoxStyle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="17dp"
                android:layout_marginEnd="17dp"
                app:layout_constraintBottom_toTopOf="@+id/guideline2"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="parent"
                tools:ignore="MissingConstraints">
    
                <EditText
                    android:id="@+id/editText"
                    style="@style/EditTextStyle"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:hint="请输入账号"
                    android:text="@={loginModel.UserName}"
                    tools:ignore="MissingConstraints" />
            </LinearLayout>
    
            <TextView
                android:id="@+id/textView2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text='@{"您输入的账号名是:"+loginModel.UserName,default=123123123}'
                android:textSize="24sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/button"
                tools:ignore="MissingConstraints" />
    
            <androidx.constraintlayout.widget.Guideline
                android:id="@+id/guideline2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                app:layout_constraintGuide_percent="0.4" />
    
            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="32dp"
                android:background="@drawable/button_drawable"
                android:text="登录"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/linearLayout"
                tools:ignore="MissingConstraints" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    
    </layout>
    
  • 第三步:在MainActivity中绑定页面和绑定声明周期

    class MainActivity : AppCompatActivity() {
        lateinit var viewDataBinding: ActivityMainBinding
        lateinit var model: UserModel
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //绑定页面
            viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
            //绑定生命周期
            viewDataBinding.lifecycleOwner = this
            model = ViewModelProvider.AndroidViewModelFactory.getInstance(this.application)
                .create(UserModel::class.java)
            viewDataBinding.loginModel = model
    
            ...
        }
    }
    
  • 第四步:传值

    viewDataBinding.button.setOnClickListener {
                val intent = Intent(MainActivity@ this, SecondActivity::class.java)
                intent.putExtra("user", "${model.UserName.value}")
                startActivity(
                    intent
                )
            }
    
  • 第五步:在另外一个activity中调用

    class SecondActivity : AppCompatActivity() {
        lateinit var viewDataBinding: ActivitySecondBinding
        lateinit var userName: String
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //绑定页面
            viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_second)
            //绑定生命周期
            viewDataBinding.lifecycleOwner = this
    
            userName = intent.getStringExtra("user").toString()
    
            viewDataBinding.tvName.text = "登录的账号是:$userName"
        }
    }
    

帮你踩坑?:

  • TextViewtext属性,需要注意data不能为Number类型
  • xml中字符不能为中文(检查一下你的输入法)
  • 反射属性、函数必须是public
  • observableField数据的时候,在某些场合需要初始化,否则会运行报错!
  • 使用LiveData作为dataBinding的时候,需要在ui中设置binding.lifecycleOwner

尾述

这篇文章已经很详细的讲了DataBinding的大部分用法,不过在看完文章后,你仍需多多实践,相信你很快就可以掌握DataBinding啦? 有问题欢迎在评论区留言讨论~

关于我

Hello,我是Taxze,如果您觉得文章对您有价值,希望您能给我的文章点个❤️,也欢迎关注我的博客

如果您觉得文章还差了那么点东西,也请通过关注督促我写出更好的文章——万一哪天我进步了呢??

基础系列:

2022 · 让我带你Jetpack架构组件从入门到精通 — Lifecycle

学会使用LiveData和ViewModel,我相信会让你在写业务时变得轻松?

当你真的学会DataBinding后,你会发现“这玩意真香”! (本文?)

以下部分还在码字,赶紧点个收藏吧?

2022 · 让我带你Jetpack架构组件从入门到精通 — Navigation

2022 · 让我带你Jetpack架构组件从入门到精通 — Room

2022 · 让我带你Jetpack架构组件从入门到精通 — Paging3

2022 · 让我带你Jetpack架构组件从入门到精通 — WorkManager

2022 · 让我带你Jetpack架构组件从入门到精通 — ViewPager2

2022 · 让我带你Jetpack架构组件从入门到精通 — 登录注册页面实战(MVVM)

进阶系列:

协程 + Retrofit网络请求状态封装

Room 缓存封装

.....

有关当你真的学会DataBinding后,你会发现“这玩意真香”!的更多相关文章

  1. ruby - 你会如何在 Ruby 中表达成语 "with this object, if it exists, do this"? - 2

    在Ruby(尤其是Rails)中,您经常需要检查某物是否存在,然后对其执行操作,例如:if@objects.any?puts"Wehavetheseobjects:"@objects.each{|o|puts"hello:#{o}"end这是最短的,一切都很好,但是如果你有@objects.some_association.something.hit_database.process而不是@objects呢?我将不得不在if表达式中重复两次,如果我不知道实现细节并且方法调用很昂贵怎么办?显而易见的选择是创建一个变量,然后测试它,然后处理它,但是你必须想出一个变量名(呃),它也会在内存中

  2. ruby-on-rails - 我真的需要在 Rails 中使用 csv gem 吗? - 2

    我的问题很简单:我是否必须在使用RubyonRails的类上require'csv'?如果我打开一个railsconsole并尝试使用CSVgem它可以工作,但我必须在文件中这样做吗? 最佳答案 CSVlibrary是ruby​​标准库的一部分;它不是gem(即第三方库)。与所有标准库(与核心库不同)一样,csv不会由ruby​​解释器自动加载。所以是的,在您的应用程序中某处您确实需要要求它:irb(main):001:0>CSVNameError:uninitializedconstantCSVfrom(irb):1from/Us

  3. ruby-on-rails - ruby 真的是一种完全面向对象的语言吗? - 2

    Ruby是完全面向对象的语言。在ruby​​中,一切都是对象,因此属于某个类。例如5属于Objectclass1.9.3p194:001>5.class=>Fixnum1.9.3p194:002>5.class.superclass=>Integer1.9.3p194:003>5.class.superclass.superclass=>Numeric1.9.3p194:005>5.class.superclass.superclass.superclass=>Object1.9.3p194:006>5.class.superclass.superclass.superclass.su

  4. ruby-on-rails - 是否有可能发现 Ruby on Rails 应用程序中未使用哪些类? - 2

    在我们的项目中,我们有一些“被遗忘的”类存在了很长一段时间。那些类已被其他类替代,但我们忘记删除它们。是否有一些自动化的方法/工具可以发现Ruby{onRails}应用程序中没有使用哪些类?谢谢! 最佳答案 这个问题已经被提出了很多次,但是最好的答案都在这里:FindunusedcodeinaRailsapp我个人喜欢日志解析:https://stackoverflow.com/a/14161807但在任何情况下,您都可以创建自己的记录器,扩展ActiveRecord::Base以创建一个观察器,该观察器将最常用的模块存储在数据库中

  5. ruby - 元编程:如何发现对象的真实类? - 2

    我在用Ruby元编程开玩笑,我写了这段代码:classClassdef===(other)other.kind_of?(self)endendclassFakeClassdefinitialize(object)methods.each{|m|eval"undef#{m}"ifm.to_sym!=:methods}define=procdo|m|eval(这会创建一个模拟对象的假类。看:a=FakeClass.new(1)#=>1a.class#=>Fixnuma.methods#=>ReturnallFixnummethodsa+1#=>2(isnotaFakeClass)Fixnu

  6. ruby-on-rails - Rails 3 Cli 执行命令真的很慢吗? - 2

    有人知道为什么我的rails3.0.7cli这么慢吗?当我运行railss或railsg时,他大约需要5秒才能真正执行命令...有什么建议吗?谢谢 最佳答案 更新:我正在将我的建议从rrails切换到rails-sh,因为前者支持REPL,而rrails不是用例。此外,当与ruby​​环境结合使用时,修补似乎确实可以提高性能变量,现在反射(reflect)在答案中。一个可能的原因可能是这个performancebuginruby每当在ruby​​代码中使用“require”时,它就会调用一些代码(更多详细信息here)。在使用Rai

  7. 最近火热的“数字藏品”,你真的了解吗? - 2

    最近火热的“数字藏品”,你真正了解吗?其实有很多人会把数字藏品跟NFT混为一谈,但其实这两者还是有差别的。数字藏品并不等同于NFT数字藏品是什么?直观来看,它可能就是一张数字化照片或视频,甚至就只是一串数字。但它却是一件对应特定作品、艺术品生成的包含着大量数字信息且拥有唯一加密信息的可以买卖交易的收藏品。NFT则是指一种基于以太坊区块链的“非同质化代币”。它在百度百科里的释义是“用于表示数字资产(包括jpg和视频剪辑形式)的唯一加密货币令牌,可以买卖”。比如已被很多人认识的比特币就是NFT的一种。NFT在元宇宙中发挥的作用是巨大的,目前正是它在支撑着元宇宙中的经济体系。数字藏品其实也是NFT的

  8. ruby-on-rails - Rails 5 - config.assets.compile 应该是真的 - 为什么? - 2

    我正在开发Rails5应用程序并使用Assets管道。它在开发模式下运行良好,但如果我尝试在生产模式下运行它,它无法正确加载图像和样式。我查了一下,发现是因为config.assets.compile=false在config/environments/production.rb中除非我将其设置为真,否则它根本不起作用。我知道实时编译不适合生产,有什么解决方案? 最佳答案 有两个与在Rails服务器中提供Assets相关的选项:Assets编译config.assets.compile=true指Assets编译。也就是说,当Rai

  9. ruby - 我天真的最大团发现算法比 Bron-Kerbosch 的运行得更快。怎么了? - 2

    简而言之,我的原始代码(用Ruby编写)如下所示:#$seenisahashtomemoizepreviouslyseensets#$sparseisahashofusernamestoalistofneighboringusernames#$setisthelistofoutputclusters$seen={}defsubgraph(set,adj)hash=(set+adj).sortreturnif$seen[hash]$sets.pushset.sort.join(",")ifadj.empty?andset.size>2adj.each{|node|subgraph(set

  10. ruby-on-rails - Ruby on Rails,没有发现 Rakefile 错误 - 2

    我在rails上安装了ruby​​,postgres。我安装了所有必需的gem文件,我创建了一个项目http://guides.rubyonrails.org/getting_started.html想要我在config/routes.rb添加了下面的代码Blog::Application.routes.drawdoresources:postsrootto:"welcome#index"end我正在尝试运行rakeroutes命令。但是我明白了rakeaborted!NoRakefilefound(lookingfor:rakefile,Rakefile,rakefile.rb,Ra

随机推荐