开头该怎么说呢,我这个主要是记录下自己学习的日志,所以开场白也不知道怎么说才好。
那就这样说好了,参考网上诸多代码,然后自己模仿下 ButterKnife 的@BindView注解写法
1.创建一个新的项目
2.创建一个 放 自定义注解 java module
3.创建一个 放 注解处理器的 java module
4.在 annotation 项目中 创建一个自定义的注解 取名为 BindView
package com.example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface BindView{
int value() default -1;
}
5 compile 项目 添加3个依赖,并且创建一个注解处理器 取名为MyAnnotationProcessor,其中最后一个依赖为 我们自定义注解的项目。
注解处理器 处理 自定义的注解,没毛病吧。
package com.example;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
@AutoService(Processor.class)
public class MyAnnotationProcessor extends AbstractProcessor{
private Filer mFiler;
private Messager mMessager;
private Elements mElementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
mMessager = processingEnvironment.getMessager();
mElementUtils = processingEnvironment.getElementUtils();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotations = new LinkedHashSet<>();
annotations.add(BindView.class.getCanonicalName());
return annotations;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> bindViewElements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
note("bindViewElements.size:"+bindViewElements.size());
if(bindViewElements==null || bindViewElements.size() == 0){
//这里说一下返回的意义,如果返回false 表示 还有其他的注解处理器 也可以处理这个注解 true表示 其他的注解处理器 不需要再次处理了
return false;
}
//先分类,比如这些 @BindView注解 是在哪些类中使用的 有可能在 AxxxActivty,BxxxActivty,CxxxActivty
//到时候会生成辅助类 BindAxxxActivty,BindBxxxActivty,BindCxxxActivty,这个辅助类的名字是我们自己随便定义的
Map<TypeElement,Set<Element>> groupMap =new HashMap<>();//这个是用来存放 每个类,对应的所有@BindView注解
for (Element element : bindViewElements){
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
Set<Element> elements = groupMap.get(enclosingElement);
if(elements==null){
elements = new HashSet<>();
groupMap.put(enclosingElement,elements);
}
elements.add(element);
}
//分好组之后,就是生成具体辅助类的代码了
for (TypeElement typeElement : groupMap.keySet()){
// 1.获取使用注解的地方 所在的包名 com.xxx.xxx.
PackageElement packageElement = mElementUtils.getPackageOf(typeElement);
String pkName = packageElement.getQualifiedName().toString();
note("所在的包名:"+pkName);
//2.获取包装类类型,即获取使用注解所在类的全类名, com.xxx.xxx.MyClass
String enclosingName = typeElement.getQualifiedName().toString();
note("所在的类全限定名:"+enclosingName);
//添加辅助类的方法
MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("bindView")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(ClassName.get(typeElement.asType()),"activity")
.returns(TypeName.VOID);
Set<Element> elements = groupMap.get(typeElement);
for (Element element : elements){
//因为BindView只作用于filed,所以这里可直接进行强转
VariableElement bindViewElement = (VariableElement) element;
//3.获取注解的成员变量名
String bindViewFiledName = bindViewElement.getSimpleName().toString();
note("变量名:"+bindViewFiledName);
//4.获取注解的成员变量类型
String bindViewFiledClassType = bindViewElement.asType().toString();
note("变量名类型:"+bindViewFiledClassType);
//5.获取注解元数据,就是注解的值 比如:@BindView(R.id.test) 中的 R.id.test
BindView bindView = element.getAnnotation(BindView.class);
int id = -1;
if(bindView!=null){
id = bindView.value();
}
note("注解的值:"+id);
//主要是这句,往方法里面添加代码
methodSpecBuilder .addStatement(String.format("activity.%s = (%s)activity.findViewById(%d)",bindViewFiledName,bindViewFiledClassType,id));
}
TypeSpec typeSpec = TypeSpec.classBuilder("Bind"+typeElement.getSimpleName().toString())
//.superclass(TypeName.get(typeElement.getSuperclass()))
.addModifiers(Modifier.FINAL,Modifier.PUBLIC)
.addMethod(methodSpecBuilder.build())
.build();
JavaFile file = JavaFile.builder(pkName,typeSpec).build();
try {
file.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
/**
* 在Gradle Console 控制台可以看到日志
* @param msg
*/
private void note(String msg) {
mMessager.printMessage(Diagnostic.Kind.NOTE, msg);
}
}
5.弄完java module 后 ,再来看看 如何使用, 在app配置文件 添加一些依赖
6.依赖添加完后 就正常 编写 布局文件 然后在 Activity使用,当在多个Activity使用@BindView注解的时候,会产生多个辅助类文件
下面这些就是编译期产生的辅助类文件 跟原文件比就多了个 Bind前缀,这个我们可以自己随便定义的
看到黑色箭头没有,黑色箭头的代码不一样。黑色箭头 指向的类就是编译期 产生的辅助文件,这里我只有2个activity使用的@BindView注解,所以产生了BindMainActivity类和 BindLoginActivtiy类,并且在调用的时候 得使用对应的辅助类才行,比如 MainActivity 就得调用BindMainActivty.binView,LoginActivtiy就的 调用 BindLoginActivtiy.bindView 。如果有很N个类 使用@BindView注解的话 就得 使用N个辅助类对应的 bingView方法了,这个不太好,下一篇日志我会改造下
测试的结果:
已经赋值上去了,成功~~~~~~~~~·