官网文档
通过视图绑定功能,您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。
对比findViewById
findViewById编写过于冗余。类型不安全,findViewById可能返回null,类型转换可能错误。对比ButterKnife
官宣不维护,推荐使用ViewBinding。类型仍然不安全。对比Kotlin Android Extensions
JetBrains废弃该插件。性能偏低。 配置说明Android Studio3.6以上
android {
viewBinding {
enabled = true
}
}
Android Studio4.0以上
android {
buildFeatures {
viewBinding = true
}
}
如果需要忽略某个布局文件,需要添加tools:viewBindingIgnore="true"
属性到布局中
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
LinearLayout>
基本使用
当开启ViewBinding后,系统会为该模块中每个XML布局文件生成一个绑定类(转换为驼峰命名并在末尾添加Binding),每个绑定类均包含根视图已交具有id的所有视图的引用。
例如:布局文件名为activity_main.xml
,生成绑定类为ActivityMainBinding
XML布局:activity_main.xml
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/ivAvatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
LinearLayout>
在Activity中使用
public class MainActivity extends AppCompatActivity {
private Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.tvName.setText("Hello ViewBinding");
binding.ivAvatar.setImageResource(R.mipmap.ic_launcher);
binding.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "hello world", Toast.LENGTH_SHORT).show();
}
});
}
}
Fragment中使用
Fragment 的存在时间比其视图长。需要在 Fragment 的 onDestroyView()
方法中清除对绑定类实例的所有引用。
方式一
public class MyFragment extends Fragment {
private FragmentMyBinding binding;
private Context context;
public MyFragment() {
}
public static MyFragment newInstance() {
MyFragment fragment = new MyFragment();
return fragment;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.context = context;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//方式一
binding = FragmentMyBinding.inflate(getLayoutInflater(), container, false);
return binding.getRoot();
//方式二
// return inflater.inflate(R.layout.fragment_my, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//方式一
binding.tvName.setText("Hello ViewBinding");
binding.ivAvatar.setImageResource(R.mipmap.ic_launcher);
binding.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "hello world", Toast.LENGTH_SHORT).show();
}
});
//方式二
// FragmentMyBinding bind = FragmentMyBinding.bind(view);
// bind.tvName.setText("Hello ViewBinding");
// bind.ivAvatar.setImageResource(R.mipmap.ic_launcher);
// bind.btn.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// Toast.makeText(context, "hello world", Toast.LENGTH_SHORT).show();
// }
// });
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
方式二
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
FragmentMyBinding bind = FragmentMyBinding.bind(view);
bind.tvName.setText("Hello ViewBinding");
bind.ivAvatar.setImageResource(R.mipmap.ic_launcher);
bind.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "hello world", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
RecyclerView adapter中使用
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private Context mContext;
private ArrayList<String> mData;
private final LayoutInflater inflater;
public MyAdapter(Context context, ArrayList<String> data) {
mContext = context;
mData = data;
inflater = LayoutInflater.from(context);
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemLayoutBinding itemBinding = ItemLayoutBinding.inflate(inflater, parent, false);
return new ViewHolder(itemBinding);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.textView.setText(mData.get(position));
}
@Override
public int getItemCount() {
return mData.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public ViewHolder(@NonNull ItemLayoutBinding itemBinding) {
super(itemBinding.getRoot());
textView = itemBinding.textView;
}
}
}
include标签中使用
ViewBinding可以与
标签一起使用
标签定义id,使用该id访问布局中的控件。
布局:titlebar.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#2196F3"
android:minHeight="50dp"
android:padding="10dp">
<TextView
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="返回" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="标题位置" />
<TextView
android:id="@+id/confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="确定" />
RelativeLayout>
Activity布局:activity_include.xml
<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:context=".IncludeActivity">
<include
android:id="@+id/titleBar"
layout="@layout/titlebar" />
LinearLayout>
在include标签中使用
public class IncludeActivity extends AppCompatActivity {
private Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
ActivityIncludeBinding binding = ActivityIncludeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.titleBar.title.setText("这是一个标题");
binding.titleBar.back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "返回", Toast.LENGTH_SHORT).show();
}
});
binding.titleBar.confirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "确定", Toast.LENGTH_SHORT).show();
}
});
}
}
include包含merge标签
标签有利于减少布局层次。需要使用bind()
方法绑定根视图。不能给
标签设置id。
布局:detail_layout.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="@+id/ivDetail"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitXY" />
merge>
Activity布局:activity_include.xml
<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=".IncludeActivity">
<include layout="@layout/detail_layout" />
LinearLayout>
在include标签中使用
package com.example.viewbindingdemo;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.viewbindingdemo.databinding.ActivityIncludeBinding;
import com.example.viewbindingdemo.databinding.DetailLayoutBinding;
public class IncludeActivity extends AppCompatActivity {
private Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
ActivityIncludeBinding binding = ActivityIncludeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
DetailLayoutBinding detailBinding = DetailLayoutBinding.bind(binding.getRoot());
detailBinding.ivDetail.setImageResource(R.mipmap.ic_launcher);
}
}
基类封装使用
BaseActivity
abstract class BaseBindingActivity<VB : ViewBinding> : AppCompatActivity() {
protected lateinit var mBinding: VB
protected lateinit var mContext: Context
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mContext = this
val type = javaClass.genericSuperclass
if (type is ParameterizedType) {
val clz = type.actualTypeArguments[0] as Class<VB>
val method = clz.getMethod("inflate", LayoutInflater::class.java)
mBinding = method.invoke(null, layoutInflater) as VB
setContentView(mBinding.root)
}
initView(savedInstanceState)
}
protected abstract fun initView(savedInstanceState: Bundle?)
}
使用:
class FirstActivity : BaseBindingActivity<ActivityFirstBinding>() {
private lateinit var detailLayoutBinding: DetailLayoutBinding
override fun initView(savedInstanceState: Bundle?) {
mBinding.textView.text = "Hello World"
mBinding.titleBar.title.text = "这是一个标题"
mBinding.titleBar.back.setOnClickListener {
Toast.makeText(
this@FirstActivity,
"返回",
Toast.LENGTH_SHORT
).show()
}
mBinding.titleBar.confirm.setOnClickListener {
Toast.makeText(
this@FirstActivity,
"确定",
Toast.LENGTH_SHORT
).show()
}
detailLayoutBinding = DetailLayoutBinding.bind(mBinding.root)
detailLayoutBinding.ivDetail.setImageResource(R.mipmap.ic_launcher)
}
}
BaseFragment
abstract class BaseBindingFragment<VB : ViewBinding> : Fragment() {
protected var mBinding: VB? = null
protected lateinit var mContext: Context
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val type = javaClass.genericSuperclass
if (type is ParameterizedType) {
val clz = type.actualTypeArguments[0] as Class<VB>
val method = clz.getMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.javaPrimitiveType
)
mBinding = method.invoke(null, inflater, container, false) as VB
}
return mBinding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView(savedInstanceState)
}
abstract fun initView(savedInstanceState: Bundle?)
override fun onDestroyView() {
super.onDestroyView()
mBinding = null
}
}
使用:
class FirstFragment : BaseBindingFragment<FragmentFirstBinding>() {
override fun initView(savedInstanceState: Bundle?) {
mBinding!!.textView.text = "Hello Fragment"
mBinding!!.titleBar.title.text = "这是一个标题2"
mBinding!!.titleBar.back.setOnClickListener {
Toast.makeText(
mContext,
"返回",
Toast.LENGTH_SHORT
).show()
}
mBinding!!.titleBar.confirm.setOnClickListener {
Toast.makeText(
mContext,
"确定",
Toast.LENGTH_SHORT
).show()
}
val detailLayoutBinding = DetailLayoutBinding.bind(mBinding!!.root)
detailLayoutBinding.ivDetail.setImageResource(R.mipmap.ic_launcher)
}
}
ViewBinding+委托实现
可以使用第三方库实现,具体逻辑可以参考其代码
依赖库
implementation 'com.hi-dhl:jdatabinding:1.0.4'
class SecondActivity : AppCompatActivity() {
private val mBinding: ActivitySecondBinding by binding()
private lateinit var detailLayoutBinding: DetailLayoutBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding.textView.text = "Hello World"
mBinding.titleBar.title.text = "这是一个标题"
mBinding.titleBar.back.setOnClickListener {
toast("返回")
}
mBinding.titleBar.confirm.setOnClickListener {
toast("确定")
}
detailLayoutBinding = DetailLayoutBinding.bind(mBinding.root)
detailLayoutBinding.ivDetail.setImageResource(R.mipmap.ic_launcher)
}
}
class SecondFragment : DataBindingFragment(R.layout.fragment_second) {
private val mBinding: FragmentSecondBinding by binding()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mBinding.textView.text = "Hello Fragment"
mBinding.titleBar.title.text = "这是一个标题2"
mBinding.titleBar.back.setOnClickListener {
toast("返回")
}
mBinding.titleBar.confirm.setOnClickListener {
toast("确定")
}
val detailLayoutBinding = DetailLayoutBinding.bind(mBinding.root)
detailLayoutBinding.ivDetail.setImageResource(R.mipmap.ic_launcher)
}
}
代码下载
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)