因为前段时间了解到了google推出的数据绑定框架databinding,就使用它实现了一个简单的android的mvvm架构思想的demo。java
使用过程之中很happy,按照其使用方式,框架会自动生成布局文件对应的XXXBinding类文件。不再用findViewById了,也不再用使用注解框架在Activity或者Fragment中写大量的控件属性了,哇,整个世界都清净了。。。。这感受太爽了。。。android
结果遇到问题了,也不知道怎么解决。。。。幸亏有google老师带咱们学习。数组
前面都是废话,下面就详细分析我遇到的问题吧!架构
使用一个Activity + 对应的布局文件举例:app
DemoActivity:框架
public class DemoActivity extends BaseActivity<DemoVM, ActivityDemoBinding> { @Bind(R.id.linkTv) TextView linkTv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initWidget(); } @Override protected void initBinding() { setBinding(DataBindingUtil.<ActivityDemoBinding>setContentView(this, R.layout.activity_demo)); binding.setVm(getViewModel()); } @Override protected void initModel() { } @Override public void initViewModel() { setViewModel(new DemoVM(this)); } private void initWidget() { linkTv.setText(Html.fromHtml(getResources().getString(R.string.url))); linkTv.setMovementMethod(LinkMovementMethod.getInstance()); } @Override protected boolean hasBackButton() { return false; } @Override protected int getToolbarTitle() { return R.string.title_activity_demo; }
activity_demo.xml(include了一个布局文件):mvvm
<?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:bind="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data> <import type="com.zdj.presentation.modules.demo.vms.DemoVM" /> <variable name="vm" type="DemoVM" /> </data> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="com.zdj.presentation.modules.demo.views.DemoActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay" app:elevation="0dp"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_demo" bind:vm="@{vm}" /> </android.support.design.widget.CoordinatorLayout> </layout>
content_demo.xml:ide
<?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:bind="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data parent="@"> <import type="com.zdj.presentation.modules.demo.vms.DemoVM"/> <variable name="vm" type="DemoVM"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="com.zdj.presentation.modules.demo.views.DemoActivity" tools:showIn="@layout/activity_demo"> <TextView android:id="@+id/linkTv" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center"/> <Button android:id="@+id/btn_LoadData" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/btn_text_load_data" bind:onClickListener="@{vm.onClickLoadData}" /> </LinearLayout> </layout>
由Activity的initWidget方法可见,若要使用content_demo中的TextView控件,还须要注解框架或者经过findViewById方法也行,找到指定的控件,而后对其处理。这样其实就是生成了两个控件实例在操做布局文件 中的控件了,可见的一个实例在Activity中,那么另外一个呢?它在content_demo.xml生成的ContentDemoBinding类中,而activity_demo.xml生成的对应的数据绑定类ActivityDemoBinding含有ContentDemoBinding一个类型的私有的属性,而偏偏的想要操做的view是ContentDemoBinding的一个属性,activity是访问不到的。函数
有没有什么办法彻底使用databinding框架自动给咱们生成的各个实例呢?这样好处至少有两个:1 不用使用findViewById 或者第三方注解框架 2 避免生成前面提到的两个控件实例对象布局
下面介绍方法,其实很简单,针对activity_demo.xml作一点点的改变便可:
<?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:bind="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data> <import type="com.zdj.presentation.modules.demo.vms.DemoVM" /> <variable name="vm" type="DemoVM" /> </data> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="com.zdj.presentation.modules.demo.views.DemoActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay" app:elevation="0dp"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <!-- 须要改变的地方:加上 android:id="@+id/views" 便可 --> <include layout="@layout/content_demo" android:id="@+id/views" bind:vm="@{vm}" /> </android.support.design.widget.CoordinatorLayout> </layout>
布局文件修改以后,代码的变化,这里只贴了initWidget方法了,由于除了去除对BufferKnife的依赖之外(能够删除控件属性了),改变的只有这里:
private void initWidget() { // 终于能够在Activity中直接访问include的控件了,这个简单的问题困扰了我好长时间。。。。 binding.views.linkTv.setText(Html.fromHtml(getResources().getString(R.string.url))); binding.views.linkTv.setMovementMethod(LinkMovementMethod.getInstance()); }
究其缘由,实际上是在ActivityDemoBinding类中添加了一个属性 public final ContentDemoBinding views,正好符合个人要求,提供了公共的访问权限。
在布局文件中,全部带id的标签都会映射为布局文件生成的对象的一个属性,由生成的binding对象的构造函数能够看出来:
public ActivityDemoBinding(android.databinding.DataBindingComponent bindingComponent, View root) { super(bindingComponent, root, 2); // 首先会将全部带id的view映射为object数组 final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds); // 这里至于 view 对应的 bindings第几个元素,须要细究ViewDataBinding类的mapBindings方法 this.mboundView0 = (android.support.design.widget.CoordinatorLayout) bindings[0]; this.mboundView0.setTag(null); this.toolbar = (android.support.v7.widget.Toolbar) bindings[2]; this.views = (com.zdj.presentation.databinding.ContentDemoBinding) bindings[1]; setRootTag(root); // listeners invalidateAll(); }
最后不忘贴出参考地址