抛出问题:
1.Butterknife是干什么的?
2.我们如何去看Butterknife框架
3.如果我们自己写一个类似Butterknife框架,我们如何去做?
ButterKnife是一个专注于Android系统的View注入框架,可以减少大量的findViewById 以及 setOnClickListener代码,可视化一键生成。 |
解决问题:
2.首先我们要明白 butterknife-annotations ,butterknife-compiler是干什么的?
我们从butterknife-annotations点击发现:
import android.support.annotation.IdRes; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.CLASS; /** * Bind a field to the view for the specified ID. The view will automatically be cast to the field * type. * <pre><code> * {@literal @}BindView(R.id.title) TextView title; * </code></pre> */ @Retention(CLASS) @Target(FIELD) public @interface BindView { /** View ID to which the field will be bound. */ @IdRes int value(); } |
这里的注解 BideView我们会在我们的Activity 绑定控件使用到 eg:
butterknife-compiler点击我们会发现:
下面我们就具体解释他们的作用:
作用 | |
AbstractProcessor / Annotation Processor Tool (apt) | 是javac的一个工具,用来在编译时扫描和编译和处理注解(Annotation)。你可以自己定义注解和注解处理器去搞一些事情。一个注解处理器它以Java代码或者(编译过的字节码)作为输入,生成文件(通常是java文件)。这些生成的java文件不能修改,并且会同其手动编写的java代码一样会被javac编译。 |
RetentionPolicy.SOURCE | 编译之后抛弃,存活的时间在源码和编译时 |
RetentionPolicy.CLASS | 保留在编译后class文件中,但JVM将会忽略(对于 底层系统开发-开发编译过程 使用) apt中source和class都一样 |
RetentionPolicy.RUNTIME | 能够使用反射读取和使用(运行时) |
3.butterknife使用
(1).在工程目录下 的build.gradle 添加apt插件classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' |
(2). 在app 目录下的build.gradle 应用 添加apt插件apply plugin: 'com.neenbedankt.android-apt' |
(3).依赖 具有apt 处理的moudle 这里不是compile 而是aptapt project(':butterknife-compiler') |
二.具体应用
1.创建模块butterknife-annotion
2.创建模块注解处理器butterkniffe-complier
注解处理器的作用上面我们已经说过了 就是处理注解生成Java文件 和javapoet类似
@AutoService(Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_7) @SupportedAnnotationTypes("com.dongnao.butterknife_annotion.BindView") public class ButterKniffeProcess extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes() { Set<String> types = new LinkedHashSet<>(); types.add(BindView.class.getCanonicalName()); return types; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } |
@Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { // System.out.println("-----------------process-----------------------"); //VariableElement 成员遍历 Set<? extends Element> elementSet = roundEnvironment.getElementsAnnotatedWith(BindView.class); // 区分 key是activity 全类名=包名+类名 值 成员变量集合 Map<String, List<VariableElement>> cacheMap = new HashMap<>(); //MainActivity.class 标签 for (Element element : elementSet) { // 全类名=包名+类名 VariableElement variableElement = (VariableElement) element; String activityName = getActivityName(variableElement); List<VariableElement> list = cacheMap.get(activityName); if (list == null) { list = new ArrayList<>(); cacheMap.put(activityName, list); } // 分类 list.add(variableElement); System.out.println("--------->" + variableElement.getSimpleName().toString()); } // 2 为每一个activity生成 Class // Iterator iterator = cacheMap.keySet().iterator(); while (iterator.hasNext()) { //得到mainActivity名字 String activityName = (String) iterator.next(); //得到mainActivity 控件成员变量的集合 List<VariableElement> caheElements = cacheMap.get(activityName); String newActivityBinder = activityName + "$ViewBinder"; Filer filer = processingEnv.getFiler(); try { JavaFileObject javaFileObject = filer.createSourceFile(newActivityBinder); String packageName = getPackageName(caheElements.get(0)); Writer writer = javaFileObject.openWriter(); String activitySimpleName = caheElements.get(0).getEnclosingElement().getSimpleName().toString() + "$ViewBinder"; writeHeader(writer, packageName, activityName, activitySimpleName); //中间部分 for (VariableElement variableElement : caheElements) { BindView bindView = variableElement.getAnnotation(BindView.class); int id = bindView.value(); String fieldName = variableElement.getSimpleName().toString(); TypeMirror typeMirror = variableElement.asType(); // TextView writer.write("target." + fieldName + "=(" + typeMirror.toString() + ")target.findViewById(" + id + ");"); writer.write("\n"); } //结尾部分 writer.write("\n"); writer.write("}"); writer.write("\n"); writer.write("}"); writer.close(); } catch (IOException e) { e.printStackTrace(); } } return false; } Element: //获得父节点 (类) TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
上面的代码其实是解析注解生成如下代码: |
3.测试:绑定成功