赞
踩
要求:Android Studio必须是3.6 Canary 11以及更高的版本才能使用
作用:View Binding是一项功能,使您可以更轻松地编写与视图交互的代码。在模块中启用视图绑定后,它将为该模块中存在的每个XML布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有ID的所有视图的直接引用。
像ButterKnife、Android Kotlin extensions一样都是为了简化findViewById
Jake Wharton 也在 Butter Knife开源库中添加了如下一句话:
Attention: Development on this tool is winding down. Please consider switching to view binding in the coming months.
想必 ViewBinding 在未来的地位和作用将不言而喻了吧。
// Android Studio 3.6.0
android {
...
viewBinding {
enabled = true
}
}
在 Android Studio 4.0 中,viewBinding 将被变成属性整合到了buildFeatures 选项中,配置要改成:
// Android Studio 4.0
android {
buildFeatures {
viewBinding = true
}
}
如果你的布局文件是activity_main.xml,则会生成一个ActivityMainBinding的类,如果你的布局文件是result_profile.xml,则会生成一个ResultProfileBinding的类,以此类推。
下面以activity_main.xml及其对应的MainActivity.java为例说明:
假设activity_main.xml放置了三个控件:TextView(Id为text)、Button(Id为button)、ImageView(没有设置Id),其中ImageView因为没有设置Id,因此绑定类中不存在对它的引用,所以在代码中无法被引用。
MainActivity.java的部分代码如下:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); LayoutInflater layoutInflater = LayoutInflater.from(this); ActivityMainBinding binding = ActivityMainBinding.inflate(layoutInflater); setContentView(binding.getRoot()); binding.text.setText("文字已变化"); binding.button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "Button被点击", Toast.LENGTH_SHORT).show(); } }); }
在获取布局的时候,如下这样写,更是官方文档中推荐的
//使用ViewBinding后的方法
mBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
注意事项
注意:原先的setContentView(R.layout.activity_main)需要注释掉,否则会重复设置ContentView。
布局的根视图(activity_main.xml)会自动生成一个名为 rootView 的成员变量。在 Activity 的 onCreate()方法中,要将 rootView传入 setContentView()方法,从而让 Activity 可以使用绑定对象中的布局,rootView是私有变量,需要使用getRoot()方法拿到。
public class BaseFragment extends Fragment { private Activity mActivity; private FrgamentBaseBinding mBind; private TextView mTv; @Override public void onAttach(Context context) { super.onAttach(context); mActivity = (Activity) context; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mBind = FrgamentBaseBinding.inflate(inflater, container, false); View root = mBind.getRoot(); //View root = inflater.inflate(R.layout.frgament_base, container, false); return root; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mTv = mBind.frgamentTv; mBind.frgamentBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(mActivity, "hello i am fragment btn", Toast.LENGTH_SHORT).show(); mTv.setText("click fragment btn~~~"); } }); mBind.frgamentTv.setText("hello i am fragment tv"); } @Override public void onDestroyView() { super.onDestroyView(); mBind = null; } }
注意:Fragment 的存在时间比其视图长。请务必在 Fragment 的 onDestroyView()
方法中清除对绑定类实例的所有引用。
include填充的布局如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container_include"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_include"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="这就是测试啊" />
</RelativeLayout>
activity_test_include.xml引用这个布局
<?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" android:orientation="vertical" tools:context=".TestIncludeActivity"> <TextView android:id="@+id/include_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="测试include" /> <!-- 1.测试include --> <include android:id="@+id/include_include" layout="@layout/include_item" /> </LinearLayout>
Java代码
public class TestIncludeActivity extends AppCompatActivity { private ActivityTestIncludeBinding mBind; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBind = ActivityTestIncludeBinding.inflate(getLayoutInflater()); setContentView(mBind.getRoot()); mBind.includeTv.setText("呵呵,为了测试include"); //1.不给这个布局中的include增加id,include的xml文件中增加id。答案是获取不到 填充部分的view //2.给这个布局中的include增加id,include的xml文件中增加id。答案是没问题的 mBind.includeInclude.containerInclude.setBackgroundColor(Color.RED); mBind.includeInclude.tvInclude.setText("hello"); //3.给这个布局中的include增加id,不给include的xml文件中的根布局增加id。答案是没问题的 } }
include:如果填充的布局不是merge,那么include的id必须要加,否则就不能获取到填充布局中的控件id。
控件中的根布局id也是可以加的,能获取到。可以通过id设置背景颜色等
include填充的布局如下
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container_include"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_include"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="这就是测试啊" />
</merge>
activity_test_include.xml引用这个布局
<?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" android:orientation="vertical" tools:context=".TestIncludeActivity"> <TextView android:id="@+id/include_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="测试include" /> <!-- 2.测试merge和include,这里一定不能有id --> <include layout="@layout/include_item" /> </LinearLayout>
Java代码
public class TestIncludeActivity extends AppCompatActivity { private ActivityTestIncludeBinding mBind; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBind = ActivityTestIncludeBinding.inflate(getLayoutInflater()); setContentView(mBind.getRoot()); mBind.includeTv.setText("呵呵,为了测试include"); //4.将include的xml文件的根布局改成merge,测试下有没有问题。这里是RelativeLayout改成Merge IncludeItemBinding binding = IncludeItemBinding.bind(mBind.getRoot()); binding.tvInclude.setText("这就不会出现问题了吧"); //无法获取到根布局。。。 } }
merge:merge xml文件中的根布局id可以加,但是获取不到,没有什么意义!
如果include中引用merge,但是有id。会报如下异常
java.lang.NullPointerException: Missing required view with ID: com.example.viewbinding:id/i1
如果想要获取到Merge中的控件,只能通过IncludeItemBinding.bind(mBind.getRoot()); 然后在操作想要的控件
创建简单的自定义Dialog
public class TestCusDialogActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_cus_dialog);
}
public void onClick(View view) {
MyDialog dialog = new MyDialog(this, R.style.AppTheme);
dialog.show();
}
}
Java代码中使用,我在activity布局中忽略了view bind。如果不想用view bind的布局一定记得要忽略!!!
public class TestCusDialogActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_cus_dialog);
}
public void onClick(View view) {
MyDialog dialog = new MyDialog(this, R.style.AppTheme);
dialog.show();
}
}
cus_view_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="这是自定义布局"
android:textSize="50sp" />
</androidx.constraintlayout.widget.ConstraintLayout>
创建的自定义LinearLayout
/*** * 创建时间:2020/8/29 20:55 * 创建人:10850 * 功能描述:自定义view * 1.使用的layout文件不包含merge * init1、2、3、4是使用inflate来导入layout布局的写法,全部可以正常显示自定义的布局。 * init10、11、12是使用ViewBinding的写法,10无法正常显示视图,11和12是两种不同的写法,道理一样。 */ public class MyLinearLayout extends LinearLayout { public MyLinearLayout(Context context) { this(context, null); } public MyLinearLayout(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // init1(); // init2(); // init3(); // init4(); //11、12都是可以的 //10的位置就偏离中心了 init12(); } private void init1() { inflate(getContext(), R.layout.cus_view_layout, this); } private void init2() { View view = LayoutInflater.from(getContext()).inflate(R.layout.cus_view_layout, this); } //和init2()方法相等 private void init3() { View view = LayoutInflater.from(getContext()).inflate(R.layout.cus_view_layout, this, true); } private void init4() { View view = LayoutInflater.from(getContext()).inflate(R.layout.cus_view_layout, this, false); addView(view); } //视图异常,布局无法填充满 private void init10() { CusViewLayoutBinding binding = CusViewLayoutBinding.inflate(LayoutInflater.from(getContext())); addView(binding.getRoot()); } private void init11() { CusViewLayoutBinding binding = CusViewLayoutBinding.inflate(LayoutInflater.from(getContext()), this, true); } private void init12() { CusViewLayoutBinding binding = CusViewLayoutBinding.inflate(LayoutInflater.from(getContext()), this, false); addView(binding.getRoot()); } }
cus_view_merge.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="这是自定义merge"
android:textSize="50sp" />
</merge>
创建的自定义LinearLayout
/*** * 创建时间:2020/8/29 20:55 * 创建人:10850 * 功能描述:自定义view * 1.使用的layout文件不包含merge * init1、2、3、4是使用inflate来导入layout布局的写法,全部可以正常显示自定义的布局。 * init10、11、12是使用ViewBinding的写法,10无法正常显示视图,11和12是两种不同的写法,道理一样。 * 2.使用的layout文件根标签为merge * */ public class MegeLinearLayout extends LinearLayout { public MegeLinearLayout(Context context) { this(context, null); } public MegeLinearLayout(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MegeLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // init1(); // init2(); // init3(); // init4(); //21没效果 20是正常的 init20(); } private void init1() { inflate(getContext(), R.layout.cus_view_merge, this); } private void init2() { View view = LayoutInflater.from(getContext()).inflate(R.layout.cus_view_merge, this); } //和init2()方法相等 private void init3() { View view = LayoutInflater.from(getContext()).inflate(R.layout.cus_view_merge, this, true); } private void init4() { View view = LayoutInflater.from(getContext()).inflate(R.layout.cus_view_merge, this, false); addView(view); } private void init20() { CusViewMergeBinding binding = CusViewMergeBinding.inflate(LayoutInflater.from(getContext()), this); } //没有效果,可以理解为还没有rootView private void init21() { CusViewMergeBinding binding = CusViewMergeBinding.bind(this); } }
activity_test_cus_view.xml
<?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" android:orientation="vertical" tools:context=".TestCusViewActivity"> <com.example.viewbinding.MyLinearLayout android:background="@color/colorPrimary" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <com.example.viewbinding.MegeLinearLayout android:background="@color/colorAccent" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout>
TestRvActivity.class
public class TestRvActivity extends AppCompatActivity { private RecyclerView mRv; private MyAdapter mAdapter;//适配器 private List<String> list;//数据集合 private LinearLayoutManager mLayout;//布局管理器 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityTestRvBinding binding = ActivityTestRvBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); mRv = binding.rv; initData();//初始化数据 mAdapter = new MyAdapter(this, list); //设置适配器 mRv.setAdapter(mAdapter); //布局有3种 LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager mLayout = new LinearLayoutManager(this); mRv.setLayoutManager(mLayout); //设置Item增加、移除动画 mRv.setItemAnimator(new DefaultItemAnimator()); //添加默认分割线 mRv.addItemDecoration(new DividerItemDecoration( this, DividerItemDecoration.VERTICAL)); //点击和长按事件 mAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(TestRvActivity.this, ((TextView) view).getText() + " click", Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(TestRvActivity.this, position + " Long click", Toast.LENGTH_SHORT).show(); mAdapter.removeData(position); mAdapter.notifyDataSetChanged(); } }); } private void initData() { list = new ArrayList<>(); for (int i = 0; i <= 200; i++) { list.add("Item " + i); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_item, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { //增加:在第二个位置增加一个item //删除:在第二个位置删掉一个item case R.id.add: mAdapter.addData(1); mAdapter.notifyDataSetChanged(); break; case R.id.delete: mAdapter.removeData(1); mAdapter.notifyDataSetChanged(); break; } return true; } }
其布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".TestRvActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/rv" />
</RelativeLayout>
MyAdapter.class
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { //数据源 private List<String> mList; private Context mContext; private RvItemBinding mBinding; public interface OnItemClickListener { void onItemClick(View view, int position); void onItemLongClick(View view, int position); } private OnItemClickListener mOnItemClickListener; public void setOnItemClickListener(OnItemClickListener onItemClickListener) { mOnItemClickListener = onItemClickListener; } //构造方法 public MyAdapter(Context context, List<String> list) { this.mList = list; this.mContext = context; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { //View inflate = LayoutInflater.from(mContext).inflate(R.layout.rv_item, viewGroup, false); //仿照原来的方式,否则不充满布局 mBinding = RvItemBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false); //这样不行,不能充满布局。不推荐 //mBinding = RvItemBinding.inflate(LayoutInflater.from(mContext)); return new ViewHolder(mBinding); } @Override public void onBindViewHolder(@NonNull final ViewHolder viewHolder, int i) { viewHolder.mTextView.setText(mList.get(i)); // ViewGroup.LayoutParams layoutParams = viewHolder.mTextView.getLayoutParams(); // layoutParams.height = new Random().nextInt(200) + 200; // viewHolder.mTextView.setLayoutParams(layoutParams); //点击事件 if (mOnItemClickListener != null) { viewHolder.mTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int pos = viewHolder.getLayoutPosition(); mOnItemClickListener.onItemClick(viewHolder.mTextView, pos); } }); viewHolder.mTextView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { int pos = viewHolder.getLayoutPosition(); mOnItemClickListener.onItemLongClick(viewHolder.mTextView, pos); return false; } }); } } @Override public int getItemCount() { return mList == null ? 0 : mList.size(); } public class ViewHolder extends RecyclerView.ViewHolder { private TextView mTextView; private FrameLayout mFrameLayout; //1.这里如果使用之前的方式,onCreateViewHolder方法,返回的是new ViewHolder(mBinding.getRoot());即可 /*public ViewHolder(@NonNull View itemView) { super(itemView); mTextView = mBinding.rvTextView; mFrameLayout = mBinding.rvContainer; }*/ //2.使用如下方式,onCreateViewHolder方法,返回的是new ViewHolder(mBinding);即可 public ViewHolder(@NonNull RvItemBinding binding) { super(binding.getRoot()); mTextView = binding.rvTextView; mFrameLayout = binding.rvContainer; } } //添加item public void addData(int position) { mList.add(position, "Insert One"); notifyItemInserted(position); } //删除item public void removeData(int position) { mList.remove(position); notifyItemRemoved(position); } }
其item布局
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:id="@+id/rv_container" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/rv_text_view" android:layout_width="match_parent" android:layout_height="120dp" android:background="@color/colorAccent" android:gravity="center" android:text="1" android:textSize="20sp" /> </FrameLayout>
public class TestRvActivity extends AppCompatActivity { private RecyclerView mRv; private MyAdapter mAdapter;//适配器 private List<String> list;//数据集合 private LinearLayoutManager mLayout;//布局管理器 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityTestRvBinding binding = ActivityTestRvBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); mRv = binding.rv; initData();//初始化数据 AAAdapter aaAdapter = new AAAdapter(R.layout.rv_item, list); mRv.setAdapter(aaAdapter); mLayout = new LinearLayoutManager(this); mRv.setLayoutManager(mLayout); } private void initData() { list = new ArrayList<>(); for (int i = 0; i <= 200; i++) { list.add("Item " + i); } } }
AAAdapter
public class AAAdapter extends BaseQuickAdapter<String, AAA> { public AAAdapter(int layoutResId, @Nullable List<String> data) { super(layoutResId, data); } public AAAdapter(@Nullable List<String> data) { super(data); } @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void convert(@NonNull AAA aaa, String s) { aaa.mRvContainer.setBackgroundColor(mContext.getColor(R.color.colorPrimary)); aaa.mTextView.setText(s + "~"); } }
AAA
public class AAA extends BaseViewHolder {
public FrameLayout mRvContainer;
public TextView mTextView;
public AAA(View view) {
super(view);
RvItemBinding rvItemBinding = RvItemBinding.bind(view);
mRvContainer = rvItemBinding.rvContainer;
mTextView = rvItemBinding.rvTextView;
}
}
这个看自己的喜好,愿意这么用就这么用,不愿意就算了
如果您希望在生成绑定类时忽略某个布局文件,请将 tools:viewBindingIgnore="true"
属性添加到相应布局文件的根视图中:
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>
使用View bind的时候,需要注意布局的名称。因为生成的Bind类是根据布局来的,这里一定要注意!注意!注意!
上面这种情况,有两种解决方式
public class TestFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater layoutInflater = LayoutInflater.from(this);
ActivityTestFragmentBinding inflate = ActivityTestFragmentBinding.inflate(layoutInflater);
setContentView(inflate.getRoot());
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:viewBindingIgnore="true">
<fragment
android:id="@+id/fragment1"
android:name="com.zg.jetpackdemo.BaseFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
与使用 findViewById
相比,视图绑定具有一些很显著的优点:
@Nullable
标记。这些差异意味着布局和代码之间的不兼容将会导致构建在编译时(而非运行时)失败。
视图绑定和数据绑定均会生成可用于直接引用视图的绑定类。但是,视图绑定旨在处理更简单的用例,与数据绑定相比,具有以下优势:
反过来,与数据绑定相比,视图绑定也具有以下限制:
考虑到这些因素,在某些情况下,最好在项目中同时使用视图绑定和数据绑定。您可以在需要高级功能的布局中使用数据绑定,而在不需要高级功能的布局中使用视图绑定。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SeNXGFT3-1598758462438)(E:\学习截图\新建文件夹\view_binds.png)]
layout
文件名去掉下划线,下划线首字母大写,最后加上Binding
。例如我有一个layout
文件叫activity_main.xml
,那对应生成的类文件为ActivityMainBinding.java
。build\generated\data_binding_base_class_source_out\debug\out\包名\databinding
下Android Studio3.6新特性之视图绑定ViewBinding使用指南
秒懂Android开发之ViewBinding,一代神器ButterKnife的终结者
上面的这些链接可以看看。
如果有问题,欢迎指出!谢谢~~~
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。