1. Problems solved
The purpose of ViewBinding is to reduce the use of template code findViewById(int), improve development efficiency, and simplify the code of Activity and Fragment.
2. Using ViewBinding
The use of ViewBinding requires Android Studio version 3.6 or above, and it needs to be installed in the build.gradle Open manually in the file as follows:
android { ... buildFeatures { viewBinding = true } }
After that, Android Studio will automatically generate the corresponding Java class for the layout file under the layout folder. The naming rule is to name the big hump of the file name and add Binding after it.
Like activity_main.xml The layout file is as follows:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:context=".MainActivity"> <TextView android:id="@+id/timestampText" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" android:textColor="@color/colorPrimary" android:gravity="center" android:padding="20dp" tools:text="Hello world" app:layout_constraintTop_toTopOf="parent"/> <Button android:id="@+id/timestampButton" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/timestampText" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" android:text="Get timestamp"/> <FrameLayout android:id="@+id/fragmentContainer" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/timestampButton" app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="20dp"/> </androidx.constraintlayout.widget.ConstraintLayout>
Will generate ActivityMainBinding.java Documents. Then in MainActivity, get an activitymainbinding instance through the static method inflate (layoutinflate) of activitymainbinding, and then directly access each control through the instance.
MainActivity.java As follows:
public class MainActivity extends AppCompatActivity { private ActivityMainBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Get the instance of the 'Binding' class through the 'inflate (layoutinflate)' method, and get the root layout through the 'getRoot()' method mBinding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(mBinding.getRoot()); // Access controls by accessing fields mBinding.timestampButton.setOnClickListener(v -> { mBinding.timestampText.setText(String.valueOf(System.currentTimeMillis())); }); getSupportFragmentManager().beginTransaction() .replace(mBinding.fragmentContainer.getId(), new TestFragment()) .commit(); } }
If it is in Fragment, it can be used as follows:
public class TestFragment extends Fragment { private FragmentTestBinding mBinding; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // Use the 'inflate (layoutinflate, ViewGroup, Boolean)' method to get the 'Binding' instance. The subsequent usage is the same. mBinding = FragmentTestBinding.inflate(inflater, container, false); mBinding.include.prettyTimeButton.setOnClickListener(v -> { SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss", Locale.CHINA); String prettyText = sdf.format(new Date()); mBinding.include.prettyTimeText.setText(prettyText); }); return mBinding.getRoot(); } @Override public void onDestroyView() { mBinding = null; super.onDestroyView(); } }
If you use < include / > you need to specify an id for it to ensure that ViewBinding can generate corresponding fields for it
If it is used in RecyclerView, use the bind(View) method to obtain the Binding instance in the ViewHolder constructor, and directly access the view through the instance in onCreateViewHolder, as follows:
public class TestRecyclerViewAdapter extends RecyclerView.Adapter<TestRecyclerViewAdapter.ViewHolder> { private List<RvBean> mList; public TestRecyclerViewAdapter(List<RvBean> list) { mList = list; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_item_test, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { final RvBean item = mList.get(position); holder.mBinding.content.setText(item.content); holder.mBinding.title.setText(item.title); holder.mBinding.avatar.setImageResource(item.imageId); } @Override public int getItemCount() { return mList.size(); } static class ViewHolder extends RecyclerView.ViewHolder{ RvItemTestBinding mBinding; public ViewHolder(@NonNull View itemView) { super(itemView); mBinding = RvItemTestBinding.bind(itemView); } } }
3. Principle analysis
The generated Binding file is as follows:
public final class ActivityMainBinding implements ViewBinding { @NonNull private final ConstraintLayout rootView; @NonNull public final FrameLayout fragmentContainer; @NonNull public final Button timestampButton; @NonNull public final TextView timestampText; private ActivityMainBinding(@NonNull ConstraintLayout rootView, @NonNull FrameLayout fragmentContainer, @NonNull Button timestampButton, @NonNull TextView timestampText) { this.rootView = rootView; this.fragmentContainer = fragmentContainer; this.timestampButton = timestampButton; this.timestampText = timestampText; } @Override @NonNull public ConstraintLayout 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.fragmentContainer; FrameLayout fragmentContainer = rootView.findViewById(id); if (fragmentContainer == null) { break missingId; } id = R.id.timestampButton; Button timestampButton = rootView.findViewById(id); if (timestampButton == null) { break missingId; } id = R.id.timestampText; TextView timestampText = rootView.findViewById(id); if (timestampText == null) { break missingId; } return new ActivityMainBinding((ConstraintLayout) rootView, fragmentContainer, timestampButton, timestampText); } String missingId = rootView.getResources().getResourceName(id); throw new NullPointerException("Missing required view with ID: ".concat(missingId)); } }
To access the root layout, ViewBinding provides the getRoot() method. At the same time, each control with id in the layout will generate a corresponding field for the caller to access.
The core method is the bind(View) method, which returns a Binding object. All the controls with id in the layout are constructed and set as fields through findViewById(int) method. Other methods, inflate (layoutinflate, ViewGroup, Boolean) and inflate (layoutinflate), are the encapsulation of the method.
4. Advantages and disadvantages
advantage:
- Simplify the template code, get rid of findViewById(int), and greatly reduce the template code in Activity, Fragment and RecyclerView. When there are more controls in a page, the effect is obvious.
- There is no null pointer problem. If findViewById(int) is used in SecondActivity to refer to the control id of MainActivity, it will be difficult to find out when it is not running. View Binding effectively solves this problem.
- Type safety, there will be no problem of forced conversion of control type.
Disadvantages:
- New classes are generated at compile time, resulting in an increase in compile time.
- As the number of classes increases, the package size will increase when the layout is large.