赞
踩
ViewBinding
的目的是为了减少样板代码findViewById(int)
的使用,提升开发效率,精简Activity
和Fragment
的代码。
ViewBinding
ViewBinding
的使用需要Android Studio
版本为3.6以上,需要在模块的build.gradle
文件中手动开启,如下:
android {
...
buildFeatures {
viewBinding = true
}
}
之后Android Studio
会自动为layout
文件夹下的布局文件生成对应的Java
类,命名规则为文件名的大驼峰命名并在后面加上Binding
。
比如activity_main.xml
布局文件如下:
<?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>
将会生成ActivityMainBinding.java
文件。然后在MainActivity
中,通过ActivityMainBinding
的静态方法inflate(LayoutInflater)
获取一个ActivityMainBinding
实例,之后通过该实例直接访问各控件。
MainActivity.java
如下:
public class MainActivity extends AppCompatActivity { private ActivityMainBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 通过 `inflate(LayoutInflater)`方法获取 `Binding`类的实例,并通过 `getRoot()` 方法得到根布局 mBinding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(mBinding.getRoot()); // 通过访问字段的方式访问各控件 mBinding.timestampButton.setOnClickListener(v -> { mBinding.timestampText.setText(String.valueOf(System.currentTimeMillis())); }); getSupportFragmentManager().beginTransaction() .replace(mBinding.fragmentContainer.getId(), new TestFragment()) .commit(); } }
如果是在Fragment
中,则通过如下方式使用:
public class TestFragment extends Fragment { private FragmentTestBinding mBinding; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // 使用 `inflate(LayoutInflater, ViewGroup, boolean)` 方法获取 `Binding` 实例,后续使用方式相同。 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(); } }
如果使用<include/>
则需要为其指定一个id
,保证ViewBinding
可以为其生成相应的字段
如果是在RecyclerView
中使用,则使用bind(View)
方法在ViewHolder
构造器中获取Binding
实例,在onCreateViewHolder
中直接通过该实例访问view
即可,如下:
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) { ViewHolder holder = new ViewHolder(RvItemTestBinding.inflate( LayoutInflater.from(parent.getContext()), parent, false )); return holder; } @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 RvItemTestBinding binding) { super(binding.getRoot()); mBinding = binding; } } }
生成的Binding
文件如下:
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)); } }
为访问根布局,ViewBinding
提供getRoot()
方法。同时该布局中每一个带id
的控件都将生成一个与之对应的字段,供调用者访问。
其核心方法是bind(View)
方法,它返回一个Binding
对象,将该布局中的所有带id
的控件通过findViewById(int)
方法构造出来并设置为字段,其它方法inflate(LayoutInflater,ViewGroup, boolean)
和inflate(LayoutInflater)
均为该方法的封装。
优点:
findViewById(int)
,使Activity
、Fragment
和RecyclerView
中的样板代码大大减少,在一个页面中控件较多的时候效果很明显。SecondActivity
中使用findViewById(int)
引用了MainActivity
的控件id
,在非运行时将难以发现,View Binding
有效解决了这个问题。缺点:
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。