ButterKnife basic use

Implementation 'com. Jakewharton: butterknife: 8.8.0 annotationProcessor' com. Jakewharton: butterknife - compiler: 8.8.0 'Copy the code
public class MainActivity extends AppCompatActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        textView.setText("Butter-Knife-Test"); }}Copy the code

ButterKnife implementation — reflection

BindView

/** * Find annotations for the control ID */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
    int value(a) default- 1;
}
Copy the code

InjectLayout

/** * Annotates the Activity layout file */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectLayout {
    int value(a) default- 1;
}
Copy the code

OnClick

/** * set the View to listen to the event annotation */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    int[] value();
}
Copy the code

BindProcessor

/** * Use annotations to inject layout file without setContentView * Use annotations to remove findViewById * Use annotations to remove setOnClickListener *  * https://blog.csdn.net/qq_20521573/article/details/82054088 */
public class BindProcessor {

    /** * is called by the Activity to bind the annotation logic **@param activity
     */
    public static void bind(Activity activity) {
        injectLayout(activity);
        bindView(activity);
        onClick(activity);
    }

    private static void injectLayout(Activity activity) { Class<? > activityCls = activity.getClass();if (activityCls.isAnnotationPresent(InjectLayout.class)) {
            InjectLayout layout = activityCls.getAnnotation(InjectLayout.class);
            int layoutId = layout.value();
            try {
                Method setContentView = activityCls.getMethod("setContentView".int.class);
                setContentView.setAccessible(true);
                setContentView.invoke(activity, layoutId);
            } catch(Exception e) { e.printStackTrace(); }}}private static void bindView(Activity activity) { Class<? > activityCls = activity.getClass(); Field[] fields = activityCls.getDeclaredFields();for (Field field : fields) {
            if (field.isAnnotationPresent(BindView.class)) {
                BindView bindView = field.getAnnotation(BindView.class);
                int viewId = bindView.value();
                try {
                    Method findViewById = activityCls.getMethod("findViewById".int.class);
                    findViewById.setAccessible(true);
                    Object view = findViewById.invoke(activity, viewId);
                    field.setAccessible(true);
                    field.set(activity, view);
                } catch(Exception e) { e.printStackTrace(); }}}}private static void onClick(final Activity activity) { Class<? > activityCls = activity.getClass(); Method[] methods = activityCls.getMethods();for (final Method method : methods) {
            if (method.isAnnotationPresent(OnClick.class)) {
                OnClick onclick = method.getAnnotation(OnClick.class);
                int[] viewIdArray = onclick.value();
                for (int viewId : viewIdArray) {
                    final View view = activity.findViewById(viewId);
                    if (view == null) {
                        continue;
                    }
                    view.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            try {
                                method.setAccessible(true);
                                method.invoke(activity, view);
                            } catch(Exception e) { e.printStackTrace(); }}}); } } } } }Copy the code

Using MyButterKnife

/** * Custom Butterknife binds view */ with annotations and reflections
@InjectLayout(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @BindView(R.id.tv_main)
    TextView tv_main;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
        BindProcessor.bind(this);
        tv_main.setText("Annotation binding View");
        Log.i(TAG, "MainActivity onCreate");
    }

    @OnClick({R.id.btn_main_click1, R.id.btn_main_click2})
    public void onClickView(View view) {
        int viewId = view.getId();
        if (viewId == R.id.btn_main_click1) {
            Toast.makeText(this."Btn01", Toast.LENGTH_SHORT).show();
        } else if (viewId == R.id.btn_main_click2) {
            Toast.makeText(this."Btn02", Toast.LENGTH_SHORT).show();
        } else {
            Log.i(TAG, "onClickView unknown"); }}}Copy the code

ButterKnife implementation – APT

With APT, you need to use annotationProcessor to rely on APT to compile and automatically generate Java code. Two modules need to be created separately. Annotation_compile stores APT source code, and annotation_lib stores annotations to be used

    annotationProcessor project(path: ':annotation_compile')
    implementation project(path: ':annotation_lib')
Copy the code

annotation_lib

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface BindView {
    int value(a);
}
Copy the code

annotation_compile

/** * Annotation handler for generating Java code */
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {

    /** * the object used to generate the file */
    private Filer mFiler;

    /** * Supported Java version */
    @Override
    public SourceVersion getSupportedSourceVersion(a) {
        return SourceVersion.latestSupported();
    }

    /** * The type of annotations currently processed in APT */
    @Override
    public Set<String> getSupportedAnnotationTypes(a) {
        Set<String> types = new HashSet<>();
        types.add(BindView.class.getCanonicalName());
        return types;
    }

    /** * Get the object used to generate the file: Filer * Generate the source file with Filer */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mFiler = processingEnvironment.getFiler();
    }

    /** * The generated code in app/build/generated/ap_generated_sources */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        // Get all BindView annotation elements; Get all BindView annotations into the Set
        Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);

        // Categorize all BindView annotation elements; BindView elements from the same Activity are grouped together
        Map<String, List<VariableElement>> map = new HashMap<>();
        for (Element element : elementsAnnotatedWith) {
            VariableElement variableElement = (VariableElement) element;
            // Get the name of the activity where the VariableElement is located
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
            // Save the activity's VariableElement with a List
            List<VariableElement> variableElementList = map.get(activityName);
            if (variableElementList == null) {
                variableElementList = new ArrayList<>();
                map.put(activityName, variableElementList);
            }
            variableElementList.add(variableElement);
        }

        // Parse the Map elements and concatenate them into Java files
        if (map.size() > 0) {
            Writer writer = null;
            Iterator<String> iterator = map.keySet().iterator();
            while (iterator.hasNext()) {
                String activityName = iterator.next();
                List<VariableElement> variableElementList = map.get(activityName);
                // Get the package name
                TypeElement typeElement = (TypeElement) variableElementList.get(0).getEnclosingElement();
                String packageName = processingEnv.getElementUtils().getPackageOf(typeElement).toString();
                // Write to the file
                try {
                    // Create a file
                    JavaFileObject sourceFile = mFiler.createSourceFile(
                            packageName + "." + activityName + "_ViewBinding");
                    // Create an input stream
                    writer = sourceFile.openWriter();
                    // Write specific content
                    writer.write("package " + packageName + "; \n");
                    writer.write("import " + packageName + ".IBinder; \n");
                    writer.write("public class " + activityName +
                            "_ViewBinding implements IBinder<"
                            + packageName + "." + activityName + ">{\n");
                    writer.write("@Override\n");
                    writer.write("public void bind("
                            + packageName + "." + activityName + " target){\n");
                    for (VariableElement element : variableElementList) {
                        // Get the name of the variable annotated by BindView
                        String variableName = element.getSimpleName().toString();
                        // Get the ID of the BindView annotation
                        int id = element.getAnnotation(BindView.class).value();
                        // Get the type of the variable annotated by BindView
                        TypeMirror typeMirror = element.asType();
                        writer.write("target." + variableName
                                + "=" + typeMirror + ") target.findViewById(" + id + "); \n");
                    }
                    writer.write("\n}\n}");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if(writer ! =null) {
                        try {
                            writer.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        return false; }}Copy the code

app module

IBinder

The xxx_ViewBinding class generated in Annotation_compile implements IBinder

/** * The user binds the activity */
public interface IBinder<T> {
    void bind(T target);
}
Copy the code

MyButterKnife

/** * is used to bind */ to the user
public class MyButterKnife {
    public static void bind(Activity activity) {
        String viewBindingName = activity.getClass().getName() + "_ViewBinding";
        try{ Class<? > viewBindingClass = Class.forName(viewBindingName);// Execute the bind method in xxx_ViewBinding
            IBinder iBinder = (IBinder) viewBindingClass.newInstance();
            iBinder.bind(activity);
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

Using MyButterKnife

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.btn_main)
    Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyButterKnife.bind(this);
        btn.setText("MyButterKnife"); }}Copy the code