Android开发

您所在的位置:网站首页 zippo盔甲机哪一年生产的 Android开发

Android开发

2024-07-12 22:53:04| 来源: 网络整理| 查看: 265

1. 前言 如何干掉模版代码是很多第三方框架的设计初衷,在Android开发中,findViewById()是必不可少的存在,这样的冗余代码在很久以前充斥在Android工程中,因此也出现了很多精简方案。在Android Studio3.6中加入了很多新特性——View Binding就是其中之一。目前已经在工程中使用并上线,未出现稳定性问题,因此做以下记录。在谈View Binding之前,我们先聊一下在此之前有哪些代表性方案做过这个事情,以及它们的优缺点分析。 1.1 Butter Knife/Kotter Knife

Butter Knife框架是17年前后很火的存在。但是在Kotlin中直接使用ButterKnife的注解方式的话,会出现空指针的异常并导致绑定失败。从而Kotter Knife应运而生,可以理解成是Butter Knife的Kotlin版本。用法如下:

//Butter Knife @BindView(R.id.title) TextView title; ButterKnife.bind(this); // TODO Use fields... // Kotter Knife val submitButton: Button by bindView(R.id.submit_button) // TODO Use val...

然而这种方案已经被作者标记为Deprecated,作者认为该框架为每个视图引用分配了一个对象,这种思路不应该被采用,以及使用这种思路的框架也应该被淘汰,并推荐使用ViewBinding的方式。

1.2 Data Binding

依稀记得Data Binding是16年底的时候推出的,那时候真的很火,因为它是谷歌对于MVVM开发模式在Android上体现之一,其底层通过Annotation Processor实现的。用法如下,关键就是新增了data节点,以及在layout中的属性表达式@{}。

默认情况下,基于layout文件的名称会生成单词首字母大写并添加“Binding”后缀一个Binding类。此类包含layout属性以及在Views中的所有binding属性(例如user变量),并且能够为该属性赋值。因此在Java代码中:

MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity); User user = new User("firstName", "lastName"); binding.setUser(user);

但是Data Binding的劣势也很明显:

它侵入了xml文件,因为只有布局文件的根标签必须是layout时, Data Binding才会生成对应的Binding Class。Annotation Processor不仅对构建速度有负面影响,也会产生部分性能问题。最最关键的,一些Data Binding相关的bug很难跟踪和解决,开发者在MVVM模式上的bug需要花费更多的时间,我觉得这也是MVVM没有在移动端发展起来的原因之一。 2. View Binding

这就是今天的主角了,View Binding既不会像Butter Knife那样为View分配多余的对象,也不会像Data Binding那样使用Annotation Processor,它的实现既简单又强大。首先来看一下如何使用View Binding。

2.1 View Binding所需环境 首先需要将Android Studio升级到3.6版本及以上升级Gradle plugin版本到3.6.1并在app模块中手动开启view binding开关(View Binding以模块为粒度进行开启/关闭) buildscript { ... dependencies { classpath "com.android.tools.build:gradle:3.6.1" } } android { ... viewBinding { enabled = true } }

这里升级gradle的时候遇到了一些小坑,即工程中使用的tinker版本过低,其使用了旧版gradle中的语法,因此会报错,将tinker版本升级到’1.9.14.6’后问题解决。

2.2 View Binding的使用

activity_main.xml的布局如下:

这里直接include了一个子布局,inner_layout.xml的布局如下:

默认情况下,每一个布局xml文件都会生成一个对应的Binding类,名字的生成规则同Data Binding。在该例子中,会自动为我们生成一个ActivityMainBinding.java,以及include的标签对应的InnerLayoutBinding,这里include必须指定一个id。当然,如果不需要为该xml生成Binding类,可以在xml的根布局中配置tools:viewBindingIgnore=“true”。 在Java中的使用如下所示: public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(LayoutInflater.from(this)); setContentView(binding.getRoot()); binding.firstButton.setText("Text Change"); binding.innerLayout.innerLayoutSecondButton.setText("Inner Layout Button Text Change"); } }

用法也很简单,直接通过binding点名字的方式就可以获取到控件实例,消除了findViewById的模版代码。 这里可以看到,setContentView的入参写法都变了,因为可通过XXXBinding类的getRoot函数获取到布局的根View,再通过setContentView添加到Activity。

2.3 View Binding的一些注意点

在2.2中我们演示了include的用法,只需要加一个id,我们就可以通过binding.innerLayout获取到该子布局。但是有个特殊情况需要处理,就是merge标签。 如果子布局inner_layout.xml中的布局如下,多了一个merge标签:

那么在activity_main.xml的布局的include标签中就一定不要设置id了(这里就不贴activity_main.xml的代码了),否则会找不到View报空指针异常。这个情况,我们可以先初始化主布局,再初始带merge的布局,那么就需要用到View Binding的另一个构造方法了,处理如下:

public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(LayoutInflater.from(this)); setContentView(binding.getRoot()); //使用 binding.firstButton.setText("Text Change"); //merge标签处理 InnerLayoutBinding subBinding = InnerLayoutBinding.bind(binding.getRoot()); subBinding.innerLayoutSecondButton.setText("Inner Layout Button Text Change"); } } 2.4 View Binding的原理解析

从2.2和2.3中我们看到了Binding类的两种构造方法,其实它有三种使用方法,如下所示:

inflate(@NonNull LayoutInflater inflater) inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, boolean attachToParent) bind(@NonNull View rootView) 第一种和第二种直接调用inflate()方法,传入inflater即可得到Binding类的实例,它会在内部inflate对应的xml布局,并得到并持有该xml布局的根布局View实例,即例子中的binding.getRoot()。inflate()最后仍然会调用bind()。第三种方法就更简单粗暴了,直接使用bind()方法并传入View实例,这个View实例可以是你在外部自己生成的,即可为你生成一个Binding类的实例,再通过点view的方式,找到传入的根View下的子View实例。比如在我们的工程中,在Adapter中封装了ViewHolder加载xml的过程并将根布局view放在了ViewHolder的基类里,这种情况显然就只能使用bind(view)的方式了。这里以2.2中生成的Binding类为例,该类的路径如下所示: 在这里插入图片描述再看一下其生成的内容,分析一下它到底为我们做了什么事情: // Generated by view binder compiler. Do not edit! public final class ActivityMainBinding implements ViewBinding { @NonNull private final LinearLayout rootView; @NonNull public final Button firstButton; @NonNull public final InnerLayoutBinding innerLayout; private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull Button firstButton, @NonNull InnerLayoutBinding innerLayout) { this.rootView = rootView; this.firstButton = firstButton; this.innerLayout = innerLayout; } @Override @NonNull public LinearLayout getRoot() { return rootView; } @NonNull public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) { return inflate(inflater, null, false); } @NonNull public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, boolean attachToParent) { View root = inflater.inflate(R.layout.activity_main, parent, false); if (attachToParent) { parent.addView(root); } return bind(root); } @NonNull public static ActivityMainBinding bind(@NonNull View rootView) { String missingId; missingId: { //绑定View且做了非空判断,否则抛出空指针 Button firstButton = rootView.findViewById(R.id.first_button); if (firstButton == null) { missingId = "firstButton"; break missingId; } View innerLayout = rootView.findViewById(R.id.inner_layout); if (innerLayout == null) { missingId = "innerLayout"; break missingId; } InnerLayoutBinding innerLayoutBinding = InnerLayoutBinding.bind(innerLayout); return new ActivityMainBinding((LinearLayout) rootView, firstButton, innerLayoutBinding); } throw new NullPointerException("Missing required view with ID: ".concat(missingId)); } } 可以看出View Binding的实现既简单又强大。它的主要逻辑在于bind()方法中,在绑定的过程做了非空判断。inflate()的逻辑也非常的清晰。 2.5 小彩蛋 从Binding类的第43行代码开始,有一个很有意思的Java用法,叫做Java Label。形如string:{},并在代码块中满足条件时直接break string跳出指定的代码块,试想一下,如果不这样写,就要把throw new NullPointerException写N遍,可以学习一下这种写法。 block: { if(condition) break block; // rest of code that won't be executed if condition is true } 同时,Java Label也可以用在双层循环中,随意指定你想结束内层循环,还是外层循环。这里将break改为continue同样适用。 outterLoop: for(int i = 0; i if(someConditon) break outterLoop; // 结束for循环 if(anotherConditon) break; // 结束while循环 // more code } // more code }


【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭