Android Jetpack 架构组件(一) View Binding

 

google官网:https://developer.android.google.cn/topic/libraries/view-binding

有了View Binding,不需要findViewById(),也不需要ButterKnife。使用更简单。

 

一. 使用步骤:

在相应的模块的build.gradle文件里添加:

android {
        ...
        viewBinding {
            enabled = true
        }
    }
    

 

经过编译后layout的xml布局文件会生成一一对应的java文件。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/txtName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

然后,在activity代码要生成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 = DataBindingUtil.setContentView(this,
                R.layout.activity_main);

        binding.txtName.setText("hello world");
        binding.txtName.setOnClickListener(new View.OnClickListener() {

        });
    }
}

 

下面就是最终效果:

android:id="@+id/txtName" -----》binding.txtName

此时就不需要findviewbyid(), 直接用binding.txtName就可以setText().

 

在Fragment使用方式也一样,但是Fragment 的存在时间比其View长。所以请务必在 Fragment 的 onDestroyView() 方法中清除对绑定类实例的所有引用。

​
    private ResultProfileBinding binding;

    @Override
    public View onCreateView (LayoutInflater inflater,
                              ViewGroup container,
                              Bundle savedInstanceState) {
        binding = ResultProfileBinding.inflate(inflater, container, false);
        View view = binding.getRoot();
        return view;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }
    

​

二. View Binding原理

Android Studio会将Resourse layout 下面的XML 文件。一一对应的编译成一个java文件。生成在目录:app\build\generated\data_binding_base_class_source_out\debug\out\com\XXX\XXX\databinding\

命名方式:将XML 文件的名称转换为驼峰式大小写,并在末尾添加“Binding”一词。

比如:activity_main.xml ---->ActivityMainBinding.java, 

如果想忽略某个布局文件,那么将 tools:viewBindingIgnore="true" 属性添加到相应布局文件的根view中, 就像下面那样,在<LinearLayout>中加了tools:viewBindingIgnore="true"。这样就不会生成绑定类了。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:viewBindingIgnore="true">
    <TextView
        android:id="@+id/txtName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

 

那么生成的绑定类有什么用呢?为什么不需要findViewById()这个操作?

我们看看ActivityMainBinding.java这个文件里到底做了哪些事情。简单的说,绑定类就是在做findViewById()的工作,然后把各个view的实例保存在绑定类, 方便开发者可以直接使用。

public final class ActivityMainBinding implements ViewBinding {
  @NonNull
  private final LinearLayout rootView;

  @NonNull
  public final TextView txtName;

  @NonNull
  public final TextView txtName1;

  @NonNull
  public final TextView txtName2;

  private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull TextView txtName,
      @NonNull TextView txtName1, @NonNull TextView txtName2) {
    this.rootView = rootView;
    this.txtName = txtName;
    this.txtName1 = txtName1;
    this.txtName2 = txtName2;
  }

  @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) {
    // 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.
    int id;
    missingId: {
      id = R.id.txtName;
      TextView txtName = rootView.findViewById(id);
      if (txtName == null) {
        break missingId;
      }

      id = R.id.txtName1;
      TextView txtName1 = rootView.findViewById(id);
      if (txtName1 == null) {
        break missingId;
      }

      id = R.id.txtName2;
      TextView txtName2 = rootView.findViewById(id);
      if (txtName2 == null) {
        break missingId;
      }

      return new ActivityMainBinding((LinearLayout) rootView, txtName, txtName1, txtName2);
    }
    String missingId = rootView.getResources().getResourceName(id);
    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
  }
}

比较好的一点是有个getRoot()的接口。这样就可以使用

方式一:

        binding = ActivityMainBinding.inflate(LayoutInflater.from(this));
        setContentView(binding.getRoot())。

由此增加了灵活度。比如,app大多数的activity都有一样风格的toolbar。那么有一个基类ToolbarActivity,其他继承ToolbarActivity的activity只要把getRoot()的view放到ToolbarActivity的body里面就可以了。

 

另外:

1. 不支持<include/>

2. Android UI Framework的发展会越来越靠向WPF(Windows Presentation Foundation)。Android的 View Binding & Data Binding,到现在依旧远没有赶上WPF。

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值