仿ButterKnife简易实现findViewById

项目创建

创建一个新项目,然后创建三个子Module
在这里插入图片描述

  • Android Library:butterknife,里面包含了butterknife的一些简易的模板类
  • Java Library:butterknife-annotations,包含了butterknife所需的注解
  • Java Library:butterknife-compiler,butterknife的注解处理器

1.在butterknife-compilerModule中导入所需的依赖

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    // 注解Module
    implementation project(path: ':butterknife_annotations')
    // 用于Java代码生成
    api 'com.squareup:javapoet:1.11.1'
    // 注解处理器,gradle5.0之后需要这样(原因不清楚~)
    api 'com.google.auto.service:auto-service:1.0-rc6'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
}

//中文乱码问题(错误:编码GBK不可映射字符)
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

sourceCompatibility = "8"
targetCompatibility = "8"

2.在butterknife-annotationsModule中定义注解BindView

package com.butterknife.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
    int value();
}

3.在butterknife工程中定义简易的几个类

package com.butterknife;

import androidx.annotation.UiThread;

/**
 * 用于解绑Activity引用
 * Created by FangJu on 2020/4/1.
 */
public interface Unbinder {
    @UiThread
    void unbind();

    Unbinder EMPTY = new Unbinder() {
        @Override
        public void unbind() {

        }
    };
}
package com.butterknife;

import android.app.Activity;
import android.view.View;

/**
 * Created by FangJu on 2020/4/1.
 */
public class ButterKnifeUtil {
    public static <T extends View> T findViewById(Activity activity, int viewId) {
        return activity.findViewById(viewId);
    }
}

package com.butterknife;

import android.app.Activity;

import java.lang.reflect.Constructor;

/**
 * Created by FangJu on 2020/4/1.
 */
public class ButterKnife {
    public static Unbinder bind(Activity activity) {
        try {
            Class<? extends Unbinder> className = (Class<? extends Unbinder>) Class.forName(activity.getClass().getName() + "_ViewBinding");
            Constructor<? extends Unbinder> declaredConstructor = className.getDeclaredConstructor(activity.getClass());
            declaredConstructor.setAccessible(true);
            return declaredConstructor.newInstance(activity);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Unbinder.EMPTY;
    }
}

4.在butterknife-compiler中定义注解处理器

package com.butterknife.compiler;

import com.butterknife.annotations.BindView;
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.TypeSpec;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
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.util.Elements;

@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
    private Filer filer;
    private Elements elementUtils;

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

    // 处理器可以处理的Java版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_8;
    }

    // 表示处理器可以处理哪些注解
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
            types.add(annotation.getCanonicalName());
        }
        return types;
    }

    private Set<Class<? extends Annotation>> getSupportedAnnotations() {
        Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
        annotations.add(BindView.class);
        return annotations;
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // 获取所有注解了BindView的类
        Set<? extends Element> bindViewElements = roundEnv.getElementsAnnotatedWith(BindView.class);
        // 定义一个map,key为类,value为类中的注解变量
        // com.fangju.butterknife.LoginActivity---[tv1, tv2, tv3]
        Map<Element, List<Element>> elementMap = new LinkedHashMap<>();
        for (Element bindViewElement : bindViewElements) {
            Element enclosingElement = bindViewElement.getEnclosingElement();

            List<Element> elementList = elementMap.get(enclosingElement);
            if (elementList == null) {
                elementList = new ArrayList<>();
                elementMap.put(enclosingElement, elementList);
            }

            elementList.add(bindViewElement);
        }

        // 生成代码
        for (Map.Entry<Element, List<Element>> entry : elementMap.entrySet()) {
            Element key = entry.getKey();
            List<Element> value = entry.getValue();
            System.out.println(key.toString() + "---" + value.toString());

            // 构造类
            String srcClassName = key.getSimpleName().toString();
            ClassName superClassName = ClassName.get("com.butterknife", "Unbinder");
            TypeSpec.Builder classBuilder =
                    TypeSpec.classBuilder(srcClassName + "_ViewBinding")
                            .addModifiers(Modifier.FINAL, Modifier.PUBLIC)
                            .addSuperinterface(superClassName);
            // 加入字段
            ClassName activityClassName = ClassName.bestGuess(srcClassName);
            classBuilder.addField(activityClassName, "target", Modifier.PRIVATE);

            // 实现方法
            ClassName callSuperName = ClassName.get("androidx.annotation", "CallSuper");
            MethodSpec.Builder unbindMethodBuilder =
                    MethodSpec.methodBuilder("unbind")
                            .addAnnotation(Override.class)
                            .addAnnotation(callSuperName)
                            .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
            unbindMethodBuilder.addStatement("$T target = this.target", activityClassName);
            unbindMethodBuilder.addStatement("if(target == null) throw new IllegalStateException(\"Bindingd already clear\")");
            unbindMethodBuilder.addStatement("this.target = null");
            for (Element element : value) {
                String fileName = element.getSimpleName().toString();
                unbindMethodBuilder.addStatement("this.target.$L = null", fileName);
            }


            // 将方法加入类中
            classBuilder.addMethod(unbindMethodBuilder.build());

            // 加入构造函数
            MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
                    .addParameter(activityClassName, "target");
            constructorBuilder.addStatement("this.target=target");

            for (Element element : value) {
                String fieldName = element.getSimpleName().toString();
                ClassName utilName = ClassName.get("com.butterknife", "ButterKnifeUtil");
                int resId = element.getAnnotation(BindView.class).value();
                // target.tv1 = ButterKnifeUtil.findViewById(source, R.id.tv1)
                constructorBuilder.addStatement("target.$L = $T.findViewById(target, $L)", fieldName, utilName, resId);

            }


            classBuilder.addMethod(constructorBuilder.build());

            // 生成类
            String pkgName = elementUtils.getPackageOf(key).getQualifiedName().toString();
            try {
                JavaFile.builder(pkgName, classBuilder.build())
                        .addFileComment("butterknife自动生成")
                        .build()
                        .writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return false;
    }
}

5.测试

package com.fangju.butterknife;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import com.butterknife.ButterKnife;
import com.butterknife.Unbinder;
import com.butterknife.annotations.BindView;

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.tv1)
    TextView tv1;
    @BindView(R.id.tv2)
    TextView tv2;
    @BindView(R.id.tv3)
    TextView tv3;

    private Unbinder unbinder;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        unbinder = ButterKnife.bind(this);

        tv1.setText("测试");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbinder.unbind();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值