使用InjectView和findViewById说拜拜

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

Java代码   收藏代码
  1. @Retention(RetentionPolicy.RUNTIME)  
  2. @Target({ElementType.FIELD})  
  3. public @interface InjectView {  
  4.     int value();  
  5. }  

 

测试Annotation

Java代码   收藏代码
  1. @InjectView(R.id.aView) View aView;  

 Annotation就是一个标识,标识出aView是InjectView关心的字段。

进行注入

Java代码   收藏代码
  1. Field[] declaredFields = getClass().getDeclaredFields();  
  2. for (Field field : declaredFields) {  
  3.     InjectView annotation = field.getAnnotation(InjectView.class);  
  4.     if (annotation != null) {  
  5.         int id = annotation.value();  
  6.         View view = findViewById(id);  
  7.         field.setAccessible(true);  
  8.         try {  
  9.             field.set(this, view);  
  10.         } catch (IllegalAccessException e) {  
  11.             e.printStackTrace();  
  12.         }  
  13.     }  
  14. }  


在setContentView之后,我们对所有的field检查是否包含@InjectView标识,对包含@InjectView标识的field,进行赋值。

通过同样的方式,我们甚至可以把setContentView省去,也通过注入来实现。假设我们有一个@InjectLayout的Annotation,我们通过这个Annotation来标识setContentView对应的id.

Java代码   收藏代码
  1. @InjectLayout(R.layout.activity_main)  
  2. public class MainActivity extends FragmentActivity {  
  3.   InjectLayout annotation = getClass().getAnnotation(InjectLayout.class);  
  4.   int id = annotation.value();  
  5.  try {  
  6.     Method setContentView = getClass().getMethod("setContentView"int.class);  
  7.     setContentView.invoke(this, id);  
  8.  } catch (Exception e) {  
  9.     e.printStackTrace();  
  10. }  

 

把上面的这些代码封装到BaseActivity中,之后或许就再也不用在activity中写setContentView和findViewById方法了。

同样的,这样的方式也适用于Fragment,只是可能需要一些变通。但是不难的。

开源项目

Github上有一个强大开源项目RoboGuice实现了一整套的注入功能,可以阅读它的官方WIKI进行了解

性能

众所周知,相对于正常的方法调用,反射调用在执行效率上会有劣势,而且反射并不能得到编译时期的优化,使得性能差距更加明显。虽然现在 Android手机的平均性能比最开始要好很多,但是在有些时候,性能往往还是需要考虑的一个很重要的一点。这时候可能就会纠结便利的 @InjectView与反射带来的部分性能损耗,谁更重要。

一种更高效的InjectView – ButterKnife

这是一个开源项目,ButterKnife,官方介绍是通过AnnotationProcessor实现的View Injection, 而不是反射,性能上面不会有什么顾虑。

使用ButterKnife很简单,只需要像上面提到的那样,对View进行标注,然后在setContentView之后(或者对于Fragment在inflateView之后),调用适当的ButterKnife.inject(…)方法即可。

此外,ButterKnife的注入不依赖于反射,我们可以放心大胆的在Adapter#getView中使用:

 

Java代码   收藏代码
  1.  public View getView(int position, View convertView, ViewGroup parent) {  
  2.         final ViewHolder viewHolder;  
  3.         if (convertView == null) {  
  4.             convertView = layoutInflater.inflate(R.layout.element_picture_event, null);  
  5.             viewHolder = new ViewHolder(convertView);  
  6.             convertView.setTag(viewHolder);  
  7.         } else {  
  8.             viewHolder = (ViewHolder) convertView.getTag();  
  9.         }  
  10.         //do anything with view holder  
  11. }  
  12. ...  
  13. class ViewHolder {  
  14.     @InjectViews({R.id.imageView0, R.id.imageView1, R.id.imageView2, R.id.imageView3}) ImageView[] imageViews;  
  15.     @InjectView(R.id.nicknameView) TextView nicknameView;  
  16.     @InjectView(R.id.infoView) TextView infoView;  
  17.     @InjectView(R.id.avatarView) ImageView avatarView;  
  18.     @InjectView(R.id.statusView) TextView statusView;  
  19.     public ViewHolder(View view) {  
  20.         ButterKnife.inject(this, view);  
  21.     }  
  22. }  

 

更多的使用方法,可以参考官方说明:

伦理片 http://www.dotdy.com/

对IDE进行配置

在IDE里面使用ButterKnife,需要对IDE进行适当的配置才可以正常的运行程序。

比如说在,IntelliJ IDEA里面,需要开启AnnotationProcessor配置。如图:

image

对于Eclipse,官方也给出了相应的开启AnnotationProcessor的方式,可以参考下面的链接

对于IDE的自动格式化代码,可能会强行将Annotation单独在一行显示,比如说这样:

 

Java代码   收藏代码
  1. @InjectView(R.id.ptrLayout)  
  2. PullToRefreshLayout ptrLayout;  

 

如果不喜欢上面的这种方式,可以在IDE中进行配置,比如说在IDEA中:

image

这样就可以保证@InjectView和定义View在一行显示了:

Java代码   收藏代码
  1. @InjectView(R.id.ptrLayout) PullToRefreshLayout ptrLayout;  

 

Ant中的配置

因为使用了AnnotationProcessor,在使用一些工具,比如说ant打包的时候,可能需要一些另外的配置,比如说,对于ant,需要加入一个处理Annotation的target. 参考代码如下:

Xml代码   收藏代码
  1. <javac encoding="UTF-8"  
  2.        source="1.5" target="1.5"  
  3.        debug="false" extdirs="" includeantruntime="false"  
  4.        destdir="bin/classes"  
  5.        bootclasspathref="project.target.class.path"  
  6.        verbose="false"  
  7.        classpathref="project.javac.classpath"  
  8.        fork="false">  
  9.     <src path="src"/>  
  10.     <src path="gen"/>  
  11.     <compilerarg line=""/>  
  12.     <compilerarg line="-processorpath libs/butterknife-6.0.0.jar"/>  
  13. </javac>  

 

对于Maven或者Gradle,相信使用Annotation Processor + Maven或Gradle,就可以谷歌到相应的解决方案。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值