V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
banxi1988
V2EX  ›  Android

Data Binding 初体验

  •  3
     
  •   banxi1988 · 2015-06-04 23:11:56 +08:00 · 9781 次点击
    这是一个创建于 3490 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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
    2 条回复    2015-06-05 18:35:55 +08:00
    jimmy
        1
    jimmy  
       2015-06-05 10:27:06 +08:00
    让我想起了JSP的写法
    CtrlSpace
        2
    CtrlSpace  
       2015-06-05 18:35:55 +08:00
    学习了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1289 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:40 · PVG 01:40 · LAX 09:40 · JFK 12:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.