Android~ViewBinding使用学习

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标签需要注意的点文中有详细说明)

原创文章 100 获赞 194 访问量 28万+

猜你喜欢

转载自blog.csdn.net/Bluechalk/article/details/105938450