ViewBinding替换findviewbyid
1.有啥优点?
- 比DataBinding轻量和快速,DataBinding中layout文件如果实现双向绑定会改变原有布局文件,业务和视图关联度高。
- 更安全,kotlin扩展Synthetics也可直接通过id访问控件,但是全局的会产生空指针错误。
- 编译安全,findviewbyid和ButterKnife会存在类型转换问题,这一错误出现在运行时,而不是编译时。产生的Binding类是根据layout文件生成,所以生成时就已经处理完成。
2. 使用
先更新AS到3.6及以上,在module的gradle中启用viewBinding。
注: 如果你不想把XMLlayout文件中某个含有Id的view包含到生成类中的话,就设置此view的tools:viewBindingIgnore属性为true
android {
...
viewBinding {
enabled = true
}
...
}
Activity中使用:
package com.viewbinding.test;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.os.Bundle;
import com.viewbinding.test.ui.main.MainFragment;
import com.viewbinding.test.databinding.MainActivityBinding;
public class MainActivity extends AppCompatActivity {
private MainActivityBinding activityBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityBinding = MainActivityBinding.inflate(getLayoutInflater());
setContentView(activityBinding.getRoot());
activityBinding.container.setBackgroundColor(Color.DKGRAY);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow();
}
}
}
Fragment中使用
package com.viewbinding.test.ui.main;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.viewbinding.test.databinding.MainFragmentBinding;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
public class MainFragment extends Fragment {
private MainFragmentBinding fragmentBinding;
private MainViewModel mViewModel;
public static MainFragment newInstance() {
return new MainFragment();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
fragmentBinding = MainFragmentBinding.inflate(getLayoutInflater());
return fragmentBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
fragmentBinding.message.setText("This is Test.");
mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
}
@Override
public void onDestroyView() {
super.onDestroyView();
fragmentBinding = null; // 避免内存泄漏
}
}
ViewHolder中使用:
static class ItemViewHolder extends RecyclerView.ViewHolder{
private final ItemBinding binding;
public BeautyViewHolder(@NonNull View itemView) {
super(itemView);
binding= ItemBinding.bind(itemView);
}
}
3. 实现原理
我们可以通过分析编译生成的中间文件MainFragmentBinding.java源码,ViewBinding中重要的函数getRoot()、inflate()、bind(),可以在bind函数中看到熟悉的findviewbyid操作,谷歌在编译的Gradle插件动了手脚,当开启viewBinding后编译时去扫描Layout文件自动生成Binding类。
// Generated by view binder compiler. Do not edit!
package com.viewbinding.test.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.viewbinding.ViewBinding;
import com.viewbinding.test.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class MainFragmentBinding implements ViewBinding {
@NonNull
private final ConstraintLayout rootView;
@NonNull
public final ConstraintLayout main;
@NonNull
public final TextView message;
private MainFragmentBinding(@NonNull ConstraintLayout rootView, @NonNull ConstraintLayout main,
@NonNull TextView message) {
this.rootView = rootView;
this.main = main;
this.message = message;
}
@Override
@NonNull
public ConstraintLayout getRoot() {
return rootView;
}
@NonNull
public static MainFragmentBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static MainFragmentBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.main_fragment, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static MainFragmentBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
String missingId;
missingId: {
ConstraintLayout main = rootView.findViewById(R.id.main);
if (main == null) {
missingId = "main";
break missingId;
}
TextView message = rootView.findViewById(R.id.message);
if (message == null) {
missingId = "message";
break missingId;
}
return new MainFragmentBinding((ConstraintLayout) rootView, main, message);
}
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}
参考:
1、你好, View Binding! 再次再见, findViewById!
2、是时候拥抱ViewBinding了!! (layout中使用include、merge标签需要注意的点文中有详细说明)