前言
在Android开发,代码里获取View一般是使用findViewById()获取目标布局文件里的指定View。但是这样使用会有大量代码重复工作并且有空指针危险。为了减少重复工作有很多大神都八仙过海各显神通,但是这些神通多多少少都有缺点。
- 大名鼎鼎的黄油刀bufferknife,缺点增加了编译速度(因为原理是它需要生成一份对应查找View的代码),并且需要时刻更新最新版本否则AndroidStudio更新后可能会出现无法编译的问题。(另外bufferknife与ViewBinding是冲突的)
- DataBinding,缺点更明显,需要更多的xml编写工作量,并且一不小心会延伸到一些邪恶的用法,那就是在xml写逻辑判断,甚至在xml增加一些业务功能。这对代码维护是恐怖的,因为xml逻辑的可读性可比纯Java代码差多了。并且如果混乱到2头都写逻辑判断,维护起来十分痛苦。
- AndroidStudio的插件功能自动生成代码,比如LayoutCreator,减少了工作量但是并没有减少代码的冗余,代码看起来一样是不简洁的。
google在AndroidStudio 3.6 版本后推出了ViewBinding,一方面可以让代码更加简洁并且提高编译速度防止空指针。另一方面AndroidStudio是支持ViewBinding进行关联互动的,所以让你在Java代码与xml之间的跳转更方便。
前提条件
AndroidStudio 需要更新到3.6版本以上。
在build.gradle文件里增加下面的代码,开启viewBinding
android { //略... buildFeatures{ viewBinding = true } }
各处简单的使用Demo
首先你需要知道一个关键点,在启用ViewBinding后。每一个layout文件都会自动生成一份Java类。它会自动根据下划线进行驼峰命名。比如一个叫 activity_mian_demo.xml 的布局文件,它对应自动生成的类叫ActivityMianDemoBinding。这就意味着我们可以在任何需要导入布局的地方都使用ViewBinding。
Activity里:
public class MainActivity extends AppCompatActivity { private ActivityMianDemoBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = ActivityMianDemoBinding.inflate(getLayoutInflater()); setContentView(mBinding.getRoot()); mBinding.btn1.setText("这是按键1"); mBinding.btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { } }); } }
Fragment里:
public class FragmentDemo extends Fragment { private FragmentDemoBinding mBinding; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mBinding = FragmentDemoBinding.inflate(getLayoutInflater()); return mBinding.getRoot(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mBinding.textView.setText("textView"); } }
在Dialog里:
public class DemoDialog extends Dialog { private DialogDemoBinding mBinding; public DemoDialog(@NonNull Context context) { super(context); mBinding = DialogDemoBinding.inflate(getLayoutInflater()); setContentView(mBinding.getRoot()); mBinding.textView.setText("hello"); } }
Adapter里:
public class DemoAdapter extends RecyclerView.Adapter<DemoAdapter.ViewHolder> { @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new ViewHolder(ItemDemoBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.binding.textView2.setText("demo"); } @Override public int getItemCount() { return 0; } public static class ViewHolder extends RecyclerView.ViewHolder { ItemDemoBinding binding; public ViewHolder(@NonNull ItemDemoBinding itemDemoBinding) { super(itemDemoBinding.getRoot()); this.binding = itemDemoBinding; } } }
xml属性include
在之前其他人的博客里有说ViewBinding还不支持include属性。但是在这篇博客发布的时间,最新版本AndroidStudio4.0的已经支持使用了include属性。
使用include的布局:
<?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"> <include android:id="@+id/title" layout="@layout/title_layout" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> <Button android:id="@+id/btn_1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="A1" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/title" /> </androidx.constraintlayout.widget.ConstraintLayout>
activity里:
public class MainActivity extends AppCompatActivity { private ActivityMianDemoBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = ActivityMianDemoBinding.inflate(getLayoutInflater()); setContentView(mBinding.getRoot()); mBinding.title.titleContent.setText("内容"); mBinding.title.titleContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, "点击", Toast.LENGTH_SHORT).show(); } }); } }
假如你有需求要分离include的布局,单独使用这个include布局的类,可以参考如下实现:
public class MainActivity extends AppCompatActivity { private ActivityMianDemoBinding mBinding; private TitleLayoutBinding mTitleLayoutBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = ActivityMianDemoBinding.inflate(getLayoutInflater()); setContentView(mBinding.getRoot()); mTitleLayoutBinding = TitleLayoutBinding.bind(mBinding.getRoot()); mTitleLayoutBinding.titleContent.setText("内容11"); } }
指定布局不被ViewBinding识别
有些时候,我们希望某些布局不需要被ViewBinding识别,我只需要在这个布局的根布局上添加tools:viewBindingIgnore="true"
<?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:viewBindingIgnore="true">
ViewBInding泛型封装
public class BaseActivity<T extends ViewBinding> extends AppCompatActivity { protected T binding; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Type superclass = getClass().getGenericSuperclass(); Class<?> aClass = (Class<?>) ((ParameterizedType) superclass).getActualTypeArguments()[0]; try { Method method = aClass.getDeclaredMethod("inflate", LayoutInflater.class); binding = (T) method.invoke(null, getLayoutInflater()); setContentView(binding.getRoot()); } catch (NoSuchMethodException | IllegalAccessException| InvocationTargetException e) { e.printStackTrace(); } }
End