为什么使用?
1. 方便处理adapter里的viewHolder的绑定问题
2. 简化代码,节省开发时间(代替android中对view的相关操作,减少大量的findViewById和setClickListener)
3. 不影响运行性能和效率
4. 代码清晰,可读性强
原理:
利用注解和注解处理器针对每个类生成相对应的类,通过butterknife.bind(this)绑定这个类,通过反射机制操作类里的方法和变量
注解:相当于一个标记
注解处理器:相当于大脑
前期准备:
使用butterknife:
implementation 'com.jackewharton:butterknife:9.0.0' 申明注解库
annotationProcessor 'com.jackwharton:butterknife-compiler:9.0.0' 申明注解处理器
自己写注解器:
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4' 注解处理器申明(编译时运行)
implementation 'com.google.auto.service:1.0-rc4'
申明java版本
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
添加插件:
dependencies {
classpath 'com.jakewharton:butterknife-gradle-plugin:9.0.0'
}
简单说明:
绑定:Butterknife.bind(this)
右键可以一键生成(需要添加插件:android butterknife zelezry),使用的时候鼠标需要停留在layoutView上(Generate - Generate Butterknife injections)
可以绑定所有的source资源:@BindString(R.string.name) String appName;
使用:
重点需要注册这个类是一个注解处理器:@AutoService(Processor.class)
继承AbstractProcessor类实现相关方法进行相应处理操作
1. 初始化工作(生成一个java文件)
//生成文件对象
Filter filter;
* 实现init方法,参数是processingEnvironment(初始化数据)
filter = processingEnvironment.getFilter();
* 实现getSupportedAnnotationTypes(说明注解处理器要处理哪些注解)
Set<String> types = new HashSet<>();
types.add(BindView.class.getCanonicalName());//BindView是自己定义的注解名称
最后return这个types
* 实现getSupportedSourceVersion(申明当前注解支持的java版本)
// 直接return一个sourceVersion对象
return processingEnv.getSourceVersion() -- processingEnv是父类成员变量
* 实现process(写文件 - 包含俩个参数set结合和RoundEnviroment)
说明:可以读取三种类型节点
1. 类节点 - TypeElement
2. 方法节点 - ExecutableElement
3. 变量节点 - VariableElement
//获取模块中用到BindView的成员变量节点集合(指App中所有activity控件)
Set<? extends Element> elementsAnnotateWith = roundEnvironment.getElementAnnotatesWith(接口类.class);
//遍历文件进行Activity分离
Map<String,List<VeriableElement>> map = new HashMap();
/***额外的说明,注解处理器可以做很多事情,因为可以拿到类对象******
Class class = variableElement.getEnclosingElement().getClass();
Filed[] = class.getDeclaredFields();//拿到类里所有变量
Method[] class.getDeclaredMethods();//拿到类里所有方法
*****************************************************************/
for(Element element: elementsAnnotatedWith){
VariableElement variableElement = (VariableElement)element;
//获取类节点
String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
List<VariableElement> variableElementList = map.get(activityName);
if(variableElementList == null){
variableElementList = new ArrayList();
map.put(activityName,variableElementList);
}
variableElementList.add(variableElement);
}
//开始写文件
if(map.size() > 0){
Write write = null;
Iterator<String> iterator = map.ketSet().iterator();
while(iterator.hasNext()){
String activityName = iterator.next();
//获取到activity所对应的集合
List<VariableElement> variableElementList = map.get(activityName);
//获取父节点 - 类节点
TypeElement enclosingElement= (TypeElement)variableEElementList.getEnclosingElement();
//通过成员变量到的包名
String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();
JavaFileObject sourceFile = filer.createSourceFile(packageName+"."+activityName+"_ViewBinding");
writer = sourceFile.openWriter();
writer.write("package "+packageName+";\n");//写类的第一句,包名
writer.write("import "+packageName+".IBinder;\n");//import引入包名
writer.write("public class "+activityName+"_ViewBinding implements
IBinder<"+packageName+"."+activityName+">{\n");
writer.write("@OverRide\n public void bind("+packageName+"."+activityName+" target){");
//遍历所有的控件
for(VariableElement variableElement:variableElements){
//获取到控件名字
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}\n");
}
}
最后在finally中
if(writer != null){
try(){
writer.close();
}catch(IOException e){
}
}
后续使用:
创建一个类(该类要实现接口类)
实现bind(){
}
类(java类执行注解类)
public static void bind(Activity activity){
activity.getClass().getName()+"_ViewBinding";
Class<T> aClass = Class.forName(name);
IBind iBinder = aClass.newInstance();
iBinder.bind(activity);
}
接口类<T>(把当前activity传进去)
void bind(T target);//可能是activity、fragment、service等