简书地址:www.jianshu.com/p/015ad08c2…html
对于android开发者而言,写冗余重复的代码一直是一件吃力不讨好的事情,而数据绑定技术可以减小大量重复的代码,能够说是android开发者的福音。它学习起来十分简单(相信了解过的应该都这么以为),但使用起来却不那么尽如人意(对不起,binding文件未找到)。java
从16年11月到如今,通过这么长时间的实践,除了前4个月在踩坑以外,到如今都没再遇到DataBinding相关的错误,趁年前有些时间,所以总结了一下实际项目中使用DataBinding的一些经验。android
本文重点不在于讲解DataBinding语法,这样的文章够多了。git
若是你正在使用DataBinding并苦恼于不能趁心如意的使用它,那么看本文是一个不错的选择。github
完整示例:github.com/ditclear/Pa…api
DataBinding-AspectJ:github.com/ditclear/Da…markdown
阅读下文请具有DataBinding相关的基础知识。架构
能够简化DataBinding的转换操做并支持和ViewModel
和与之关联的layout文件的跳转,能够提高开发时的效率,节省时间app
贴两张图看看:框架
更多功能可查看此连接:plugins.jetbrains.com/plugin/9271…
俗话说没法不成章,对于一个团队而言,不论是大仍是小,都须要一套合理的统一的命名规范,既方便相互之间的协做,也减轻了CodeReview时的困难。对实践了Databinding的团队格外如此。
<!--user_activity.xml --> <variable name="uservm" type="io.ditclear.app.viewmodel.UserViewModel"/> <!--student_activity.xml --> <variable name="studentvm" type="io.ditclear.app.viewmodel.StudentViewModel"/> 复制代码
<!--user_activity.xml --> <variable name="vm" type="io.ditclear.app.viewmodel.UserViewModel"/> <!--student_activity.xml --> <variable name="vm" type="io.ditclear.app.viewmodel.StudentViewModel"/> 复制代码
也许你是刚接触DataBinding,按照官网的Guide,极可能会定义一些非必需的variable或者import 一些自定义的静态类来进行字符串处理、View显示隐藏等等的操做。可是这不是一个好习惯,过多的variable除了会让你多作几回无谓的绑定外,数据也将变得难以管理。因此尽量少的variable和import是一种较好的实践。
<data> <import type="com.ditclear.app.util.DateUtil"/> <import type="android.view.View"/> <import type="com.ditclear.app.util.StringUtil"/> <import type="com.ditclear.app.network.model.StudentState"/> <import type="java.math.BigDecimal"/> <import type="com.ditclear.app.presentation.student.StudentActivity"/> <variable name="isShow" type="Boolean"/> <variable name="time" type="java.util.Date"/> <variable name="date" type="java.util.Date"/> <variable name="signTime" type="String"/> <variable name="item" type="com.ditclear.app.network.model.student.StudentItem"/> <variable name="presenter" type="com.ditclear.app.presentation.student.StudentActivity.Presenter"/> </data> 复制代码
<data> <variable name="presenter" type="com.ditclear.app.helper.presenter.Presenter"/> <variable name="vm" type="com.ditclear.app.view.student.viewmodel.StudentViewModel"/> </data> 复制代码
数据的处理和view的显示都用ViewModel来处理,好比:
android:text="@{vm.signTime}"
android:visibility="@{vm.isShowVisbility}"
复制代码
这样的好处是减轻了layout.xml文件的复杂程度,xml文件只用来单纯的展现数据, 而复杂的逻辑判断都放在了ViewModel中,而且为页面布局数据提供了惟一的入口,方便查看和管理数据源。
在官方的指南里有这样的写法:
<data> <import type="com.example.MyStringUtils"/> <variable name="user" type="com.example.User"/> </data> <TextView android:text="@{MyStringUtils.capitalize(user.lastName)}" android:layout_width="wrap_content" android:layout_height="wrap_content"/> 复制代码
或者这样的
android:text="@{String.valueOf(index + 1)}" android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}" android:transitionName='@{"image_" + id}' android:text="@{@string/nameFormat(firstName, lastName)}" android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}" android:text="@{map[`firstName`]}" 复制代码
须要注意的是这些都不表明着最佳实践,只是说明有这样的功能,支持这样的用法。
并且它有一个很大的弊端,相信不少刚入门DataBinding的人都遇到过找不到binding文件的错,而后被劝退,缘由无外乎就是表达式写错、variable的类路径不对或者没import相应的类(好比View)等等,都与复杂的表达式有关系,而DataBinding报错也不太智能,有时不能准肯定位,因此建议不要在xml里进行复杂的数据绑定,这些都尽可能放到ViewModel里进行,xml只绑定简单的基础数据类型。
<data> <variable name="vm" type="com.example.UserViewModel"/> </data> <TextView android:text="@{vm.capitalizeLastName}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="@{vm.index}" android:visibility="@{vm.showName}" android:transitionName='@{vm.transitionName}'/> 复制代码
先看看DataBinding支持的写法
android:onClick="@{presenter.onClick()}" //1.方法引用 android:onClick="@{()->presenter.onClick()}" //2.lamda表达式 android:onClick="@{(view)->presenter.onClick(view)}" //3.lamda表达式 android:onClick="@{()->presenter.onClick(item)}"//4.带参数lamda表达式 android:onClick="@{(view)->presenter.onClick(view, item)}"//5.带参数lamda表达式 复制代码
选择不少,并且使用方法也是五花八门,有的喜欢直接使用viewmodel里的方法,有的直接将Activity或者fragment做为handler,还有的可能会在activity/fragment里写一个内部类做为presenter,而后因为方法名也能够自定义,因此极可能出现你叫presenter.save()
另外一个叫(v)->handler.onSave(v)
的状况。
<layout>
<data>
<variable
name="vm"
type="io.ditclear.app.viewmodel.AnimalViewModel"/>
<variable
name="handler"
type="io.ditclear.app.view.AnimalActivity"/>
<variable
name="presenter"
type="io.ditclear.app.view.AnimalActivity.Presenter"
</data>
<LinearLayout
tools:context="io.ditclear.app.view.AnimalActivity">
<Button
android:onClick="@{vm.shoutWhat()}"/>
<Button
android:onClick="@{(v)->handler.shout(v)}"/>
<Button
android:onClick="@{()->presenter.onShout()}"/>
</LinearLayout>
</layout>
复制代码
推荐的一种处理方式是使用封装过的View.OnClickListener
来统一处理点击事件,包裹一层的目的是为了避免依赖于具体实现。
public interface Presenter extends View.OnClickListener{ @Override void onClick(View v); } 复制代码
xml布局文件
<layout>
<data>
<variable
name="vm"
type="io.ditclear.app.viewmodel.AnimalViewModel"/>
<variable
name="presenter"
type="io.ditclear.app.helper.Presenter"
</data>
<LinearLayout
tools:context="io.ditclear.app.view.AnimalActivity">
<Button
android:id="@+id/save_btn"
android:onClick="@{(v)->presenter.onClick(v)}"/>
<Button
android:id="@+id/delete_btn"
android:onClick="@{(v)->presenter.onClick(v)}"/>
<Button
android:id="@+id/submit_btn"
android:onClick="@{(v)->presenter.onClick(v)}"/>
</LinearLayout>
</layout>
复制代码
这里推荐使用(v)->presenter.onClick(v)的写法,缘由之一是比较直观一点,其二是须要参数view
接着在activity/fragment中来实现Presenter接口,处理点击事件
public class AnimalActivity extends AppCompatActivity implements Presenter { private AnimalActivityBinding mBinding; private AnimalViewModel mViewModel; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding= DataBindingUtil.setContentView(this, R.layout.animal_activity); Animal animal=new Animal("dog",1); mViewModel=new AnimalViewModel(animal); mBinding.setVm(viewModel); mBinding.setPresenter(this); } @SingleClick @Override public void onClick(View v) { //根据id进行区分 switch (v.getId()){ case R.id.save_btn: save(); break; case R.id.delete_btn: delete(); break; case R.id.submit_btn: submit(); break; } } private void submit(){ //调用viewModel的方法 mViewModel.submit(); } } 复制代码
@SingleClick是一个注解,做为AspectJ的切面,来防止屡次点击,须要将view做为参数,详细可参考文章DataBinding结合AspectJ防止屡次点击
若是你使用RxJava和RxLifeCycle来处理数据和管理生命周期,那么这里的submit()
方法将会更加简单。
private void submit(){ //调用viewModel的方法 mViewModel.submit() .compose(bindToLifecycle()) .subscribe({ //success },{ //error }) } 复制代码
详细状况能够看这篇文章:Retrofit及RxJava
RecyclerView功能极其强大,能作到的事情不少,网上已经出现不少关于多类型RecyclerView的处理方法,在使用DataBinding这一年多时间里,感觉即是使用DataBinding来处理RecyclerView Item再合适不过,充分作到了数据和itemView的完美分离,告别了反复、冗余的自定义Adapter,不须要关心太多无心义的事情。
详情请查看github地址(kotlin版本):github.com/ditclear/Bi…
tools能够告诉Android Studio,哪些属性在运行的时候是被忽略的,只在设计布局的时候有效。好比咱们要让android:text属性只在布局预览中有效能够这样
<TextView android:id="@+id/text_main" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Title" android:layout_margin="@dimen/main_margin" android:text="@{vm.title}" tools:text="I am a title" /> 复制代码
tools能够覆盖android的全部标准属性,将android:换成tools:便可。同时在运行的时候就连tools:自己都是被忽略的,不会被带进apk中。
好比为ImageView,你定义了一个BindingAdapter
@BindingAdapter("url") public static void bindImgUrl(ImageView imageView,String url){ Glide.with(imageView.getContext()).load(url).into(imageView); } 复制代码
实际状况中,ImageView并无url这个属性, 这时能够在attrs.xml文件中为ImageView添加这一属性,rebuild一下项目,之后就能自动补全属性了
<declare-styleable name="ImageView"> <attr name="url" format="string"/> </declare-styleable> 复制代码
不少开发者放弃DataBinding缘由就在于出错了不容易排查错误。 只显示出不少XXBinding未找到。 若是有必定使用经验的就知道只看最后一条报错信息就够了。 这里介绍一种我常用来排查错误的方式: 在Android Studio 的terminal 里运行
./gradlew clean assembleDebug
或者
./gradlew compileDebugJavaWithJavac
由于DataBinding是编译生成代码的,不少错误都是xml中表达式写的有问题致使的,因此运行以上命令容易在terminal中打印出具体错误的信息。这些命令对于须要编译生成代码的框架排查错误十分有用,好比Dagger2。
DataBinding使用起来很简单,可是因为它没有一个统一的规范和写法,须要靠开发者本身去摸索和研究才能熟练运用,而这其中又会出现一些小坑,因此可能致使刚学习的人以为难以驾驭而被迫放弃。可是它倒是一门很是实用的技术,无论你是否使用MVVM架构,单凭它能够减小不少冗余的代码和跟RecyclerView的完美契合的优势就值得去了解和使用它。
关于怎么较好的实践,总结一哈:
在通过一年以上实践后,总结出了以上的一些避免踩坑的方式和较好的实践方法,但愿对准备学习、正在学习或者正在使用的同窗一些帮助。
毕竟对于DataBinding :使用得当,那它就是神兵利器,使用不当,那么便伤人(Code)伤己。