天天看点

Kotlin使用DataBinding+LiveData遇到的小坑

Kotlin使用DataBinding+LiveData遇到的小坑

这两天在学习DataBinding的过程中遇到了一些问题,首先是网上的许多资料都还是gradle3.1之前的产物,对于先直已经默认整合DataBindingV1、V2版本,对kotlin的支持、尤其是在fragment中的使用没有很好的说明。

截止截稿,gradle版本已经到 3.5_rc1 了。

开启DataBinding,仍需要在项目的build.gradle中开启它

// An highlighted block
...
apply plugin: 'kotlin-kapt'

android{
	...
	//启用DataBinding
	dataBinding {
        enabled = true
    }
    //kotlin支持
    kapt {
        generateStubs = true
    }
}
dependencies{
	...
    //lifecycle
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha02'
    implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0-alpha02'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha02'
    kapt 'androidx.lifecycle:lifecycle-compiler:2.2.0-alpha02'
	//databinding
    kapt 'com.android.databinding:compiler:3.2.0-alpha10'
}
           

比如这里我新建了一个带viewModel的Fragment或Activity

WelcomeFragment.kt	/ WelcomeActivity.kt
WelcomeViewModel.kt
welcome_fragment.xml
           

ViewModel和其他代码就忽略不计了。

首先布局文件并非是在Activity或者Fragment自动生成的布局上直接添加data节点,而是需要添加一个新的layout文件,其节点为全小写的 <layout/> (当然也可以在原来的布局外面手动套一层layout)

<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools ="http://schemas.android.com/tools">
    ...
	 <data>
        <variable
            name="viewModel"
            type="com.android.test.WelcomeViewModel"/>
    </data>

 	<!--这里开始就是原来实际上的根布局-->
	 <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/cl_welcome"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white">
        
		...
		
		<TextView
            android:id="@+id/tv_welcome_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.msg}"
            android:textColor="@color/textColorTip"
            android:textSize="@dimen/TextSizeCommon"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintVertical_bias="0.75"
            />
	
    </layout>
           

省略了大部分内容。

在TextView的text属性中对viewModel.msg进行了引用

于是就来到被坑了一段时间的LiveData绑定时间。

在Activity中

class WelcomeActivity: AppCompatActivity() {
	...
	override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val mBinding= DataBindingUtil.setContentView<WelcomeFragmentBinding>(this,R.layout.welcome_fragment)
		viewModel = ViewModelProviders.of(this).get(WelcomeViewModel::class.java)
		mBinding.viewModel = viewModel
        mBinding.lifecycleOwner = this
    }
}
           

在Fragment中

class WelcomeFragment : Fragment() {
	...
	override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val mBinding = DataBindingUtil.inflate<WelcomeFragmentBinding>(inflater, R.layout.welcome_fragment, container, false)
        viewModel = ViewModelProviders.of(this).get(WelcomeViewModel::class.java)
        mBinding.viewModel = viewModel
        mBinding.lifecycleOwner = this
        return mBinding.root
    }
}
           

其实方法很简单,就是获取一个带DataBinding的View,然后绑定你在xml文件中设定的参数,最后把赋予DataBinding生命周期监听lifecycleOwner即可。只不过在Fragment中还需要通过root方法获取并返回View

坑就在于:

1、首先,大多数资料过老,都还是基于rxjava编写的。

2、起因的是谋篇博文,他的DataBinding实例mBinding没有指定类型,然后调用了一个mBinding.setViewModel()方法,我当时就愣住了,我翻遍ViewDataBinding类,哪有这个方法。这篇是java写的,还有一篇kotlin不知道是抄错了还是怎么回事,再一次误导了我,他直接就val了一个mBinding就开始给viewmodel赋值,返回的参数根本不带类型。

所以问题的关键在于,无论是Activity 的 setContentView方法还是Fragment的inflate方法,返回的都是继承于ViewDataBinding的参数。

而这个继承于ViewDataBinding的WelcomeFragmentBinding,是由带DataBinding的xml布局文件welcome_fragment.xml自动生成的,名字也是和布局文件的名字相关的,只要在xml文件中添加data之后进行sync,就会自动生成…

WelcomeFragmentBinding的lifecycleOwner、root方法都是继承自ViewDataBinding,但是上文代码中的viewModel指的却是你自己在xml中定义的变量

<data>
        <variable
            name="viewModel"
            type="com.android.test.WelcomeViewModel"/>
    </data>
           

总结:

总的来说这个事情还是因为我不够细心和耐心导致的,写下这篇笔记主要是出于记住教训的目的,同时也看看能不能帮到其他正在学习DataBinding的朋友少走些弯路。