irpas技术客

Android findViewById及替代方案大全_柚子君下_android findviewbyid

未知 7037

自打学习Android开发以来,findViewById就是我使用最频繁的函数之一。findViewById可为灵活至极,但是又让人爱恨交加。那findViewById究竟有哪些优缺点,他又有哪些替代方案呢?今天就来做一个总结

findViewById findViewById的原理

findViewById的原理非常简单,我们都知道Android中的View结构是一个树形结构,findViewById就是自树的根节点,依次遍历其子节点,知道找到目标的id。

findViewById的优点

还是先夸夸findViewById的优点吧,

兼容性好,下面各种替代方案,都有其适用的场景,但是findViewById适用所有的场景,当你不知道用哪种方案的时候,那就用findViewById吧,肯定没有错。非常灵活,适合动态加载layout文件。比如一个Activity,需要在不同的业务中,加载两个不同的layout文件,但是两个layout文件只有部分间距不同,其他各个元素都是相同的。这个时候,用findViewId就可以完美适用。 findViewById的缺点 性能略差不好,findViewById是基于树形结构的查找,理论上会带来性能的额外开销,但是实际项目中,因为控件的个数也不会非常非常多,所以可以忽略不计。;Fragment中使用容易犯错。从原理图可以看到,在Activity中调用findViewById,实际上是调用Window中的findViewById,但是Fragment中并没有单独的Window,Fragment中调用findViewById的效果和Activity中调用的效果一模一样。所以如果一个Activity中有多个Fragment,Fragment中的控件名称又有重复的,那直接findViewById会出错的;增加代码的耦合度,findViewById随时实地都可以调用,在子view中,在Activity中等等,如果滥用起来,会让代码耦合的一塌糊涂,后面查找bug起来,非常麻烦,因为不知道View的属性在哪个类中被改变了;容易引发空指针,一个大型项目中,控件的id经常会重复,xml中删除了一个控件,但是对应的Activity中没有删除这个控件的相关引用,编译时并不会报错,但是运营室时会报出空指针;代码可读性不好,findViewById往往在Activity的onCreate方法中被引用,我们不能方便的将xml中的控件与代码中的控件结合起来,特别是如果xml的命名与代码中的命名又不规范,代码阅读起来简直就是噩梦。 ButterKnife

有人推出了ButterKnife框架,详细的用法不啰嗦了,简单看下使用方法

class ExampleActivity extends Activity { @BindView(R.id.title) TextView title; @BindView(R.id.subtitle) TextView subtitle; @BindView(R.id.footer) TextView footer; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); // TODO Use fields... } }

额。。。

从上面的代码可以看到,ButterKnife基本上只是对findViewById的一个取代而已,增加了代码的可读性,findViewById的各种缺点依然存在。

copy一份网上找来的优点吧(笔者不是很认同)

1、强大的View绑定和Click事件处理功能,简化代码,提升开发效率 2、方便的处理Adapter里的ViewHolder绑定问题 3、运行时不会影响APP效率,使用配置方便 4、代码清晰,可读性强 5、…

笔者认为,ButterKnife虽然没有解决findViewById的绝大部分问题,但是他依然是一个非常优秀的开源框架。作为一个第三方的开源框架,他已经做出了最大努力。

kotlin-android-extensions

如果使用kotlin,则可以用kotlin-android-extensions来替代findViewById,用法如下 module的build.gradle文件加入以下代码

plugins { id 'kotlin-android-extensions' }

然后在代码中就可以引用了

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

对生成的apk进行反编译下,看下他的原理是什么

/* access modifiers changed from: protected */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView((int) R.layout.activity_main); ((TextView) findViewById(R.id.tv_hello)).setOnClickListener(MainActivity$$ExternalSyntheticLambda0.INSTANCE); }

可以看到,他本质上依然是使用findViewById进行实现的,那findViewById的缺点他依然存在。

databinding

databinding与viewbinding的诞生,为优化findViewById的难题提出了另外一种思路,就是生成一个databinding文件,文件中记录了各个控件的引用。用法如下 module的build.gradle中加入以下代码

android { dataBinding { enabled true } }

然后将布局文件用layout标签包裹起来

<?xml version="1.0" encoding="utf-8"?> <layout> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> ... </androidx.constraintlayout.widget.ConstraintLayout> </layout>

activity代码如下

class MainActivity : AppCompatActivity() { lateinit var binding:ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.tvHello.setOnClickListener { } } }

可以看到,databinding会根据动态生成一个ActivityMainBinding文件,在执行ActivityMainBinding.inflate的时候,会自动生成控件的引用(mapBindings方法),这里对布局tree执行一趟遍历查找就可以生成所有的引用。

综上,我们归纳中他的优缺点。

优点

规避了控件空指针错误,如果有引用错误,则会在编译阶段发现;效率比findViewById要高,一趟遍历可以生成所有控件的引用,而findViewById是每次执行时都需要遍历一遍代码的可读性要高,得益于Android Studio的强大功能,我们很轻易的将java(kotlin)代码中的控件引用与xml的定义结合起来

缺点

灵活性不高,如果需要动态选取引用的布局文件,binding就无法适用了。改造成本较大,布局文件只有加上layout标签才可以使用。 viewbinding

google在推出databinding的同时,推出了viewbinding。 先看下viewbinding的用法

module的build.gradle中增加以下代码

android { viewBinding { enabled true } }

viewbinding会默认为所有的布局文件生成对应的binding文件,如果不想生成,可以在布局文件中加上以下代码

tools:viewBindingIgnore="true"

然后在activity中直接使用即可,用法与databinding的用法相同。

但是viewbinding与databinding的最大不同是,databinding的控件引用通过mapbinding来实现的,一趟查找就能生成所有的控件引用,但是viewbinding是通过findViewById来生成所有的控件引用,所有viewbinding理论上效率比databinding要差一点,但是更加轻量级。

viewbinding生成控件的引用

public static ActivityMainBinding bind(View rootView2) { int id = R.id.ll_layout; LinearLayout llLayout2 = (LinearLayout) ViewBindings.findChildViewById(rootView2, R.id.ll_layout); if (llLayout2 != null) { id = R.id.tv_hello; TextView tvHello2 = (TextView) ViewBindings.findChildViewById(rootView2, R.id.tv_hello); if (tvHello2 != null) { id = R.id.tv_hello1; TextView tvHello12 = (TextView) ViewBindings.findChildViewById(rootView2, R.id.tv_hello1); if (tvHello12 != null) { return new ActivityMainBinding((ConstraintLayout) rootView2, llLayout2, tvHello2, tvHello12); } } } throw new NullPointerException("Missing required view with ID: ".concat(rootView2.getResources().getResourceName(id))); }

综上,我们总结下viewbinding的优缺点

优点

规避了引用控件时的空指针错误问题,编译时生成控件的引用较databinding,更加轻量级代码可读性高,同databinding一样 缺点灵活性不高,如果需要动态选取引用的布局文件,binding就无法适用了。默认会为所有的xml生成binding文件,会生成很多冗余的文件。


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Android #findviewbyid