还在用findViewById,不来了解下其它方式?

众所周知,都2225年了,如果你还在用Java敲安卓代码,findViewById已经是一种非常繁琐的操作,如果要去获取的id数量多,则对开发更加不友好。如果一个页面id过多,经常会有如下场景:

TextView title = findViewById(R.id.*tv_title*);
TextView title2 = findViewById(R.id.tv_title2);
TextView title3 = findViewById(R.id.tv_title3);
TextView title4 = findViewById(R.id.tv_title4);
TextView title5 = findViewById(R.id.tv_title5);
TextView title6 = findViewById(R.id.tv_title6);
TextView title7 = findViewById(R.id.tv_title7);
TextView title8 = findViewById(R.id.tv_title8);
TextView title9 = findViewById(R.id.tv_title28);
TextView title10 = findViewById(R.id.tv_title9);
TextView title11 = findViewById(R.id.tv_title10);
TextView title12 = findViewById(R.id.tv_title11);
TextView title13 = findViewById(R.id.tv_title12);
TextView title14 = findViewById(R.id.tv_title13);
TextView title15 = findViewById(R.id.tv_title14);
TextView title16 = findViewById(R.id.tv_title15);
TextView title17 = findViewById(R.id.tv_title16);
TextView title18 = findViewById(R.id.tv_title17);
TextView title19 = findViewById(R.id.tv_title18);...

数量一多,你会发现,这已经极其不友好。其实不光是不友好有问题,了解findViewById的原理后,你也会发现其内部实现在一定情况下对整体性能有轻微影响。

findViewById() 的原理

findViewById()的流程原理其实非常简单,以activity中的findViewById流程为例,activity要么继承自android.app.Activity,要么继承自androidx.appcompat.app.AppCompatActivity(你要是没适配AndroidX的话那就是support包)。这其中:

1⃣️、android.app.Activity继承类会通过getWindow得到Window对象来调用findViewById();

@Nullable
public <T extends View> T findViewById(@IdRes int id) {return getWindow().findViewById(id);
}

2⃣️、androidx.appcompat.app.AppCompatActivity继承类会通过getDelegate()得到AppCompatDelegate委派类的实例对象后调用其findViewByid(),这个对象实际是AppCompatDelegateImpl对象,创建其时传入了activity.getWindow得到的window对象。

@SuppressWarnings("TypeParameterUnusedInFormals")
@Override
public <T extends View> T findViewById(@IdRes int id) {return getDelegate().findViewById(id);
}

1⃣️和2⃣️最后都会调用Window(getWindow)里的findViewById()。

@Nullable
public <T extends View> T findViewById(@IdRes int id) {return getDecorView().findViewById(id);
}

Window类中通过getDecorView()来得到View对象(实际上是一个ViewGroup对象),

@Nullable
public final <T extends View> T findViewById(@IdRes int id) {if (id == NO_ID) {return null;}return findViewTraversal(id);
}

通过调用findViewById()来调用ViewGroup中重写的findViewTraversal()。下面源码图片是通过在线浏览网站获取到的ViewGroup类中findViewTraversal()的相关实现:

image.gif
可以看到这就是个遍历方法,如果对应界面内子元素是个View,只要id配对上可以直接返回,如果是一个ViewGroup则会调用子ViewGroup或子View的这个方法,依次遍历,直到找到目标id。很明显这是个深度优先搜索,时间复杂度为O(n)。

相关替代方案

1、 ButterKnife

大名鼎鼎的黄油刀,使用方式极为简便,项目地址:

https://github.com/JakeWharton/butterknife

在gradle中依赖:

implementation 'com.jakewharton:butterknife:xxx'
annotationProcessor 'com.jakewharton:butterknife-compiler:xxx'

对应在activity中操作为(具体一键生成方式这里不表):

public class MainActivity extends AppCompatActivity {@BindView(R.id.tv_title1)TextView tvTitle1;@BindView(R.id.tv_title2)TextView tvTitle1;@BindView(R.id.tv_title3)TextView tvTitle3;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//绑定处理ButterKnife.bind(this);}
......
}

其实ButterKnife只是通过注解对findViewById的进行的一个取代,增加代码的可读性,而findViewById的各种缺点依然存在。当然,就这个开源框架而言,功能绝不仅仅是替代findViewById()。自从kt语言出来后,黄油刀的功效捉襟见肘,非Java版的老工程,不推荐。

2、kotlin-android-extensions

如果你项目可以使用kotlin,则可以使用kotlin-android-extensions。

在module的gradle中加入:

plugins {id 'kotlin-android-extensions'
}

可直接在对应类中通过其id的形式控制其控件,相当于已经获取了一遍控件id。

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.test.demo.main.activity_main.*class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)tv_hello.setOnClickListener {}}
}

这种方法其本质上依旧还是通过findViewById去实现,有兴趣的小伙伴可以反编译看看。虽然现在此法已不被官方推荐,但其便利性还是首屈一指。

3、ViewBinding

一曲新人笑,几度旧人哭。此法一出,kotlin-android-extensions已不被官方推荐。

image.png
(注意,此法只能在AndroidStudio3.6及更高版本上可用)

使用方式:在build.gradle中依赖:

android {...buildFeatures {viewBinding true}
}

reload后,系统会为每个layout目录下 XML 布局文件生成一个绑定类。每个绑定类均包含对根视图以及具有 ID 的所有视图的引用。系统会通过以下方式生成绑定类的名称:将 XML 文件的名称转换为驼峰式大小写,并在末尾添加“Binding”一词。
例如:某个布局命名为activity_login,其所生成的绑定类的名称就为LoginActivityBinding,这个绑定类就会完成findViewById的工作。

不同布局会生成不同绑定类,他们所生成的路径在:在build/generated/data_binding_base_class_source_out/debug/out/com/xxx/yyy/databinding/目录下。

当然,如果不想xml文件生成 Binding 类,可以在 xml 布局文件中根 view 写入此属性:

tools:viewBindingIgnore="true"

其在代码中使用方式如下:(ViewBinding在Java和kotlin类中都可使用,这里仅是拿kotlin类举例)

· Activity:

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());binding.tvTest.setText("This is ViewBinding");
}

可见,setContentView()中的参数改为了XXXbing.getroot()。调用布局中的某控件,只需要XXXbing.viewID(驼峰原则)可直接拿到实例对象(上述代码中的binding.tvTest控件在xml中的id为tv_test)。例如:xml中TextView控件id为tv_demo,则在activity中对应实例为XXXbing.tvDemo。

· Fragment中:

public class MyFragment extends Fragment {private FragmentMyBinding binding;private Context context;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {binding = FragmentMyBinding.inflate(getLayoutInflater(), container, false);return binding.getRoot();}@Overridepublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);binding.tvTitle.setText("Hello ViewBinding");}@Overridepublic void onDestroyView() {super.onDestroyView();binding = null;}
}

可以看出,跟Activity的引用方式区别不大,这里需要稍微注意Fragment 的存在时间比其视图长。在 Fragment对应onDestroyView()时要清除对绑定类实例的所有引用。

·RecyclerView adapter中:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
、、、、、、@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {ItemLayoutBinding itemBinding = ItemLayoutBinding.inflate(inflater, parent, false);return new ViewHolder(itemBinding);}@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {holder.textView.setText(mData.get(position));}static class ViewHolder extends RecyclerView.ViewHolder {TextView textView;public ViewHolder(@NonNull ItemLayoutBinding itemBinding) {super(itemBinding.getRoot());textView = itemBinding.textView;}}
}

可见,应用方式无大致区别。

总结

1、findViewById兼容性好,适用所有场景,且灵活;

2、findViewById性能略差,底层就是个深度优先搜索,且id过多情况下容易造成可读性极差的情况,从上述的原理流程中不难看出,在Activity中调用findViewById,实际上是调用Window中的findViewById,但是Fragment中并没有单独的Window,Fragment中调用findViewById的效果和Activity中调用的效果一模一样。所以如果一个Activity中有多个Fragment,Fragment中的控件名称又有重复的,直接使用findViewById会爆错。

3、ButterKnife可一键生成,方便至极,但缺点跟findViewById一样。如果不是老工程,此法已不推荐使用。

4、Google官方表示,与使用 findViewById 相比,ViewBinding具有一些很显著的优点:

· 空指针安全:由于视图绑定(ViewBinding)会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用 @Nullable 标记。(说白了就是让你丫代码少爆空指针)

· 类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。

这些差异意味着布局和代码之间的不兼容将会导致构建在编译时(而非运行时)失败。

下篇预告:第四种方式:DataBinding。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/660952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ISOLAR-A/B问题总结

ISOLAR-A/B报错问题一&#xff1a; 1. Target ARObject: <ECUC-MODULE-CONFIGURATION-VALUES-REF> Unable to resolve reference /ETAS_Project/EcucModuleConfigurationValuess/E2E. - Line: 99. [Infos] <ECUC-MODULE-CONFIGURATION-VALUES-REF> : </ARPacka…

Pytest 与allure测试报告集成

通过Feature, story, step 记录测试的功能&#xff0c;场景及测试步骤 # login.pylogin_func函数 传入参数是name 和 password 当输入的name和password与数据库db_data中数据一致时&#xff0c;返回“XXX成功登录系统&#xff01;” 当输入的name存在于数据库db_data但密码不正…

Qt应用开发(安卓篇)——调用ioctl、socket等C函数

一、前言 在 Qt for Android 中没办法像在嵌入式linux中一样直接使用 ioctl 等底层函数&#xff0c;这是因为因为 Android 平台的安全性和权限限制。 在 Android 中&#xff0c;访问设备硬件和系统资源需要特定的权限&#xff0c;并且需要通过 Android 系统提供的 API 来进行。…

自定义SpringBoot启动图标

在SpringBoot项目的resources目录下创建banner.txt文件 在https://www.bootschool.net/网站上复制Ascll艺术字&#xff08;图&#xff09;粘贴到banner.txt中保存。 启动项目就会加载 可以修改颜色&#xff0c;和版本号 ${application.version} 输出版本 ${spring-boot.v…

HubSpot是如何通过社交媒体与用户建立互动?

HubSpot善于利用社交媒体平台&#xff0c;与用户建立深度互动&#xff0c;增强用户对品牌的参与感与黏性。以下是HubSpot在社交媒体上建立互动的关键策略&#xff1a; 1. 及时回应用户评论&#xff1a;建立积极互动氛围 HubSpot注重在社交媒体上及时回应用户的评论。无论是表…

用的到的linux-文件移动-Day2

前言&#xff1a; 在上一节&#xff0c;我们复习了cd大法和创建生成文件和文件夹的方法&#xff0c;介绍了一些“偷懒”&#xff08;高效&#xff09;的小技巧&#xff0c;本节&#xff0c;我们一起来探讨下&#xff0c;我们对文件移动操作时有哪些可以偷懒的小技巧~ 一、复制…

引领AI创意教育新浪潮,瑞云AIGC实训平台解决方案来了

过去的2023年&#xff0c;AI&#xff08;人工智能&#xff09;成为了年度科技圈关键词&#xff0c;各行各业都在AI化&#xff0c;据统计&#xff0c;AIGC市场规模预计到2030年将达到万亿级别&#xff0c;这不仅是市场的趋势&#xff0c;更是创新的机遇。 教育行业更是如此&…

【网络基础】网络协议传输层UDP和TCP

UDP 解包和分用 解包&#xff08;解析数据包&#xff09; 捕获数据包&#xff1a;首先&#xff0c;接收端的网络栈捕获UDP数据包。检查目的端口&#xff1a;接收端检查数据包头部的目的端口&#xff0c;以确定哪个应用程序应该接收该数据包。验证校验和&#xff1a;接收端可能…

阿赵UE学习笔记——14、LOD

阿赵UE学习笔记目录   大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的用法。这次看看虚幻引擎的Level Of Detail(LOD)的用法。 一、测试场景准备 用植物系统&#xff0c;在地形上面刷了好多草&#xff1a; 这个时候看一下网格&#xff0c;会发现网格比较多和密集。 …

CentOS部署Docker Registry镜像仓库并结合内网穿透实现远程访问

文章目录 1. 部署Docker Registry2. 本地测试推送镜像3. Linux 安装cpolar4. 配置Docker Registry公网访问地址5. 公网远程推送Docker Registry6. 固定Docker Registry公网地址 Docker Registry 本地镜像仓库,简单几步结合cpolar内网穿透工具实现远程pull or push (拉取和推送)…

因子图、边缘化与消元算法的抽丝剥茧 —— Notes for “Factor Graphs for Robot Perception“

Title: 因子图、边缘化与消元算法的抽丝剥茧 —— Notes for “Factor Graphs for Robot Perception” 文章目录 I. 前言II. 因子图的基本概念1. 因子图的定义2. SLAM 中的因子图A. 因子图的图示B. 因子图的因式C. 因子图的二分图形式 III. 边缘化与消元运算的基本原理1. 边缘化…

全网最简单的幻兽帕鲁服务器搭建教程

幻兽帕鲁是一款备受欢迎的多人在线游戏&#xff0c;为了提供更好的游戏体验&#xff0c;许多玩家选择自行搭建服务器。本文将指导大家如何简单快速地搭建幻兽帕鲁服务器&#xff0c;轻松享受游戏的乐趣。 第一步&#xff1a;购买游戏联机服务器 购买入口&#xff1a;https://tx…

shell - 免交互

一.Here Document 免交互 1. 交互的概念 交互&#xff1a;当计算机播放某多媒体程序的时候&#xff0c;编程人员可以发出指令控制该程序的运行&#xff0c;而不是程序单方面执行下去&#xff0c;程序在接受到编程人员相应的指令后而相应地做出反应。 对于Linux操作系统中&…

Three.js学习1:threejs简介及文档本地部署

开一个天坑&#xff0c;Three.js 我觉得未来3D页面一定是一个趋势。 -----------------------------华丽的分割线------------------------- github&#xff1a;https://github.com/mrdoob/three.js/ 官网&#xff1a;Three.js – JavaScript 3D Library Threejs官网中文文…

LaTeX教程(001)-LaTeX文档结构(01)

LaTeX教程(001)- LaTeX \LaTeX LATE​X文档结构(01) 说在前面 这是我本人学习《The LaTeX Companion》第三版的笔记&#xff0c;但并不是翻译。 书籍的第一章对 LaTeX \LaTeX LATE​X及其历史进行了相当长的介绍&#xff0c;这是几乎每一本关于 LaTeX \LaTeX LATE​X的书都会…

如何一键更新幻兽帕鲁服务器?腾讯云轻量应用服务器版

如何在不需要远程登录服务器的情况下&#xff0c;通过一行命令来更新幻兽帕鲁呢&#xff1f; 腾讯云轻量云一键部署幻兽帕鲁服务器教程&#xff1a;https://curl.qcloud.com/pzBO9wN7 首先是幻兽帕鲁Windows服务器版&#xff0c;只需要在腾讯云的轻量应用服务器详情页&#x…

《苍穹外卖》电商实战项目实操笔记系列(P123~P184)【下】

史上最完整的《苍穹外卖》项目实操笔记系列【下篇】&#xff0c;跟视频的每一P对应&#xff0c;全系列10万字&#xff0c;涵盖详细步骤与问题的解决方案。如果你操作到某一步卡壳&#xff0c;参考这篇&#xff0c;相信会带给你极大启发。 上篇&#xff1a;P1~P65《苍穹外卖》项…

C#中使用OpenCvSharp4库读取本地图像并显示

C#中使用OpenCvSharp4库读取本地图像并显示 OpenCvSharp4是基于.NET 的 OpenCV 包装器&#xff0c;OpenCV源代码是采用C和C写的&#xff0c;目前对于C和Python开发者相对来说比较友好&#xff0c;对于Python开发者而言官方提供了opencv-python使用。 首选我们使用Visual Studi…

运动编辑学习笔记

目录 跳舞重建&#xff1a; 深度运动重定向 Motion Preprocessing Tool anim_utils MotionBuilder 跳舞重建&#xff1a; https://github.com/Shimingyi/MotioNet 深度运动重定向 https://github.com/DeepMotionEditing/deep-motion-editin 游锋生/deep-motion-editin…

uni-app app引入天地图

话不多说咸鱼来了 <template><view><div class"mapBox" style"width: 100%; height: 100vh;background: #ddc0c0;" id"mapId" ></div></view> </template> <script module"test" lang"r…