Data Binding 初体验

2015-06-04 23:11:56 +08:00
 banxi1988

Data Binding 初体验

简单使用

用于 Data-bindng 的模板文件,是由 data 节点 跟 layout 节点组成.
一个简单的示例如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>

使用上来说,如下:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityDataBindingBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_data_binding);
        binding.setUser( new User("Haizhen","Lee"));
    }

然后就OK了, 以前繁杂的findViewById的调用,及设置值

与RecyclerView 结合使用示例

与普通的 ViewHolder有所区别, 保存一个 Binding应该是比较好的选择:

class UserViewHolder extends RecyclerView.ViewHolder {
    private RecyclerListItemBinding mBinding;
    public UserViewHolder(RecyclerListItemBinding binding) {
        super(binding.getRoot());
        mBinding = binding;
    }

    public void bind(User user){
        mBinding.setUser(user);
    }
}

然后修改RecyclerViewAdapter如下:

class DemoRecyclerViewAdapter extends  RecyclerView.Adapter<UserViewHolder>{
    private List<User> userList;
    private static final int layoutId = R.layout.recycler_list_item;

    public DemoRecyclerViewAdapter(List<User> users){
        this.userList = users;
    }

    @Override
    public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        RecyclerListItemBinding binding = DataBindingUtil.inflate(layoutInflater,layoutId,parent,false);
        return new UserViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(UserViewHolder holder, int position) {
        User user = userList.get(position);
        holder.bind(user);
    }

    @Override
    public int getItemCount() {
        return userList.size();
    }
}

好奇的心

  1. ActvityDataBindingBinding 怎么来的?
    这是 Android 编译系统 根据布局文件名 activity_data_binding.xml 生成的
    是编译期动态生成类

  2. DataBindingUtil 做了什么?

查看源代码如下:

public static <T extends ViewDataBinding> T setContentView(Activity activity, int layoutId) {
        View decorView = activity.getWindow().getDecorView();
        ViewGroup contentView = (ViewGroup)decorView.findViewById(16908290);
        ViewDataBinding binding = inflate(activity.getLayoutInflater(), layoutId, contentView, false);
        activity.setContentView(binding.getRoot(), binding.getRoot().getLayoutParams());
        return binding;
    }

看看ActivityDataBindingBinding 怎么绑定的.

可以看到绑定User对象的操作在 executeBindings中进行.

@Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        java.lang.String firstNameUser = null;
        java.lang.String lastNameUser = null;
        com.banxi1988.v2exgeek.demo.User user = mUser;

        if ((dirtyFlags & 0b11L) != 0) {
            // read firstName~.~user~
            if ( user != null) {
                firstNameUser = user.firstName;
            }

            // read lastName~.~user~
            if ( user != null) {
                lastNameUser = user.lastName;
            }
        }
        // batch finished
        if ((dirtyFlags & 0b11L) != 0) {
            // api target 1
            this.mboundView1.setText(firstNameUser);
        }
        if ((dirtyFlags & 0b11L) != 0) {
            // api target 1
            this.mboundView2.setText(lastNameUser);
        }
    }

新布局文件编译后的中间产物

build/layout-info/debug 目录下生成的如下内容的
文件,名为:activity_data_bindingLayout.xml文件中

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Layout layout="activity_data_binding"
    modulePackage="com.banxi1988.v2exgeek"
    directory="layout"
    isMerge="false">
    <Variables>
        <entries type="com.banxi1988.v2exgeek.demo.User" name="user"/>
    </Variables>
    <Targets>
        <Target tag="layout/activity_data_binding_0" view="LinearLayout">
            <Expressions/>
        </Target>
        <Target tag="binding_1" view="TextView">
            <Expressions>
                <Expression text="user.firstName" attribute="android:text"/>
            </Expressions>
        </Target>
        <Target tag="binding_2" view="TextView">
            <Expressions>
                <Expression text="user.lastName" attribute="android:text"/>
            </Expressions>
        </Target>
    </Targets>
</Layout>

多一些了解

  1. 类的生成使用了 antlr
  2. 对 null 宽容, 也提供了 ?? 操作符,方便的为null的引用设置默认值
  3. 想起了 AngularJS, 以前的 JSP, 但是说绑定肯定不如AngularJS.
    不过对于 Android 开发者来说,也已经是极大的进步了.

  4. 在xml的属性中写表达式, 要用到小于号大于号的都觉得挺悲剧的,希望正式版本能有更好的写法
    android:visibility="@{age &lt; 13 ? View.GONE : View.VISIBLE}"
    <variable name="userList" type="List&lt;User>"/>

编译期的元编程

  1. data binding 选择 在编译期来做 View 查找定位,生成数据绑定代码, 这样保证了性能.
  2. 以前使用过 ButterKnife 就觉得非常好用,data binding 进一步增加了好用程度. 不过他们都有一个特点就是,充分利用了编译期动态生成代码, ButterKnife 对 annotation processing 的依赖也更强一些. 另一个有名依赖注入库,Dagger 也是充分利用了注解在编译期生成代码. 这样为了编程的编程,这就是我心中的元编程.
  3. 希望有更多基于Java注解,基于编译期代码生成的工具,库出来,造福广大开发者

参考文档

  1. Data Binding Guide
9845 次点击
所在节点    Android
2 条回复
jimmy
2015-06-05 10:27:06 +08:00
让我想起了JSP的写法
CtrlSpace
2015-06-05 18:35:55 +08:00
学习了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/196198

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX