Data-Binding(一):别在使用FindViewById()了

在通过AS开发Android APP的一个鲜为人知的特性就是使用数据绑定(data binding)。通过这种手段,可以实现非常多有用的特性,这些会在我未来的文章中有所介绍,但有一点最最基础需要你知道的事情就是去掉了findViewById。

是否这些代码是让你头疼的

TextView hello = (TextView) findViewById(R.id.hello); 

有很多有用的工具,主要用来消除这些代码里面类似这样的代码。但是在AS 1.5或者更高级的版本中,官方提供了一个方法可以替代上面的操作。

首先你需要修改你的APP的build.gradle文件,在android的代码块后,添加下面的语句

	android {
   		…
    	dataBinding.enabled = true
	}

接下来你需要在你的layout文件的最外层添加tag ,用来替代任何你所使用的ViewGroup:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
    <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin"
            android:paddingBottom="@dimen/activity_vertical_margin"
            tools:context=".MainActivity">

        <TextView
                android:id="@+id/hello"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>

    </RelativeLayout>
</layout>

这个layout标签用来提示AS在进行下一步之前需要额外的处理,找到一些有趣的Views并且标记他们。所有没有添加外层layout标签的的布局是不会经历多余的处理过程,所以你可以在你的程序中不修改其他任何东西的前提下,方便的将其他你喜欢的地方。

你所要做的下一键事情就是告诉它在运行时载入不同的布局文件。由于这些工作又回到了Eclaire的release,对于新的架构变化不依赖加载这些预处理的布局文件。因此,你需要修改一下你的加载过程。

对于一个Activity,替换之前的:

setContentView(R.layout.hello_world);
TextView hello = (TextView) findViewById(R.id.hello);
hello.setText("Hello World"); // for example, but you'd use
                              // resources, right?

你可以这样的去加载它:

HelloWorldBinding binding = 
    DataBindingUtil.setContentView(this, R.layout.hello_world);
binding.hello.setText("Hello World"); // you should use resources!

这里你可以看到这样的一个类,HelloWorldBinding是通过hello_world.xml布局文件和具有ID"@+id/hello"的View去分配的一个你可以使用的final作用域hello。没有转换,没有findViewById。

这种访问views的机制不但相较于findViewById更加简单,而且速度也更加快!绑定的过程对于一个布局上的所有Views只需要一次遍历,就可以注册对应的作用域。若你使用findViewById,你的view层级需要在每一次的调用中都去寻找。

还有一件事情,就是你看到的变量名是通过驼峰命名法来的(就像hello_world.xml最后定义为了HelloWorldBinding类),所有你给ID"@+id/hello_text"的命名应该为helloText

若你将布局文件扩展到RecyclerView,ViewPager,或者其他没有设置在Activity的内容中的地方,你需要在通用的类中使用通用的类型安全的方法调用。这里有很多适合LayoutInflater的版本,你只需要挑选一个你最喜欢的方式。例如:

HelloWorldBinding binding = HelloWorldBinding.inflate(
    getLayoutInflater(), container, attachToContainer);

如果你无法在ViewGroup的容器中去扩展,你需要去扩展View层级。(这一句翻译的还是有问题)你可以在绑定的时候通过getRoot()得到:

linearLayout.addView(binding.getRoot());

现在,你可能会想,如果我通过一些不同的Views用不同的构造方法但又要使用一个布局时改怎么办?在布局的预处理和运行时的阶段,对于你所绑定的View IDs生成了一个类,只不过是将那些你没有使用的扩展layout设置为Null。

是不是很神奇?最好玩的部分在于,通过这种方法在运行时没有使用反射,也没有使用任何高科技手段。这可以非常简单的将你目前的APP接入这种方法,这会使得你的工作更加简单一点,并且还可以使你的layouts加载的更加快一点。


第一次翻译,有很多地方还不是很规范,不断学习吧。

原始文章地址


历史文章记录:

  1. Data-Binding(一):别在使用FindViewById()了
  2. Data-Binding(二):include标签的使用
  3. Data-Binding (三):让View-Id不再那么必要
  4. Data Binding(四):事件监听