视图绑定和数据绑定均会生成可用于直接引用视图的绑定类。但是,视图绑定旨在处理更简单的用例,与数据绑定相比,具有以下优势:
Attention:
This tool is now deprecated. Please switch to view binding. Existing versions will continue to work,
obviously, but only critical bug fixes for integration with AGP will be considered.
Feature development and general bug fixes have stopped.
// 该工具已经废弃,请切换至ViewBinding。现有版本继续工作,只会修复严重bug,其他随意了。
通过视图绑定功能,您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。
绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。
设置说明
- 注意:视图绑定在 Android Studio 3.6 Canary 11 及更高版本中可用。记得升级垃圾软件哦。
- 目前我们的项目一般都是分模块的,视图绑定功能也支持按模块启用,想在哪个模块启用就直接在该模块的build.gradle
文件中添加以下代码。
android {
// android块下添加
viewBinding {
enabled = true
}
}
<LinearLayout
tools:viewBindingIgnore="true" >
...
</LinearLayout>
用法
为某个模块启用视图绑定功能后,系统会为该模块中包含的每个 XML 布局文件生成一个绑定类。每个绑定类均包含对根视图以及具有 ID 的所有视图的引用。
系统会通过以下方式生成绑定类的名称:将 XML 文件的名称转换为驼峰式大小写,并在末尾添加“Binding”一词。
例如,假设某个布局文件的名称为 result_profile.xml:
<LinearLayout >
<TextView android:id="@+id/name" />
<ImageView android:cropToPadding="true" />
<Button android:id="@+id/button"
android:background="@drawable/rounded_button" />
</LinearLayout>
所生成的绑定类的名称就为 ResultProfileBinding。此类具有两个字段:一个是名为 name 的 TextView,另一个是名为 button 的 Button。
该布局中的 ImageView 没有 ID,因此绑定类中不存在对它的引用。
每个绑定类还包含一个 getRoot() 方法,用于为相应布局文件的根视图提供直接引用。
在此示例中,ResultProfileBinding 类中的 getRoot() 方法会返回 LinearLayout 根视图。
以下几个部分介绍了生成的绑定类在 Activity 和 Fragment 中的使用。
在 Activity 中使用视图绑定
如需设置绑定类的实例以供 Activity 使用,请在 Activity 的 onCreate() 方法中执行以下步骤:
- 调用生成的绑定类中包含的静态 inflate() 方法。此操作会创建该绑定类的实例以供 Activity 使用。
- 通过调用 getRoot() 方法或使用 Kotlin 属性语法获取对根视图的引用。
- 将根视图传递到 setContentView(),使其成为屏幕上的活动视图。
Java
class ResultProfileActivity extends AppCompatActivity {
private ResultProfileBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ResultProfileBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
}
}
Kotlin
class ResultProfileActivity : AppCompatActivity() {
private lateinit var binding: ResultProfileBinding
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = ResultProfileBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
}
}
您现在即可使用该绑定类的实例来引用任何视图:
binding.getName().setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
viewModel.userClicked()
});
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }
在 Fragment 中使用视图绑定
如需设置绑定类的实例以供 Fragment 使用,请在 Fragment 的 onCreateView() 方法中执行以下步骤:
- 调用生成的绑定类中包含的静态 inflate() 方法。此操作会创建该绑定类的实例以供 Fragment 使用。
- 通过调用 getRoot() 方法或使用 Kotlin 属性语法获取对根视图的引用。
- 从 onCreateView() 方法返回根视图,使其成为屏幕上的活动视图。
Java
class ResultProfileFragment extends Fragment {
private ResultProfileBinding binding;
@Override
public View onCreateView (LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
binding = ResultProfileBinding.inflate(inflater, container, false);
View view = binding.getRoot();
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
Kotlin
class ResultProfileFragment : Fragment() {
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
您现在即可使用该绑定类的实例来引用任何视图:
binding.getName().setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
viewModel.userClicked()
});
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }
不要着急,你要的封装在下面。
思考一下我们能不能直接在BaseXXX中将binding给初始化出来,然后在对应的子界面可以直接使用,安排。
因为项目中使用的是kotlin,懒得转换成java了,参考kotlin的实现,java手到擒来,如果没调通可以留言,后续更新。
针对BaseActivity
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
protected lateinit var binding: VB
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val type = javaClass.genericSuperclass
if (type is ParameterizedType) {
val clazz = type.actualTypeArguments[0] as Class<VB>
val method = clazz.getMethod("inflate", LayoutInflater::class.java)
binding = method.invoke(null, layoutInflater) as VB
setContentView(binding.root)
}
onCreated(savedInstanceState)
}
abstract fun onCreated(savedInstanceState: Bundle?)
}
那么在具体的Activity中可以直接使用:
class MainActivity : FullActivity<ActivityMainBinding>() {
override fun onCreated(savedInstanceState: Bundle?) {
// xxx就是你在activity_main布局中定义的属性名
binding.xxx.text = "123"
}
}
针对BaseFragment
abstract class BaseFragment<out VB : ViewBinding> : Fragment {
private var _binding: VB? = null
val binding: VB get() = _binding!!
constructor() : super()
@ContentView
constructor(@LayoutRes contentLayoutId: Int) : super(contentLayoutId)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//利用反射,调用指定ViewBinding中的inflate方法填充视图
val type = javaClass.genericSuperclass
val clazz = (type as ParameterizedType).actualTypeArguments[0] as Class<VB>
val method = clazz.getMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.java
)
_binding = method.invoke(null, layoutInflater, container, false) as VB
return _binding!!.root
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
}
针对BaseAdapter
abstract class BaseRecyclerviewAdapter<VB : ViewBinding, M> :
RecyclerView.Adapter<BaseRecyclerviewAdapter.ViewHolder>() {
protected var data: List<M>? = null
protected lateinit var binding: VB
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val type = javaClass.genericSuperclass
val clazz = (type as ParameterizedType).actualTypeArguments[0] as Class<VB>
val method = clazz.getMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.java
)
binding = method.invoke(null, LayoutInflater.from(parent.context), parent, false) as VB
return ViewHolder(binding.root)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.setIsRecyclable(false)
bindData(binding, data!![position],position)
}
override fun getItemCount(): Int {
return data?.size ?: 0
}
fun setMoreData(newData: List<M>) {
data = newData
notifyDataSetChanged()
}
abstract fun bindData(binding: VB, item: M,position: Int)
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}
针对BaseViewGroup
abstract class BaseViewGroup<out VB : ViewBinding>(
context: Context,
attr: AttributeSet? = null,
def: Int = 0
) : FrameLayout(context, attr, def) {
protected val binding: VB by lazy {
val type = javaClass.genericSuperclass
val clazz = (type as ParameterizedType).actualTypeArguments[0] as Class<VB>
val method = clazz.getMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.java
)
method.invoke(null, LayoutInflater.from(context), this, true) as VB
}
init {
binding.root
}
}
愉快的时光总是这么短暂,拜拜了各位。有需要请移步公号"程序员指北",虽然我也不经常写,但不影响你关注哈哈。