简易ButterKnife

一、APT的使用

用于APP在编译时期生成代码,要在AndroidStudio项目中创建Java Library工程。
1.1 build.gradle源码
apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //另外一个Java Library,用来生成Annotation
    implementation project(path: ':annotations')
    //注册一个APT功能
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
    //有这个才能够在编译时期调用annotationProcessor
    compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
    //javaPoet:使用oop思想生成代码
    implementation 'com.squareup:javapoet:1.12.1'

}


// 防止java控制台输出中文乱码
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

sourceCompatibility = "7"
targetCompatibility = "7"
1.2 AbstractProcessor派生类源码
package com.piaopiao.annonation_compiler;

import com.google.auto.service.AutoService;
import com.piaopiao.annotations.BindView;
import com.piaopiao.annotations.OnClick;
import com.squareup.javapoet.ClassName;
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.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
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.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

/**
 * 用来生成衍生代码
 */
@AutoService(Processor.class)
//允许/支持的注解类型,让注解处理器处理
@SupportedAnnotationTypes({Constants.BIND_VIEW, Constants.ON_CLICK})
//指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AnnotationsCompiler extends AbstractProcessor {

    //1.日志打印工具类
    private Messager messager;

    //3.需要一个用来生成文件的对象
    Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();
    }


    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
        javaPoet(roundEnvironment);
//        writerJava(roundEnvironment);
        return true;

    }

    /**
     * 手写java类
     * @param roundEnv
     */
    private void writerJava(RoundEnvironment roundEnv) {
        //类    TypeElement
        //方法  ExecutableElement
        //属性  VariableElement
        //需要开始进行分类
        Map<String, List<Element>> mapElement = new HashMap<>();

        //字段
        Set<? extends Element> elementsAnnotatedWithField = roundEnv.getElementsAnnotatedWith(BindView.class);
        for (Element element : elementsAnnotatedWithField) {
            VariableElement variableElement = (VariableElement) element;
            //得到activity名字(全类名)
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
            List<Element> variableElements = mapElement.get(activityName);
            if (variableElements == null) {
                variableElements = new ArrayList();
                mapElement.put(activityName, variableElements);
            }
            variableElements.add(variableElement);
        }

        //方法
        Set<? extends Element> elementsAnnotatedWithMethod = roundEnv.getElementsAnnotatedWith(OnClick.class);
        for (Element element : elementsAnnotatedWithMethod) {
            ExecutableElement executableElement = (ExecutableElement) element;
            //得到activity名字(全类名)
            String activityName = executableElement.getEnclosingElement().getSimpleName().toString();
            //找到方法元素
            List<Element> executableElements = mapElement.get(activityName);
            if (executableElements == null) {
                executableElements = new ArrayList();
                mapElement.put(activityName, executableElements);
            }
            executableElements.add(executableElement);
        }

        if (mapElement.size() > 0) {
            Writer writer = null;
            Iterator<String> iterator = mapElement.keySet().iterator();
            while (iterator.hasNext()) {
                //开始生成对应的文件
                String activityName = iterator.next();
                List<Element> elements = mapElement.get(activityName);
                //得到包名
                TypeElement enclosingElment = (TypeElement) elements.get(0).getEnclosingElement();
                String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElment).toString();
                //写入文件
                try {
                    JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + activityName + "_ViewBinding");
                    writer = sourceFile.openWriter();
                    //package com.example.butterknife_framework_demo;
                    writer.write("package " + packageName + ";\n");
                    //import com.example.butterknife_framework_demo.IBinder;
                    writer.write("import " + packageName + ".IBinder;\n");
                    //public class MainActivity_ViewBinding implements IBinder<com.example.butterknife_framework_demo.MainActivity> {
                    writer.write("public class " + activityName + "_ViewBinding implements IBinder<" + packageName + "." + activityName + ">{\n");
                    //@Override
                    writer.write("@Override\n");
                    //public void bind(com.example.butterknife_framework_demo.MainActivity target) {
                    writer.write("public void bind(final " + packageName + "." + activityName + " target){\n");
                    //target.textView = (android.widget.TextView) target.findViewById(2131165359);
                    for (Element element : elements) {
                        if (element instanceof VariableElement) {
                            //得到名字
                            String variableName = element.getSimpleName().toString();
                            //得到ID
                            int id = element.getAnnotation(BindView.class).value();
                            //得到类型
                            TypeMirror typeMirror = element.asType();
                            //target.textView = (android.widget.TextView) target.findViewById(2131165359);
                            writer.write("target." + variableName + "=(" + typeMirror + ")target.findViewById(" + id + ");\n");
                        } else if (element instanceof ExecutableElement) {//方法
                            //方法名
                            String executableName = element.getSimpleName().toString();
                            int[] viewIds = element.getAnnotation(OnClick.class).value();
                            for (int id : viewIds) {
                                writer.write("target.findViewById(" + id + ").setOnClickListener( new android.view.View.OnClickListener(){\n");
                                //        void onClick(View v);
                                writer.write("public void onClick(android.view.View view){\n");
                                writer.write("target." + executableName + "(view);");
                                writer.write("\n}\n});");
                            }
                        }

                    }
                    writer.write("\n}\n}");

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (writer != null) {
                        try {
                            writer.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
        }
    }

    /**
     * 利用javaPoet生成java类
     * @param roundEnv
     */
    private void javaPoet(RoundEnvironment roundEnv) {
        //类    TypeElement
        //方法  ExecutableElement
        //属性  VariableElement
        //需要开始进行分类
        Map<String, List<Element>> mapElement = new HashMap<>();

        //字段
        Set<? extends Element> elementsAnnotatedWithField = roundEnv.getElementsAnnotatedWith(BindView.class);
        for (Element element : elementsAnnotatedWithField) {
            VariableElement variableElement = (VariableElement) element;
            //得到activity名字(全类名)
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
            List<Element> variableElements = mapElement.get(activityName);
            if (variableElements == null) {
                variableElements = new ArrayList();
                mapElement.put(activityName, variableElements);
            }
            variableElements.add(variableElement);
        }

        //方法
        Set<? extends Element> elementsAnnotatedWithMethod = roundEnv.getElementsAnnotatedWith(OnClick.class);
        for (Element element : elementsAnnotatedWithMethod) {
            ExecutableElement executableElement = (ExecutableElement) element;
            //得到activity名字(类名)
            String activityName = executableElement.getEnclosingElement().getSimpleName().toString();
            //找到方法元素
            List<Element> executableElements = mapElement.get(activityName);
            if (executableElements == null) {
                executableElements = new ArrayList();
                mapElement.put(activityName, executableElements);
            }
            executableElements.add(executableElement);
        }

        //writerClass(mapElement);
        if (mapElement.size() > 0) {
            Iterator<String> iterator = mapElement.keySet().iterator();
            while (iterator.hasNext()) {
                //开始生成对应的文件
                String activityName = iterator.next();
                List<Element> elements = mapElement.get(activityName);
                //得到包名
                TypeElement enclosingElment = (TypeElement) elements.get(0).getEnclosingElement();
                String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElment).toString();


                MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("bind")
                        .addModifiers(Modifier.PUBLIC)
                        .addParameter(ClassName.get(enclosingElment), "target", Modifier.FINAL)
                        .addAnnotation(Override.class)
                        .returns(void.class);
                for (Element element : elements) {
                    if (element instanceof VariableElement) {
                        //得到名字
                        String variableName = element.getSimpleName().toString();
                        //得到ID
                        int id = element.getAnnotation(BindView.class).value();
                        //得到类型
                        TypeMirror typeMirror = element.asType();

                        methodSpecBuilder.addStatement("target.$N=($T)target.findViewById($L)", variableName, typeMirror, id);
                    } else if (element instanceof ExecutableElement) {//方法
                        //方法名
                        String executableName = element.getSimpleName().toString();
                        int[] viewIds = element.getAnnotation(OnClick.class).value();
                        for (int id : viewIds) {
                            // 匿名内部类实现的接口
                            ClassName viewClick = ClassName.get("android.view.View", "OnClickListener");

                            // 重写的方法
                            MethodSpec clickMethod = MethodSpec.methodBuilder("onClick")
                                    .addModifiers(Modifier.PUBLIC)
                                    .addAnnotation(Override.class)
                                    .addParameter(ClassName.get("android.view", "View"), "view")
                                    .addStatement("target.$N(view)", executableName)
                                    .build();
                            // 创建匿名内部类
                            TypeSpec typeClick = TypeSpec.anonymousClassBuilder("")
                                    .addSuperinterface(viewClick)
                                    .addMethod(clickMethod)
                                    .build();

                            methodSpecBuilder.addStatement("target.findViewById($L).setOnClickListener($L)", id, typeClick);

                        }

                    }
                }
                //创建方法
                MethodSpec methodSpec = methodSpecBuilder.build();
				//获取接口
                ClassName iBinder = ClassName.get(packageName, "IBinder");
				//获取对应的Activity
                ClassName activityClassName = ClassName.get(packageName, activityName);

                TypeSpec typeSpec = TypeSpec
                        .classBuilder(activityName + "_ViewBinding")
                        //添加泛型
                        .addSuperinterface(ParameterizedTypeName.get(iBinder, activityClassName))
                        .addModifiers(Modifier.PUBLIC)
                        .addMethod(methodSpec).build();

                JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build();

                try {
                	//生成类
                    javaFile.writeTo(filer);
                } catch (Exception e) {
                    e.printStackTrace();
                    messager.printMessage(Diagnostic.Kind.NOTE, "生成文件出现异常::" + e.getMessage());
                }
            }
        }
    }
}

二、简易ButterKnife的用法

package com.piaopiao.ymhbutterknife;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.piaopiao.annotations.BindView;
import com.piaopiao.annotations.OnClick;

public class MainActivity extends AppCompatActivity {


    @BindView(R.id.tv)
    TextView textView;

    @BindView(R.id.tv1)
    TextView textView1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        YmhButterKnife.binder(this);
        textView.setText("第一个点击事件");
        textView1.setText("第二个点击事件");
    }

    @OnClick({R.id.tv, R.id.tv1})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.tv:
                Toast.makeText(this, "第一个按钮", Toast.LENGTH_SHORT).show();
                break;
            case R.id.tv1:
                Toast.makeText(this, "第二个按钮", Toast.LENGTH_SHORT).show();
                break;
        }

    }

}

三、编译效果

package com.piaopiao.ymhbutterknife;

import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import java.lang.Override;

public class MainActivity_ViewBinding implements IBinder<MainActivity> {
  @Override
  public void bind(final MainActivity target) {
    target.textView=(TextView)target.findViewById(2131165325);
    target.textView1=(TextView)target.findViewById(2131165326);
    target.findViewById(2131165325).setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View view) {
        target.onClick(view);
      }
    });
    target.findViewById(2131165326).setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View view) {
        target.onClick(view);
      }
    });
  }
}

四、APT的调试

4.1 调试步骤
点击Debug按钮进入调试模式,再进行rebuild就可以进行调试了
4.2 注意事项
1. 如果按照上述步骤仍然无法进入调试模式,那么将所有模块的build目录都删除
2. APT生成对应模块代码的时候应该把该模块的build目录删掉

推荐文章

JavaPoet的使用指南
JavaPoet使用详解
JavaPoet的基本使用

源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值