遇到问题
每次在创建或重新创建视图后使用findViewById()函数来获取对它的引用时,Android 系统都会在运行时遍历视图层次结构来查找它。当您的应用只有少数视图时,这不是问题。然而,生产应用程序可能在一个布局中有几十个视图,即使有最好的设计,也会有嵌套的视图。
考虑一个包含滚动视图的线性布局,该滚动视图包含一个文本视图。对于较大或较深的视图层次结构,查找视图可能需要足够的时间,从而明显降低用户的应用程序速度。在变量中缓存视图会有所帮助,但您仍然必须在每个命名空间中为每个视图初始化一个变量。有了很多视图(View)和多个活动(Activity),这些都累加在一起,这样会导致增加重复代码,降低项目可维护性,即所有开发人员的技术痛点。
技术方案
通过视图绑定功能,您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。
在大多数情况下,视图绑定会替代 findViewById。
在此任务中,您将设置视图绑定,并使用视图绑定将对findViewById()的调用替换为对视图绑定的调用。
操作步骤
第 1 步:启用视图绑定
要使用视图绑定,您需要在 Gradle 文件中启用视图绑定,因为默认情况下未启用。这是因为视图绑定会增加编译时间并可能影响应用程序启动时间。
1.打开build.gradle (Module: 项目名.app)文件,如下图所示:
2.要在android模块内启用视图绑定。
「旧版本」设置viewBinding元素内的enabled属性为true,代码如下:
android {
...
viewBinding {
enabled = true
}
}
「新版本」上面是Kotlin Android扩展功能的,该功能已废弃,这意味着不再支持使用 Kotlin 合成器进行视图绑定。如果您的应用使用 Kotlin 合成器进行视图绑定,请迁移到 Jetpack 视图绑定,代码如下:
android {
...
buildFeatures {
viewBinding true
}
}
3.如果您的应用程序不使用Parcelize功能,请删除启用Kotlin Android扩展的行,代码如下:
plugins {
id 'kotlin-android-extensions'
}
4.如果您的应用程序使用Parcelize功能,请切换到使用独立的「kotlin-parcelize」Gradle插件,该插件提供了一个 Parcelable 实现生成器,代码如下:
plugins {
id 'kotlin-parcelize'
}
5.出现同步项目的提示时,点击「立刻同步(Sync Now)」,如果没有提示,请依次执行「文件(File)> 使用Gradle文件来同步项目(Sync Project with Gradle Files)」操作,或直接点击工具栏右侧的Gradle图标,如下图所示:
第 2 步:更改布局文件以用于视图绑定
为某个模块启用视图绑定功能后,系统会为该模块中包含的每个 XML 布局文件生成一个绑定类。每个绑定类均包含对根视图以及具有 ID 的所有视图的引用。
1.打开activity_main.xml文件,并切换到「代码(Code)」选项卡,如下图所示:
2.将之前的<ConstraintLayout>标签替换为<LinearLayout>标签,并去掉无必要的部分代码,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btnText" />
</LinearLayout>
「小知识」
依次执行「代码(Code) > 重新格式化代码(Reformat Code)」操作,修复代码缩进即可,如下图所示:
3.如果您希望在生成绑定类时忽略某个布局文件,请将 tools:viewBindingIgnore="true" 属性添加到相应布局文件的根视图中,代码如下:
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>
第 3 步:在MainActivity类中创建绑定对象
将绑定对象的引用添加到MainActivity类内,以便您可以使用它来访问视图。
1.打开MainActivity.kt或MainActivity.java文件,代码如下:
Kotlin
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
Java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
2.在onCreate()方法上为绑定对象创建一个变量。该变量通常称为binding。系统会通过以下方式生成绑定类的名称:将指定的XML 文件名转换为驼峰式大小写,并在末尾添加「Binding」一词。
流程思路,如下图所示:
「小知识」
小驼峰式命名法(lower camel case):第一个单字以小写字母开始,第二个单字的首字母大写。例如:firstName、lastName。
大驼峰式命名法(upper camel case):每一个单字的首字母都采用大写字母,例如:FirstName、LastName、CamelCase。
示例代码,如下:
// Kotlin
private lateinit var binding: ActivityMainBinding
// Java
ActivityMainBinding binding;
3.点击ActivityMainBinding,并按下「Alt+Enter(Win版)或Option+Enter(Mac版)」组合键,导入这个缺失的类即可。
导入前,提示「导入ActivityMainBinding」,如下图所示:
Kotlin
Java
导入后,添加相关的import语句,代码如下:
// Kotlin
import com.fm.test.databinding.ActivityMainBinding
// Java
import com.fm.test.databinding.ActivityMainBinding;
第 4 步:在MainActivity类中使用视图绑定
如需设置绑定类的实例以供MainActivity类使用,请在MainActivity类的 onCreate() 方法中执行以下步骤:
1.调用生成的绑定类中包含的静态 inflate() 方法,即此操作会创建该绑定类的实例以供MainActivity类使用,代码如下:
Kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.fm.test.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater);
}
}
Java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.fm.test.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
}
}
2.通过调用 getRoot() 方法或使用 Kotlin 属性语法获取对根视图的引用,即会返回 LinearLayout 根视图,代码如下:
Kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.fm.test.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater);
val view = binding.root
}
}
Java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import com.fm.test.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
}
}
3.将根视图传递到 setContentView(),使其成为屏幕上的Activity视图,代码如下:
Kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.fm.test.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater);
val view = binding.root
setContentView(view)
}
}
Java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import com.fm.test.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
}
}
第 5 步:使用绑定对象替换对findViewById()的所有调用
现在可以将对绑定对象中视图的引用替换为对findViewById()的所有调用。创建绑定对象时,编译器从从布局中视图的ID生成绑定对象中视图的名称。例如,按钮的id属性值是user_btn,和绑定对象中的userBtn一样。
在onCreate()中,将findViewById()替换为引用绑定对象中的ID属性值「注意:以小驼峰式命名法(lower camel case)为准」。将findViewById(R.id.btn)替换为binding.btn,并添加要执行的操作代码,代码如下:
Kotlin
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.fm.test.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater);
val view = binding.root
setContentView(view)
binding.btn.setOnClickListener(this::clickHandlerFunction)
}
private fun clickHandlerFunction(view: View) {
Toast.makeText(this,"我是未来码匠", Toast.LENGTH_LONG).show()
}
}
Java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.fm.test.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
binding.btn.setOnClickListener(this::clickHandlerFunction);
}
public void clickHandlerFunction(View view) {
Toast.makeText(this,"我是未来码匠", Toast.LENGTH_LONG).show();
}
}
提示
如果您在进行更改后看到编译器错误,请选择「构建(Build) > 清理项目(Clean Project)」 ,然后选择「构建(Build)> 重构项目(Rebuild Project)」。这样做通常会更新生成的文件。否则,选择「文件(File) > 项目无效时缓存或重新启动(Invalidate Caches/Restart)」进行更彻底的清理。
您之前了解Resources保存对应用程序中所有资源的引用的对象。在引用视图时,您可以Binding以类似的方式考虑对象;但是,该Binding对象要复杂得多。
运行效果
下载源码
Android技术知识点:如何使用视图绑定来消除findViewById()https://download.csdn.net/download/qq_29901385/86247662
与 findViewById 的区别
与使用 findViewById 相比,视图绑定具有一些很显著的优点:
- Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用 @Nullable 标记。
- 类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。
这些差异意味着布局和代码之间的不兼容将会导致构建在编译时(而非运行时)失败。
与数据绑定的对比
视图绑定和数据绑定均会生成可用于直接引用视图的绑定类。但是,视图绑定旨在处理更简单的用例,与数据绑定相比,具有以下优势:
- 更快的编译速度:视图绑定不需要处理注释,因此编译时间更短。
- 易于使用:视图绑定不需要特别标记的 XML 布局文件,因此在应用中采用速度更快。在模块中启用视图绑定后,它会自动应用于该模块的所有布局。
反过来,与数据绑定相比,视图绑定也具有以下限制:
- 视图绑定不支持布局变量或布局表达式,因此不能用于直接在 XML 布局文件中声明动态界面内容。
- 视图绑定不支持双向数据绑定。
考虑到这些因素,在某些情况下,最好在项目中同时使用视图绑定和数据绑定。您可以在需要高级功能的布局中使用数据绑定,而在不需要高级功能的布局中使用视图绑定。
总结
使用数据绑定替换对findViewById()的调用的步骤:
1.在build.gradle (Module: 项目名.app)文件的android模块内启用视图绑定:buildFeatures { viewBinding true }
2.在XML布局中使用任何视图作为根视图。
3.定义绑定变量:private lateinit var binding:ActivityMainBinding(Kotlin) 或 ActivityMainBinding binding;(Java)
4.在MainActivity类内创建绑定对象,将setContentView(R.layout.activity_main)替换为如下的代码:
Kotlin
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
Java
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
5.用对绑定对象中视图ID属性值的引用替换对findViewById()的调用。例如:findViewById<Button>(R.id.btn) => binding.btn,即在本例中,视图名是XML文件中的视图ID属性值「注意:以小驼峰式命名法(lower camel case)为准」。
实践出真知,钻研出成果。学习Android的过程不管有多难,我们也要时刻保持一颗上进的心,努力钻研,反复分析,寻找突破口。与此同时,攻克难题也需要我们有足够的耐心,厚积薄发,突破极限。
欢迎各位大佬来阅读我创作的Android开发系列文章。希望大家能针对文章内容提出宝贵意见,同时也欢迎大家能指正问题,进一步地与我探讨改进方案。希望本篇文章能得到大家的鼓励和点赞,大家的支持将是我创作的最大动力。
作者声明
本篇经验系本人依照真实经历原创,需要转载到博客、自媒体平台、技术网站等,未经许可,严格遵守本人的知识版权,谢绝转载。如想转载,请联系本人授权。
联系邮箱:3403978592@qq.com
微信号:X9267543839
QQ:3403978592