目录

使用前提

一、在Activity中使用

二、在Fragment中使用

三、在Adapter中使用

四、在引入布局中使用


使用前提

在使用ViewBinding前要在build.gradle工程模块中加入如下配置:

android {
    ...

    buildFeatures {
        viewBinding = true
    }

    ...
}

在加入上述配置后Android Studio会自动为每一个布局文件都生成一个对应的Binding类。

Binding类的命名规则是将布局文件按驼峰方式重命名后,再加上Binding作为结尾。

例如:

activity_main.xml布局对应的Binding类就是ActivityMainBinding

fragment_main.xml布局对应的Binding类就是FragmentMainBinding

 


一、在Activity中使用

假设在activity_main.xml的布局文件中有一个名为ButtonSubmitButton控件,现在要为这个Button控件设置监听

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //setContentView(R.layout.activity_main)

        //加载布局,并获取布局中的元素引用
        //LayoutInflater是一个用于从XML布局文件中实例化布局的类。
        //它能够将XML布局文件转换为实际的视图对象
        val binding = ActivityMainBinding.inflate(layoutInflater)
        //通过 binding.root 获取布局的根视图
        setContentView(binding.root)
        //binding.ButtonSubmit使用Button控件
        binding.ButtonSubmit.setOnClickListener{
            //添加点击事件的处理逻辑
        }
    }
}

如果要在onCreate函数之外使用控件,需要将binding声明成全局变量,如下:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.ButtonSubmit.setOnClickListener{
            //添加点击事件的处理逻辑
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        binding.ButtonSubmit.setOnClickListener{
            //添加点击事件的处理逻辑
        }
    }
}


二、在Fragment中使用

方法一:

class MainFragment : Fragment() {
    // 使用可空类型定义一个私有的_binding变量,用于存储FragmentMainBinding的实例
    private var _binding: FragmentMainBinding? = null
    // 定义一个绑定的属性访问器,它返回一个非空的FragmentMainBinding实例。
    private val binding get() = _binding!!
    // 使用FragmentMainBinding.inflate方法从给定的LayoutInflater和可选的容器中加载布局。
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        _binding = FragmentMainBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        // 首先调用super.onDestroyView(),以确保Fragment的基类也执行其销毁逻辑
        super.onDestroyView()
        // 将_binding设置为null,释放与视图绑定相关的资源
        _binding = null
    }

}

方法二:

class MainFragment : Fragment(R.layout.fragment_main) {

    private lateinit var binding: FragmentMainBinding

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // 使用ViewBinding.inflate()方法来加载布局
        binding = FragmentMainBinding.inflate(layoutInflater)
        // 将binding.root添加到Fragment的视图中
        view = binding.root

        // 现在可以使用binding对象来访问布局中的元素
        binding.exampleTextView.text = "Hello, ViewBinding!"
        // 为按钮设置点击事件
        binding.exampleButton.setOnClickListener {
            // 按钮点击事件的处理逻辑
        }
    }
}

这两种方法都是使用ViewBinding在Fragment中绑定布局

1、绑定初始化时机:

        第一种方法:在onCreateView中初始化ViewBinding。这是Fragment生命周期中创建视图的阶段,适合在视图创建时立即绑定。
        第二种方法:在onViewCreated中初始化ViewBinding。这是Fragment视图创建后,但尚未设置任何额外的视图设置之前,适合在视图创建之后进行绑定。


2、视图返回方式:

        第一种方法:直接返回binding.root作为onCreateView的结果。这意味着Fragment的根视图是通过ViewBinding生成的。
        第二种方法:在onViewCreated中将binding.root赋值给view参数。这通常发生在Fragment的视图已经通过其他方式(例如,直接使用R.layout.fragment_example)创建之后。


3、视图销毁处理:

        第一种方法:在onDestroyView中将_binding设置为null,以避免内存泄漏。这是因为Fragment可能被重用,所以需要在视图销毁时清理绑定。
        第二种方法:通常不需要在onDestroyView中进行特殊处理,因为lateinit的binding会在onDestroyView之前自动清理。


4、类型安全访问:

        第一种方法:使用_binding!!来强制访问绑定对象,这需要确保_binding在使用前已经被初始化。
        第二种方法:使用lateinit var声明binding,这确保了binding在使用前已经被初始化,并且是不可空的。


三、在Adapter中使用

我们定义fruit_item.xml来作为RecyclerView子项的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="60dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/fruitImage"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginLeft="20dp"
            android:layout_gravity="center_vertical"
            android:layout_marginTop="10dp"/>

        <TextView
            android:id="@+id/fruitName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginTop="10dp"/>
    </LinearLayout>

</LinearLayout>

编写不用ViewBindingAdapter来加载和显示这个子项布局:

package com.example.recyclerviewtest

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.myapplicationtestexample.R


class FruitAdapter(val fruitList: List<Fruit>) :
    RecyclerView.Adapter<FruitAdapter.ViewHolder>() {

    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val fruitImage: ImageView = view.findViewById(R.id.fruitImage)
        val fruitName: TextView = view.findViewById(R.id.fruitName)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
        return  ViewHolder(view)

    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val fruit = fruitList[position]
        holder.fruitImage.setImageResource(fruit.imageId)
        holder.fruitName.text = fruit.name
    }

    override fun getItemCount() = fruitList.size

}

具体细节实现可移步:

RecyclerView的实现icon-default.png?t=N7T8http://t.csdnimg.cn/5zWcA

编写使用ViewBindingAdapter来加载和显示这个子项布局:

class FruitAdapter(val fruitList: List<Fruit>) : RecyclerView.Adapter<FruitAdapter.ViewHolder>() {

    //让ViewHolder的构造函数接收FruitItemBinding这个参数
    //ViewHolder的父类RecyclerView.ViewHolder只会接收View类型的参数
    //因此需要调用binding.root获得fruit_item.xml中根元素的实例传给RecyclerView.ViewHolder
    inner class ViewHolder(binding: FruitItemBinding) : RecyclerView.ViewHolder(binding.root) {
        val fruitImage: ImageView = binding.fruitImage
        val fruitName: TextView = binding.fruitName
    }
    //调用FruitItemBinding的inflate()函数去加载fruit_item.xml布局文件
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = FruitItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }

    
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val fruit = fruitList[position]
        holder.fruitImage.setImageResource(fruit.imageId)
        holder.fruitName.text = fruit.name
    }

    override fun getItemCount() = fruitList.size

}

这段代码的核心基本都在onCreateViewHolder()函数ViewHolder函数当中。

这样就不需要再使用findViewById()函数来查找控件实例了,而是调用binding.fruitImagebinding.fruitName就可以直接引用到相应控件的实例。

四、在引入布局中使用

假设我们有如下example.xml布局,是希望作为一个通用布局引入到各布局当中的:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/exampleTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Example"
        android:textSize="20sp" />

</RelativeLayout>

假设需要在 activity_main.xml 布局中引入该布局,我们这样引入:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    ...
    <include 
        android:id="@+id/example"
        layout="@layout/example" />
    ...

</LinearLayout>

然后可以直接在MainActivity文件中使用example.xml布局的控件

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.example.exampleTextView.text = "Title"
       
    }

}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐