文章目录
butterknife Github地址:https://github.com/JakeWharton/butterknife
1 butterknife简单使用
在app的build.gradle中添加依赖:
implementation 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
使用如下:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv_text)
TextView tvText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
tvText.setText("被设置了");
}
@OnClick(R.id.btn_submit)
void submit() {
Toast.makeText(MainActivity.this,"点击了按钮",Toast.LENGTH_SHORT).show();
}
}
MakeProject后,打开如下文件,说明butterknife在编译时自动为我们生成了代码。
我们简单看下源码
在Utils中的133行可以看出,最后还是调用了findViewById方法。
2 手写实现
在这之前可以参照以下文章:
-
Android注解处理器(APT)简单实例:https://blog.csdn.net/hongxue8888/article/details/99820719
-
javapoet:
https://blog.csdn.net/hongxue8888/article/details/99737289 -
Java 注解与注解处理器:
https://blog.okclouder.cn/2019/09/08/java-annotation/ -
Android编译时注解APT实战(AbstractProcessor)
https://www.jianshu.com/p/07ef8ba80562 -
在线文档-jdk-zh :
http://tool.oschina.net/apidocs/apidoc?api=jdk-zh
要实现的结构图如下:
2.1 annotations
新建一个名为annotations的Java Library,用于存放需要使用的注解
2.1.1 BindView
添加注解BindView:
@Target(ElementType.FIELD) //声明我们定义的注解是放在什么上面的 也就是作用域
@Retention(RetentionPolicy.SOURCE) //声明我们定义的注解的生命周期 java--->class-->run
public @interface BindView {
int value();
}
2.2 annotation_compiler
新建一个名为annotation_compiler的Java Library,里面添加注解处理器,用于在编译期自动生成我们需要的代码。
2.2.1 注册注解处理器
在 processor 的 main 目录下新建 resources 目录,然后添加一个 META-INF/services 目录。
在 META-INF/services 目录下新建一个名叫 javax.annotation.processing.Processor 的文件。
文件内容如下:
com.hongx.annotation_compiler.AnnotationCompiler
2.2.2 使用AutoService注册
如果觉得上一步的注册 service 步骤麻烦,可以使用 google 的 auto service 自动注册。
在annotation_compiler的build.gradle配置如下:
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 注册我们的注解处理器
// implementation 'com.google.auto.service:auto-service:1.0-rc3'
// As-3.4.1 + gradle5.1.1-all + auto-service:1.0-rc4
annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'//1
compileOnly'com.google.auto.service:auto-service:1.0-rc4'
implementation project(path: ':annotations')//2
}
sourceCompatibility = "7"
targetCompatibility = "7"
注释1:auto-service用于注册注解处理器
注释2:添加annotations依赖
auto-service使用只需在添加
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
...
}
Make Project后会自动生成javax.annotation.processing.Processor 文件,如下图:
2.2.3 AnnotationCompiler
具体的代码如下:
/**
* 这个类就是APT
*/
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
//1.定义一个用于生成文件的对象
Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filer = processingEnvironment.getFiler();
}
//2.需要确定当前APT处理所有模块中哪些注解
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindView.class.getCanonicalName());
// types.add(Override.class.getCanonicalName());
return types;
}
//3.支持的JDK的版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 在这个方法中,我们去生成IBinder的实现类
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//-------------------------注释1 开始------------------//
//得到程序中所有写了BindView注解的元素的集合
//类元素(TypeElement)
//可执行元素(ExecutableElement)
//属性元素(VariableElement)
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);
//定义一个MAP用来分类
Map<String, List<VariableElement>> map = new HashMap<>();
//开始分类存入MAP中
for (Element element : elementsAnnotatedWith) {
VariableElement variableElement = (VariableElement) element;
//获取activity的名字
String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
List<VariableElement> elementList = map.get(activityName);
if (elementList == null) {
elementList = new ArrayList<>();
map.put(activityName, elementList);
}
elementList.add(variableElement);
}
//运行到这就已经完成了分类工作
//-------------------------注释1结束------------------//
if (map.size() > 0) {
//开始写入文件
Writer writer = null;
//每一个activity都要生成一个对应的文件
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String activityName = iterator.next();
List<VariableElement> elementList = map.get(activityName);
//获取包名
TypeElement enclosingElement = (TypeElement) elementList.get(0).getEnclosingElement();
String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();
try {
//生成文件
//包名.MainActivity_ViewBinding
JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + activityName + "_ViewBinding");
writer = sourceFile.openWriter();
// package com.example.dn_butterknife;
writer.write("package " + packageName + ";\n\n");
// import com.example.dn_butterknife.IBinder;
writer.write("import " + packageName + ".IBinder;\n\n");
// public class MainActivity_ViewBinding implements IBinder<com.example.dn_butterknife.MainActivity>{
writer.write("public class " + activityName + "_ViewBinding implements IBinder<"
+ packageName + "." + activityName + ">{\n\n");
// @Override
writer.write(" @Override\n");
// public void bind(com.example.dn_butterknife.MainActivity target) {
writer.write(" public void bind(" + packageName + "." + activityName + " target){\n\n");
// target.tvText=(android.widget.TextView)target.findViewById(2131165325);
for (VariableElement variableElement : elementList) {
//获取控件的名字
String variableName = variableElement.getSimpleName().toString();
//获取ID
int id = variableElement.getAnnotation(BindView.class).value();
//获取控件的类型
TypeMirror typeMirror = variableElement.asType();
writer.write(" target." + variableName + "=(" + typeMirror + ")target.findViewById(" + id + ");\n");
}
writer.write("\n}}");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return false;
}
}
init 被注解处理工具调用,并输入 processingEnvironment 参数。processingEnvironment 提供了很多工具类,如 Elements、Types、Filer 和 Messenger 等。
process 注解处理的主函数,这里处理扫描、评估和处理注解的代码,以及生产 Java 文件。
getSupportedAnnotationTypes 指明注解处理器是处理哪些注解的。
getSupportedSourceVersion 指明使用的 Java 版本,通常返回 SourceVersion.latestSupported。
下面解释下注释1代码块:
elementsAnnotatedWith包含了所有Activity中的BindView注解,所以我们使用一个Map对这些注解进行分类,而Map中的键就是activity名,值就是BindView注解的集合,原理如下图所示:
Elements:一个用来处理Element的工具类,源代码的每一个部分都是一个特定类型的Element,例如:
package com.example; // PackageElement
public class Foo { // TypeElement
private int a; // VariableElement
private Foo other; // VariableElement
public Foo () {} // ExecuteableElement
public void setA ( // ExecuteableElement
int newA // TypeElement
) {}
}
再解释下下面这句代码
String activityName = variableElement.getEnclosingElement().
getSimpleName().toString();
variableElement是属性元素,getEnclosingElement获取它外层的元素,也就是Activity。
具体api文档如下:
2.2.4 添加配置到app中
在app的build.gradle中配置如下:
implementation project(path: ':annotations')//1
annotationProcessor project(path: ':annotation_compiler')//2
注释1:添加annotations依赖。
注释2:annotation_compiler作为一个注解处理器添加到build.gradle中。
2.3 生成代码
新建IBinder接口
public interface IBinder<T> {
void bind(T target);
}
待会会实现这个接口,用于绑定activity
在MainActivity中使用BindView注解,如下:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tvText)
TextView textView;
...
}
MakeProject后生成如下图代码:
代码如下:
package com.hongx.hxbutterknife;
import com.hongx.hxbutterknife.IBinder;
public class MainActivity_ViewBinding implements IBinder<com.hongx.hxbutterknife.MainActivity>{
@Override
public void bind(com.hongx.hxbutterknife.MainActivity target){
target.tvText=(android.widget.TextView)target.findViewById(2131165326);
}}
接下来需要通过IBinder的bind方法,绑定activity:
public class HxButterKnife {
public static void bind(Activity activity) {
String name = activity.getClass().getName() + "_ViewBinding";
try {
Class<?> aClass = Class.forName(name);
IBinder iBinder = (IBinder) aClass.newInstance();
iBinder.bind(activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.4 使用
在MainActivity的onCreate方法中绑定activity
import com.hongx.annotations.BindView;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv_text)
TextView tvText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HxButterKnife.bind(this);
tvText.setText("被设置了");
}
}
2.5 JavaPoet
参考:
https://github.com/square/javapoet
https://square.github.io/javapoet/1.x/javapoet/
JavaPoet - 优雅地生成代码:
https://www.jianshu.com/p/fba2eec47976
JavaPoet 看这一篇就够了:
https://juejin.im/entry/58fefebf8d6d810058a610de
以上的连接字符串工作是比较繁琐的,我们可以使用JavaPoet开源库进行编写,提升效率。
JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。这个框架功能非常有用,我们可以很方便的使用它根据注解、数据库模式、协议格式等来对应生成代码。通过这种自动化生成代码的方式,可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作。
项目主页及源码:https://github.com/square/javapoet
2.5.1 万能例子
参考:
JavaPoet 看这一篇就够了:
https://juejin.im/entry/58fefebf8d6d810058a610de
展示一段几乎涵盖你所常见 case 的例子 (仅为了展示 JavaPoet 用法, 生成的代码可能编译不过). 如果哪里不知道怎么生成的, 可以方便的在下面的生成代码里查找生成方法。
在annotation_compiler下新建CodeGenerate,注意:只能在JavaLibrary下使用。
package com.hongx.annotation_compiler;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import com.squareup.javapoet.WildcardTypeName;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.Modifier;
/**
* @author: fuchenming
* @create: 2019-09-10 10:23
*/
public class CodeGenerate {
public static void main(String[] args) {
TypeSpec clazz = clazz(builtinTypeField(), // int
arrayTypeField(), // int[]
refTypeField(), // File
typeField(), // T
parameterizedTypeField(), // List<String>
wildcardTypeField(), // List<? extends String>
constructor(), // 构造函数
method(code())); // 普通方法
JavaFile javaFile = JavaFile.builder("com.hongx.javapoet", clazz).build();
System.out.println(javaFile.toString());
}
/**
* `public abstract class Clazz<T> extends String implements Serializable, Comparable<String>, Comparable<? extends String> {
* ...
* }`
*
* @return
*/
public static TypeSpec clazz(FieldSpec builtinTypeField, FieldSpec arrayTypeField, FieldSpec refTypeField,
FieldSpec typeField, FieldSpec parameterizedTypeField, FieldSpec wildcardTypeField,
MethodSpec constructor, MethodSpec methodSpec) {
return TypeSpec.classBuilder("Clazz")
// 限定符
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
// 泛型
.addTypeVariable(TypeVariableName.get("T"))
// 继承与接口
.superclass(String.class)
.addSuperinterface(Serializable.class)
.addSuperinterface(ParameterizedTypeName.get(Comparable.class, String.class))
.addSuperinterface(ParameterizedTypeName.get(ClassName.get(Map.class),
TypeVariableName.get("T"),
WildcardTypeName.subtypeOf(String.class)))
// 初始化块
.addStaticBlock(CodeBlock.builder().build())
.addInitializerBlock(CodeBlock.builder().build())
// 属性
.addField(builtinTypeField)
.addField(arrayTypeField)
.addField(refTypeField)
.addField(typeField)
.addField(parameterizedTypeField)
.addField(wildcardTypeField)
// 方法 (构造函数也在此定义)
.addMethod(constructor)
.addMethod(methodSpec)
// 内部类
.addType(TypeSpec.classBuilder("InnerClass").build())
.build();
}
/**
* 内置类型
*/
public static FieldSpec builtinTypeField() {
// private int mInt;
return FieldSpec.builder(int.class, "mInt", Modifier.PRIVATE).build();
}
/**
* 数组类型
*/
public static FieldSpec arrayTypeField() {
// private int[] mArr;
return FieldSpec.builder(int[].class, "mArr", Modifier.PRIVATE).build();
}
/**
* 需要导入 import 的类型
*/
public static FieldSpec refTypeField() {
// private File mRef;
return FieldSpec.builder(File.class, "mRef", Modifier.PRIVATE).build();
}
/**
* 泛型
*/
public static FieldSpec typeField() {
// private File mT;
return FieldSpec.builder(TypeVariableName.get("T"), "mT", Modifier.PRIVATE).build();
}
/**
* 参数化类型
*/
public static FieldSpec parameterizedTypeField() {
// private List<String> mParameterizedField;
return FieldSpec.builder(ParameterizedTypeName.get(List.class, String.class),
"mParameterizedField",
Modifier.PRIVATE)
.build();
}
/**
* 通配符参数化类型
*
* @return
*/
public static FieldSpec wildcardTypeField() {
// private List<? extends String> mWildcardField;
return FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(List.class),
WildcardTypeName.subtypeOf(String.class)),
"mWildcardField",
Modifier.PRIVATE)
.build();
}
/**
* 构造函数
*/
public static MethodSpec constructor() {
return MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.build();
}
/**
* `@Override
* public <T> Integer method(String string, T t, Map<Integer, ? extends T> map) throws IOException, RuntimeException {
* ...
* }`
*
* @param codeBlock
* @return
*/
public static MethodSpec method(CodeBlock codeBlock) {
return MethodSpec.methodBuilder("method")
.addAnnotation(Override.class)
.addTypeVariable(TypeVariableName.get("T"))
.addModifiers(Modifier.PUBLIC)
.returns(int.class)
.addParameter(String.class, "string")
.addParameter(TypeVariableName.get("T"), "t")
.addParameter(ParameterizedTypeName.get(ClassName.get(Map.class),
ClassName.get(Integer.class),
WildcardTypeName.subtypeOf(TypeVariableName.get("T"))),
"map")
.addException(IOException.class)
.addException(RuntimeException.class)
.addCode(codeBlock)
.build();
}
/**
* ‘method’ 方法中的具体语句
*/
public static CodeBlock code() {
return CodeBlock.builder()
.addStatement("int foo = 1")
.addStatement("$T bar = $S", String.class, "a string")
// Object obj = new HashMap<Integer, ? extends T>(5);
.addStatement("$T obj = new $T(5)",
Object.class, ParameterizedTypeName.get(ClassName.get(HashMap.class),
ClassName.get(Integer.class),
WildcardTypeName.subtypeOf(TypeVariableName.get("T"))))
// method(new Runnable(String param) {
// @Override
// void run() {
// }
// });
.addStatement("baz($L)", TypeSpec.anonymousClassBuilder("$T param", String.class)
.superclass(Runnable.class)
.addMethod(MethodSpec.methodBuilder("run")
.addAnnotation(Override.class)
.returns(TypeName.VOID)
.build())
.build())
// for
.beginControlFlow("for (int i = 0; i < 5; i++)")
.endControlFlow()
// while
.beginControlFlow("while (false)")
.endControlFlow()
// do... while
.beginControlFlow("do")
.endControlFlow("while (false)")
// if... else if... else...
.beginControlFlow("if (false)")
.nextControlFlow("else if (false)")
.nextControlFlow("else")
.endControlFlow()
// try... catch... finally
.beginControlFlow("try")
.nextControlFlow("catch ($T e)", Exception.class)
.addStatement("e.printStackTrace()")
.nextControlFlow("finally")
.endControlFlow()
.addStatement("return 0")
.build();
}
}
右键-Run CodeGenerate.main() 后 查看输出:
package com.hongx.javapoet;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.Comparable;
import java.lang.Exception;
import java.lang.Integer;
import java.lang.Object;
import java.lang.Override;
import java.lang.Runnable;
import java.lang.RuntimeException;
import java.lang.String;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class Clazz<T> extends String implements Serializable, Comparable<String>, Map<T, ? extends String> {
static {
}
private int mInt;
private int[] mArr;
private File mRef;
private T mT;
private List<String> mParameterizedField;
private List<? extends String> mWildcardField;
{
}
public Clazz() {
}
@Override
public <T> int method(String string, T t, Map<Integer, ? extends T> map) throws IOException,
RuntimeException {
int foo = 1;
String bar = "a string";
Object obj = new HashMap<Integer, ? extends T>(5);
baz(new Runnable(String param) {
@Override
void run() {
}
});
for (int i = 0; i < 5; i++) {
}
while (false) {
}
do {
} while (false);
if (false) {
} else if (false) {
} else {
}
try {
} catch (Exception e) {
e.printStackTrace();
} finally {
}
return 0;
}
class InnerClass {
}
}
2.5.2 使用JavaPoet实现
如果想调试注解处理器,可以参考这篇文章:
AndroidStudio调试(debug)注解处理器(AnnotationProcessor):
https://blog.csdn.net/hongxue8888/article/details/99710884
先看使用,在MainActivity和SecondActivity都添加了两个注解
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv_text)
TextView tvText;
@BindView(R.id.tv_text2)
TextView tvText2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HxButterKnife.bind(this);
tvText.setText("被设置了");
tvText2.setText("被设置了2");
}
public void toSecond(View view) {
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
}
}
public class SecondActivity extends AppCompatActivity {
@BindView(R.id.tv_text3)
TextView tvText3;
@BindView(R.id.tv_text4)
TextView tvText4;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
HxButterKnife.bind(this);
tvText3.setText("被设置了3");
tvText4.setText("被设置了4");
}
}
查看注解处理器生成的代码:
package com.hongx.hxbutterknife;
import android.widget.TextView;
import java.lang.Override;
public class MainActivity_ViewBinding2 implements IBinder<MainActivity> {
@Override
public void bind(MainActivity target) {
target.tvText = (TextView)target.findViewById(2131165326);
target.tvText2 = (TextView)target.findViewById(2131165327);
}
}
package com.hongx.hxbutterknife;
import android.widget.TextView;
import java.lang.Override;
public class SecondActivity_ViewBinding2 implements IBinder<SecondActivity> {
@Override
public void bind(SecondActivity target) {
target.tvText3 = (TextView)target.findViewById(2131165328);
target.tvText4 = (TextView)target.findViewById(2131165329);
}
}
具体实现代码如下:
package com.hongx.annotation_compiler;
import com.google.auto.service.AutoService;
import com.hongx.annotations.BindView;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
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.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
/**
* 这个类就是APT
*/
@AutoService(Processor.class)
public class AnnotationCompiler2 extends AbstractProcessor {
//1.定义一个用于生成文件的对象
Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filer = processingEnvironment.getFiler();
}
//2.需要确定当前APT处理所有模块中哪些注解
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindView.class.getCanonicalName());
return types;
}
//3.支持的JDK的版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 在这个方法中,我们去生成IBinder的实现类
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//-------------------------注释1 开始------------------//
//得到程序中所有写了BindView注解的元素的集合
//类元素(TypeElement)
//可执行元素(ExecutableElement)
//属性元素(VariableElement)
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);
//定义一个MAP用来分类
Map<String, List<VariableElement>> map = new HashMap<>();
//开始分类存入MAP中
for (Element element : elementsAnnotatedWith) {
VariableElement variableElement = (VariableElement) element;
//获取activity的名字
String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
List<VariableElement> elementList = map.get(activityName);
if (elementList == null) {
elementList = new ArrayList<>();
map.put(activityName, elementList);
}
elementList.add(variableElement);
}
//运行到这就已经完成了分类工作
//-------------------------注释1结束------------------//
if (map.size() > 0) {
// package com.hongx.hxbutterknife;
//
// import com.hongx.hxbutterknife.IBinder;
//
// public class MainActivity_ViewBinding implements IBinder<com.hongx.hxbutterknife.MainActivity>{
//
// @Override
// public void bind(com.hongx.hxbutterknife.MainActivity target){
//
// target.tvText=(android.widget.TextView)target.findViewById(2131165326);
//
// }
//
// }
//每一个activity都要生成一个对应的文件
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String activityName = iterator.next();
List<VariableElement> elementList = map.get(activityName);
//获取包名
TypeElement enclosingElement = (TypeElement) elementList.get(0).getEnclosingElement();
String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();
List<CodeBlock> codeBlockList = new ArrayList<>();
for (VariableElement variableElement : elementList) {
//获取控件的名字
String variableName = variableElement.getSimpleName().toString();
//获取ID
int resourceId = variableElement.getAnnotation(BindView.class).value();
//获取控件的类型
TypeMirror typeMirror = variableElement.asType();
CodeBlock singleCodeBlock = codeBlock(variableName, typeMirror, resourceId);
codeBlockList.add(singleCodeBlock);
}
CodeBlock allCodeBlock = getAllBlock(codeBlockList);
/**
* $L $S $T $N 都是占位符。
*
* $L:常量
* $S:String类型
* $T:变量指定类型,可以通过ClassName来指定外部类名
* $N:生成的方法名或者变量名
*/
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("bind")
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
// 重写的方法
.addAnnotation(Override.class)
.addParameter(ClassName.get(packageName, activityName), "target")
.addCode(allCodeBlock);
ClassName IBinderClass = ClassName.get("com.hongx.hxbutterknife", "IBinder");
ClassName superinterface = ClassName.bestGuess(ClassName.get(packageName, activityName).toString());
TypeSpec activityClass = TypeSpec.classBuilder(activityName + "_ViewBinding2")
.addModifiers(Modifier.PUBLIC)
// 添加接口,ParameterizedTypeName的参数1是接口,参数2是接口的泛型
.addSuperinterface(ParameterizedTypeName.get(IBinderClass, superinterface))
.addMethod(methodBuilder.build())
.build();
JavaFile javaFile = JavaFile.builder(packageName, activityClass).build();
try {
javaFile.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
/**
* 生成多条代码
*/
public static CodeBlock getAllBlock(List<CodeBlock> codeBlockList) {
CodeBlock.Builder codeBlockBuilder = CodeBlock.builder();
for (CodeBlock codeBlock : codeBlockList) {
codeBlockBuilder.add(codeBlock);
}
return codeBlockBuilder.build();
}
public static CodeBlock codeBlock(String variableName, TypeMirror typeMirror, int resourceId) {
CodeBlock.Builder codeBlockBuilder = CodeBlock.builder();
CodeBlock codeBlock = CodeBlock.builder()
.addStatement("$N.$L = ($T)target.findViewById($L)", "target", variableName, typeMirror, resourceId)
.build();
codeBlockBuilder.add(codeBlock);
return codeBlockBuilder.build();
}
}
从生成的代码中可以看出bind中会有多条代码,如下图所示,所以我们可以在getAllBlock()方法实现多条代码。
代码Github地址:https://github.com/345166018/AndroidIOC/tree/master/HxButterknife