注解扫盲和自定义注解
带你从头到尾玩转注解
这两篇是我之前学习的时候看的博客,作者整理的很详细了。
我为了加深印象,自己又整理了个脑图,喜欢看图的可以看看本人整理脑图,个人喜欢看图说话,更直观一点。
编译时注解和运行时注解的区别
在现在市面上大多数的注解举例的时候,都是参照ButterKnife,然后告诉你怎么通过注解实现setContentView和FindViewByID。使用的呢也是两种方法:
- 运行时注解(借助反射机制实现)
- 编译时注解(APT-Annotation Processor Tool)
看着好像不管哪一种都可以,那么具体的实际运用中我们应该怎么选呢?
如果你有自己实现一遍你应该有一个直观的感受:
- 反射只能使用本身已有的方法。因为反射的本质就是:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
- 而APT是生成我们自己的方法,可以做到无中生有。
- 所以说:APT > 反射。
JavaPoet导包import遇到的问题
官方文档
官方文档写的很清楚,它的定义是:JavaPoet is a Java API for generating .java source files。
这里说一下我遇到的问题:
当我想要实现自定义的ButterKnife时,我可以很简单的实现findViewById。但是我需要给View添加点击事件的时候遇到了问题:
//添加findviewByID的方法
bindViewMethodSpec.addStatement(
String.format("targetActivity.%s = (%s) targetActivity.findViewById(%s)"
, childElement.getSimpleName()
, ClassName.get(childElement.asType()).toString()
, bindView.id()));
通过上面的代码,我们很自然的想到,那我添加点击事件不就是:
bindViewMethodSpec.addStatement(
String.format("targetActivity.%s.setOnClickListener(targetActivity)"
, childElement.getSimpleName()))
但是我没有,我使用的办法是:
bindViewMethodSpec.addStatement(
String.format("targetActivity.%s.setOnClickListener((View.OnClickListener)targetActivity)"
, childElement.getSimpleName()));
哈哈,结果编译不过去(智障),原因是没有View的import:
程序包View不存在
所以这里已经不是我的方法智障不智障的问题了,而是我们怎么通过Javapoet导包的问题了。
我查看了Javapoet的文档,参考了它的导包,总结出来两种导包的方法和三种解决这个问题的方法
第一种:
ClassName arrayList3 = ClassName.get("android.view", "View");
bindViewMethodSpec.addStatement(
String.format("targetActivity.%s.setOnClickListener(( %s.OnClickListener)targetActivity)"
, childElement.getSimpleName()
, arrayList3)
);
第二种:
ClassName arrayList3 = ClassName.get("android.view", "View");
bindViewMethodSpec.addStatement(
String.format("targetActivity.%s.setOnClickListener(( $T.OnClickListener)targetActivity)"
, childElement.getSimpleName())
, arrayList3
);
这两种是很相似的办法,只不过一个的通过String.format来拼接,一个是通过addStatement拼接。
下面这个就是我说的不需要导包解决的方法:
第三种:
bindViewMethodSpec.addStatement(
String.format("targetActivity.%s.setOnClickListener((android.view.View.OnClickListener)targetActivity)"
, childElement.getSimpleName()));
是的,你没有看错,就是全路径!!!
在这里贴出官方的方法,说实话我看的第一遍是没找到方法的,我是试了各种办法都不行的时候,最先想到的全路径的写法,而且百度的时候也没有具体的写法。在第二天又看的时候才看到,可能是当时没有静下心吧。。。
ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");
ClassName list = ClassName.get("java.util", "List");
ClassName arrayList = ClassName.get("java.util", "ArrayList");
TypeName listOfHoverboards = ParameterizedTypeName.get(list, hoverboard);
MethodSpec beyond = MethodSpec.methodBuilder("beyond")
.returns(listOfHoverboards)
//下面这行是重点,在之前我一直以为是上面的return,然后跟ParameterizedTypeName.get方法死磕不出来才看到的下面。
.addStatement("$T result = new $T<>()", listOfHoverboards, arrayList)
.addStatement("result.add(new $T())", hoverboard)
.addStatement("result.add(new $T())", hoverboard)
.addStatement("result.add(new $T())", hoverboard)
.addStatement("return result")
.build();