Android的findViewById真是太烦人了,模板似的方法,要写在每个Activity,Fragment,Adapter里面。声明 View和findView总是间隔着未知的行距;setOnClickListener之后,总是要寻找对应的onClick方法在何处。
难道Android就不能智能的把layout中的View相应的与对应field绑定起来?
答案是:Android本身不支持,但是我们可以通过一些hack达到目的。
基于反射的InjectView
findViewById接受一个int的id参数,就可以找到对应的View。通过annotation,我们可以把这个int类型的id声明的对应的field上面,通过Java的反射,遍历每个field,找到对应的id,就可以完成对field的赋值。
下面是具体做法:
声明Annotation
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface InjectView { int value(); }
测试Annotation
@InjectView(R.id.aView) View aView;
Annotation就是一个标识,标识出aView是InjectView关心的字段。
进行注入
Field[] declaredFields = getClass().getDeclaredFields(); for (Field field : declaredFields) { InjectView annotation = field.getAnnotation(InjectView.class); if (annotation != null) { int id = annotation.value(); View view = findViewById(id); field.setAccessible(true); try { field.set(this, view); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
在setContentView之后,我们对所有的field检查是否包含@InjectView标识,对包含@InjectView标识的field,进行赋值。
通过同样的方式,我们甚至可以把setContentView省去,也通过注入来实现。假设我们有一个@InjectLayout的Annotation,我们通过这个Annotation来标识setContentView对应的id.
@InjectLayout(R.layout.activity_main) public class MainActivity extends FragmentActivity { InjectLayout annotation = getClass().getAnnotation(InjectLayout.class); int id = annotation.value(); try { Method setContentView = getClass().getMethod("setContentView", int.class); setContentView.invoke(this, id); } catch (Exception e) { e.printStackTrace(); }
把上面的这些代码封装到BaseActivity中,之后或许就再也不用在activity中写setContentView和findViewById方法了。
同样的,这样的方式也适用于Fragment,只是可能需要一些变通。但是不难的。
开源项目
Github上有一个强大开源项目RoboGuice
实现了一整套的注入功能,可以阅读它的官方WIKI进行了解:
https://github.com/roboguice/roboguice/wiki
性能
众所周知,相对于正常的方法调用,反射调用在执行效率上会有劣势,而且反射并不能得到编译时期的优化,使得性能差距更加明显。虽然现在 Android手机的平均性能比最开始要好很多,但是在有些时候,性能往往还是需要考虑的一个很重要的一点。这时候可能就会纠结便利的 @InjectView与反射带来的部分性能损耗,谁更重要。
一种更高效的InjectView – ButterKnife
这是一个开源项目,ButterKnife,官方介绍是通过AnnotationProcessor实现的View Injection, 而不是反射,性能上面不会有什么顾虑。
使用ButterKnife很简单,只需要像上面提到的那样,对View进行标注,然后在setContentView之后(或者对于Fragment在inflateView之后),调用适当的ButterKnife.inject(…)方法即可。
此外,ButterKnife的注入不依赖于反射,我们可以放心大胆的在Adapter#getView中使用:
public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder viewHolder; if (convertView == null) { convertView = layoutInflater.inflate(R.layout.element_picture_event, null); viewHolder = new ViewHolder(convertView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } //do anything with view holder } ... class ViewHolder { @InjectViews({R.id.imageView0, R.id.imageView1, R.id.imageView2, R.id.imageView3}) ImageView[] imageViews; @InjectView(R.id.nicknameView) TextView nicknameView; @InjectView(R.id.infoView) TextView infoView; @InjectView(R.id.avatarView) ImageView avatarView; @InjectView(R.id.statusView) TextView statusView; public ViewHolder(View view) { ButterKnife.inject(this, view); } }
更多的使用方法,可以参考官方说明:
http://jakewharton.github.io/butterknife/
对IDE进行配置
在IDE里面使用ButterKnife,需要对IDE进行适当的配置才可以正常的运行程序。
比如说在,IntelliJ IDEA里面,需要开启AnnotationProcessor配置。如图:
对于Eclipse,官方也给出了相应的开启AnnotationProcessor的方式,可以参考下面的链接:
http://jakewharton.github.io/butterknife/ide-eclipse.html
对于IDE的自动格式化代码,可能会强行将Annotation单独在一行显示,比如说这样:
@InjectView(R.id.ptrLayout) PullToRefreshLayout ptrLayout;
如果不喜欢上面的这种方式,可以在IDE中进行配置,比如说在IDEA中:
这样就可以保证@InjectView和定义View在一行显示了:
@InjectView(R.id.ptrLayout) PullToRefreshLayout ptrLayout;
Ant中的配置
因为使用了AnnotationProcessor,在使用一些工具,比如说ant打包的时候,可能需要一些另外的配置,比如说,对于ant,需要加入一个处理Annotation的target. 参考代码如下:
<javac encoding="UTF-8" source="1.5" target="1.5" debug="false" extdirs="" includeantruntime="false" destdir="bin/classes" bootclasspathref="project.target.class.path" verbose="false" classpathref="project.javac.classpath" fork="false"> <src path="src"/> <src path="gen"/> <compilerarg line=""/> <compilerarg line="-processorpath libs/butterknife-6.0.0.jar"/> </javac>
对于Maven或者Gradle,相信使用Annotation Processor + Maven或Gradle,就可以谷歌到相应的解决方案。
相关推荐
通过在字段上使用 @InjectView 消除 findViewById 调用。 使用@InjectViews 将列表中的多个视图分组。 使用操作、设置器或属性一次对所有这些进行操作。 通过使用 @OnClick 和其他方法注释方法来消除侦听器的匿名...
"injectView"可能就是一个类似的自定义注解,用于简化视图查找和绑定的过程,避免手动调用findViewById方法。开发者可以通过在Activity或Fragment的类成员上使用这个注解,来自动将对应的UI组件与成员变量关联。 以...
总结来说,本例子通过创建自定义注解`BindView`和`OnClick`,结合注解处理器`ViewBinderProcessor`,实现了在Android应用中简化findViewById和setOnClickListener操作的目标。这种编程模式不仅减少了手动操作,提高...
总的来说,这个"告别findViewById的Demo"展示了如何通过注解和相关库来简化Android开发中的视图查找,提高了代码的可维护性和开发效率。理解并掌握这种方法,对于提升个人的Android开发技能和项目质量有着显著的帮助...
然而,随着版本的更新,Google引入了一种新的特性——视图绑定(View Binding),它旨在简化UI元素的引用,提高代码的可读性和维护性,从而替代了传统的`findViewById()`。本文将深入探讨视图绑定的工作原理、优点...
使用这个工具类,我们可以在初始化时一次性解析整个布局,然后在需要时通过`ViewFinder.findViewById`快速获取视图,大大减少了代码量和冗余。例如: ```java ViewFinder finder = new ViewFinder(this, R.layout....
总结来说,Android注解的使用,尤其是像Butter Knife这样的库,可以极大地优化开发流程,提高效率,使代码更加整洁。通过注解绑定视图和事件,开发者可以专注于业务逻辑,而不是重复的查找和设置视图。在实际开发中...
总的来说,“注解-findViewById”是一个提高Android开发效率和代码可读性的实践,通过使用注解工具库,我们可以避免编写繁琐的视图查找代码,使我们的应用更加模块化和易于维护。开发者可以根据项目需求选择合适的...
2. **兼容性**:确保工具与不同版本的Android Studio和Gradle兼容,以及与不同的构建系统(如Ant或Maven)配合使用。 3. **性能**:如果项目包含大量布局文件,生成代码的速度不应过慢,以免影响开发效率。 4. **...
3. **代码规范**:虽然插件方便,但过度依赖可能导致代码可读性降低,因此在实际项目中,应结合编码规范和团队协作需求合理使用。 总之,FindViewHelper这类插件是Android开发者的好帮手,它们通过自动化代码生成来...
本文将深入探讨Android注入理论,以及为什么可以使用@InjectView代替findViewById()。 首先,理解依赖注入的基本原理是关键。依赖注入是一种设计模式,它允许对象在运行时获得其依赖项,而不是在构造函数或初始化...
总的来说,自动生成`FindViewById`工具是提升Android开发效率的重要辅助手段,它可以帮助开发者专注于业务逻辑,而非重复的查找代码。通过理解和熟练运用这类工具,可以显著提高代码质量和开发速度。对于初次接触的...
通过在字段上使用@InjectView消除findViewById调用。 使用@InjectViews一个列表中的多个视图@InjectViews 。 使用动作,设置器或属性一次对所有这些进行操作。 通过使用@OnClick和其他注释方法来消除侦听器的匿名...
当你的项目不再使用 ButterKnife 了,你该怎么处理已经注解...RemoveButterKnife 一个可以把 注解代码还原为 findViewById 样式的一个AndroidStudio 插件。.zip,一个android工作室插件,可以帮助移除butterknife的使用
自定义注解,实现了findViewById 和 OnCLIckListener 注解不会去新建一个OnClickListener对象,所有view都指向一个listener, 然后通过反射调用方法去执行
总之,通过实现一个简单的IOC框架,我们可以改善Android应用的代码结构,减少`findViewById`的使用,提高代码的可读性和可维护性。然而,实际项目中可能需要考虑更多的因素,比如性能优化、支持多种组件注入等,这...
需要导入jar包该工具类直接extends viewutils ,所有的findviewbyid都使用$来表示!
支持链式操作 vq id R id TextView1 text "I find hello world" ; vq id R id button1 text "find it " clicked this "onClickMethod" ;...vq id R id imageView1 image android R ...
Android中findViewById返回为空null的快速解决办法可以通过使用LayoutInflater来inflate视图控件,然后使用findViewById方法来获取视图控件,或者使用parent或root视图控件来获取想要的视图控件。同时,在自定义视图...
在 Android 开发中,我们需要注意控件的布局关系和 findViewById 的使用,以避免出现类似的错误。同时,我们也需要了解 findViewById 的工作原理和其返回值的含义,以便更好地 debug 和解决问题。