【Jetpack】学穿:ViewBinding → 视图绑定(中)

简介: 手机厂商还没卷完Android 12,Android 13 就悄然声息地来了,距离Google 2008年9月22日发布Android 1.0,已过去13个年头。

0x3、ViewBinding基本用法


ViewBinding 的作用:代替findViewById,还可以保证空安全和类型安全,支持Java。


:使用ViewBinding,AGP版本需 >= 3.6


接着介绍下基本用法,部分内容搬运自官方文档:《视图绑定》


① 启用ViewBinding


需要启用视图绑定的 Module,在其 build.gradle 添加下述配置:


android {
    ...
    viewBinding {
        enabled = true
    }
}


不需要生成绑定类的布局XML文件,可在根节点中添加下述属性:


<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>


编译后,AGP会为Module中包含的XML布局文件生成一个绑定类,类名规则:


XML文件名转换为Pascal大小写,并加上Binding,比如:result_profile.xml → ResultProfileBinding。


② 三个类绑定API


// View已存在
fun <T> bind(view : View) : T
// View未存在
fun <T> inflate(inflater : LayoutInflater) : T
fun <T> inflate(inflater : LayoutInflater, parent : ViewGroup?, attachToParent : Boolean) : T


接下来演示一波各种场景下的ViewBinding用法,其实都是围绕上述三个API进行的~


③ Activity


class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 1、实例化绑定实例
        binding = ActivityMainBinding.inflate(layoutInflater)
        // 2、获得对根视图的引用
        val view = binding.root
        // 3、让根视图称为屏幕上的活动视图
        setContentView(view)
        // 4、引用视图控件
        binding.tvContent.text = "修改TextView文本"
    }
}


④ Fragment


class ContentFragment: Fragment() {
    private var _binding: FragmentContentBinding? = null
    private val binding get() = _binding!!
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentContentBinding.inflate(inflater, container, false)
        return binding.root
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.ivLogo.visibility = View.GONE
    }
    override fun onDestroyView() {
        super.onDestroyView()
        // Fragment的存活时间比View长,务必在此方法中清除对绑定类实例的所有引用
        // 否则会引发内存泄露
        _binding = null
    }
}


网络异常,图片无法展示
|


如果布局已inflated,还可以采用另一种写法(调bind):


class TestFragment: Fragment(R.layout.fragment_content) {
    private var _binding: FragmentContentBinding? = null
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val binding = FragmentContentBinding.bind(view)
        _binding = binding
        binding.ivLogo.visibility = View.VISIBLE
    }
    override fun onDestroyView() {
        super.onDestroyView()
        // 同样需要置空
        _binding = null
    }
}


⑤ Dialog


如果是继承DialogFragment写法同Fragment,如果是继承Dialog写法示例如下(PopupWindow类似)~


class TestDialog(context: Context) : Dialog(context) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DialogTestBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.tvTitle.text = "对话框标题"
    }
}


⑥ RecyclerView


class TestAdapter(list: List<String>) : RecyclerView.Adapter<TestAdapter.ViewHolder>() {
    private var mList: List<String> = list
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        // 需在此初始化以获得父类容器
        val binding = ItemTestBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.tvItem.text = "Adapter"
    }
    override fun getItemCount() = mList.size
    // 传递Binding对象
    class ViewHolder(binding: ItemTestBinding) : RecyclerView.ViewHolder(binding.root) {
        var tvItem: TextView = binding.tvItem
    }
}


⑦ 自定义ViewGroup


ViewGroup子类才能使用视图绑定,View子类不可使用,示例如下:


class TestLayout: LinearLayout {
    constructor(context: Context): super(context)
    constructor(context: Context, attrs: AttributeSet): super(context, attrs) {
        val inflater = LayoutInflater.from(this.context)
        val binding = ItemLayoutBinding.inflate(inflater, this, true)
        binding.tvLayout.text = "自定义ViewGroup"
    }
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
    }
}


⑧ include


根据include的布局xml是否带**<merge>标签**,分为两种,先是不带的情况:

include的xml文件名为sub_include_test.xml,id为include_layout:


网络异常,图片无法展示
|


然后是带的情况,布局文件改下:


网络异常,图片无法展示
|


使用部分的代码不变,运行奔溃报错信息如下:


网络异常,图片无法展示
|


原因是merge并不会加载到布局里,解法:把include标签的id去掉,然后bind传入父布局~


网络异常,图片无法展示
|


⑨ ViewStub


网络异常,图片无法展示
|


基础用法很简单,也很好上手,但存在下述问题:


需重复编写:创建和回收ViewBinding实例的样板代码,特别是Fragment,还要手动置空。


所以有必要封装优化一波~


0x4、封装优化思路


① 泛型 + 父类实现模板代码


最容易想到的常规写法,配合泛型,把模板代码都在父类中写好,非常简单:


abstract class BaseFragment<T : ViewBinding>(layoutId: Int) : Fragment(layoutId) {
    private var _binding: T? = null
    val binding get() = _binding!!
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        _binding = initBinding(view)
        init()
    }
    abstract fun initBinding(view: View): T
    abstract fun init()
    override fun onDestroyView() {
        _binding = null
        super.onDestroyView()
    }
}
// 子类实现
class TestFragment : BaseFragment<FragmentContentBinding>(R.layout.fragment_content) {
    override fun initBinding(view: View) = FragmentContentBinding.bind(view)
    override fun init() {
        binding.ivLogo.visibility = View.VISIBLE
    }
}


相关文章
|
Java Android开发 Windows
用Jetpack Compose Desktop做一个推箱子小游戏,演示键盘事件绑定的方式
做Windows桌面游戏是少不了与键盘交互的,不过其实并非我们做Windows桌面应用才需要小游戏,如果要做安卓机顶盒APP,也是要监听键盘的,只不过那是遥控器的键盘,方式其实也是一样的。
279 0
用Jetpack Compose Desktop做一个推箱子小游戏,演示键盘事件绑定的方式
|
Android开发 Windows 容器
浅析 JetPack Compose 是如何安装到View视图上
为什么 Compose 无需在意 view 层级问题,怎样嵌套都行? (最简单10s就能明白);
265 0
浅析 JetPack Compose 是如何安装到View视图上
|
XML Java API
Jetpack 系列(6)—— ViewBinding 与 Kotlin 委托双剑合璧
Jetpack 系列(6)—— ViewBinding 与 Kotlin 委托双剑合璧
224 0
Jetpack 系列(6)—— ViewBinding 与 Kotlin 委托双剑合璧
|
API Android开发 Kotlin
【Jetpack】学穿:Activity Results API(下)
【Jetpack】学穿:Activity Results API
362 0
|
API 开发者
【Jetpack】学穿:Activity Results API(中)
【Jetpack】学穿:Activity Results API
237 0
|
API 开发者
【Jetpack】学穿:Activity Results API(上)
【Jetpack】学穿:Activity Results API
211 0
|
存储 缓存 数据管理
【Jetpack】学穿:ViewModel → 视图模型(下)
本节带来组件 → ViewModel 视图模型的解读!叫 视图数据 可能更贴切,有人也叫 视图状态
268 0
|
XML 数据格式 Python
【Jetpack】学穿:ViewModel → 视图模型(中)
本节带来组件 → ViewModel 视图模型的解读!叫 视图数据 可能更贴切,有人也叫 视图状态
160 0
|
存储 编解码 数据管理
【Jetpack】学穿:ViewModel → 视图模型(上)
本节带来组件 → ViewModel 视图模型的解读!叫 视图数据 可能更贴切,有人也叫 视图状态
198 0
|
缓存 Java 编译器
【Jetpack】学穿:LiveData → ???(下)
在开始这篇文章前,我就遇到了第一个关于LiveData的问题:该怎么翻译这个词呢?
308 0