本文共 6956 字,大约阅读时间需要 23 分钟。
Butter Knife框架是17年前后很火的存在。但是在Kotlin中直接使用ButterKnife的注解方式的话,会出现空指针的异常并导致绑定失败。从而Kotter Knife应运而生,可以理解成是Butter Knife的Kotlin版本。用法如下:
//Butter Knife@BindView(R.id.title) TextView title;ButterKnife.bind(this);// TODO Use fields...
// Kotter Knifeval submitButton: Button by bindView(R.id.submit_button)// TODO Use val...
然而这种方案已经被作者标记为Deprecated,作者认为该框架为每个视图引用分配了一个对象,这种思路不应该被采用,以及使用这种思路的框架也应该被淘汰,并推荐使用ViewBinding的方式。
依稀记得Data Binding是16年底的时候推出的,那时候真的很火,因为它是谷歌对于MVVM开发模式在Android上体现之一,其底层通过Annotation Processor实现的。用法如下,关键就是新增了data节点,以及在layout中的属性表达式@{}。
默认情况下,基于layout文件的名称会生成单词首字母大写并添加“Binding”后缀一个Binding类。此类包含layout属性以及在Views中的所有binding属性(例如user变量),并且能够为该属性赋值。因此在Java代码中:
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);User user = new User("firstName", "lastName");binding.setUser(user);
但是Data Binding的劣势也很明显:
这就是今天的主角了,View Binding既不会像Butter Knife那样为View分配多余的对象,也不会像Data Binding那样使用Annotation Processor,它的实现既简单又强大。首先来看一下如何使用View Binding。
buildscript { ... dependencies { classpath "com.android.tools.build:gradle:3.6.1" }}android { ... viewBinding { enabled = true }}
这里升级gradle的时候遇到了一些小坑,即工程中使用的tinker版本过低,其使用了旧版gradle中的语法,因此会报错,将tinker版本升级到’1.9.14.6’后问题解决。
activity_main.xml的布局如下:
这里直接include了一个子布局,inner_layout.xml的布局如下:
public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(LayoutInflater.from(this)); setContentView(binding.getRoot()); binding.firstButton.setText("Text Change"); binding.innerLayout.innerLayoutSecondButton.setText("Inner Layout Button Text Change"); }}
用法也很简单,直接通过binding点名字的方式就可以获取到控件实例,消除了findViewById的模版代码。
这里可以看到,setContentView的入参写法都变了,因为可通过XXXBinding类的getRoot函数获取到布局的根View,再通过setContentView添加到Activity。在2.2中我们演示了include的用法,只需要加一个id,我们就可以通过binding.innerLayout获取到该子布局。但是有个特殊情况需要处理,就是merge标签。
如果子布局inner_layout.xml中的布局如下,多了一个merge标签:那么在activity_main.xml的布局的include标签中就一定不要设置id了(这里就不贴activity_main.xml的代码了),否则会找不到View报空指针异常。这个情况,我们可以先初始化主布局,再初始带merge的布局,那么就需要用到View Binding的另一个构造方法了,处理如下:
public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(LayoutInflater.from(this)); setContentView(binding.getRoot()); //使用 binding.firstButton.setText("Text Change"); //merge标签处理 InnerLayoutBinding subBinding = InnerLayoutBinding.bind(binding.getRoot()); subBinding.innerLayoutSecondButton.setText("Inner Layout Button Text Change"); }}
从2.2和2.3中我们看到了Binding类的两种构造方法,其实它有三种使用方法,如下所示:
inflate(@NonNull LayoutInflater inflater)inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, boolean attachToParent)bind(@NonNull View rootView)
// Generated by view binder compiler. Do not edit!public final class ActivityMainBinding implements ViewBinding { @NonNull private final LinearLayout rootView; @NonNull public final Button firstButton; @NonNull public final InnerLayoutBinding innerLayout; private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull Button firstButton, @NonNull InnerLayoutBinding innerLayout) { this.rootView = rootView; this.firstButton = firstButton; this.innerLayout = innerLayout; } @Override @NonNull public LinearLayout getRoot() { return rootView; } @NonNull public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) { return inflate(inflater, null, false); } @NonNull public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, boolean attachToParent) { View root = inflater.inflate(R.layout.activity_main, parent, false); if (attachToParent) { parent.addView(root); } return bind(root); } @NonNull public static ActivityMainBinding bind(@NonNull View rootView) { String missingId; missingId: { //绑定View且做了非空判断,否则抛出空指针 Button firstButton = rootView.findViewById(R.id.first_button); if (firstButton == null) { missingId = "firstButton"; break missingId; } View innerLayout = rootView.findViewById(R.id.inner_layout); if (innerLayout == null) { missingId = "innerLayout"; break missingId; } InnerLayoutBinding innerLayoutBinding = InnerLayoutBinding.bind(innerLayout); return new ActivityMainBinding((LinearLayout) rootView, firstButton, innerLayoutBinding); } throw new NullPointerException("Missing required view with ID: ".concat(missingId)); }}
block:{ if(condition) break block; // rest of code that won't be executed if condition is true}
outterLoop: for(int i = 0; i < 10; i++){ while(condition) { if(someConditon) break outterLoop; // 结束for循环 if(anotherConditon) break; // 结束while循环 // more code } // more code}
转载地址:http://zewci.baihongyu.com/