其他分享
首页 > 其他分享> > Android利用注解自定义一个超级简单的ButterKnife

Android利用注解自定义一个超级简单的ButterKnife

作者:互联网

一、概述

  自从Java1.5后,其提供了一个非常强大的功能那就是注解。在普通的开发中可能不会自定义注解,甚至有些时候一个app开发下来完全不需要注解。但是想要在技术方面做一个纵深,自己封装框架,成为高级工程师,专家或者架构师,注解这块的知识是绕不开的。所以学习一下java中的注解对自己的内功修炼是非常有必要的。

  现在主流的开源框架大多数都会用到注解,不同的框架注解的参与程度也不一样。像:EventBus、ButterKnife、Retrofit2等框架其注解的参与程度是比较深的,如果想要在注解方面有更快的提升,建议各位看官去看这些框架的源代码。

  本节会先讲一下java&android中常用的元注解。然后通过两个小例子来系统的说一下注解的具体用法:自定义ButterKnife和获取类似retrofit中interfaceservice中的方法信息,来加强各位看官在注解方面上的理解。

二、元注解

  网上将Java元注解的文章非常的多,本节只将在作者看来最重要的两个,@target和@retention。其他的大家自行学习。本节的重点在于实践,其理论只是相对较少。

  1.@Target,其定义了注解可以在什么地方使用:如:类、属性、方法、参数、构造方法、局部变量、包声明等。

    @Target注解有以下几种值可以使用,当然,多种值可以组合使用

    a.TYPE:类、接口、枚举声明

    b.FIELD:在属性上声明

    c.METHOD:在方法上声明

    d.PARAMETER:参数参数声明

    e.CONSTRUCTOR:构造方法声明

    f.ANNOTATION_TYPE:注解类型声明

    g.LOCAL_VARIABLE:局部变量声明

    h.PACKAGE:包声明

  2@Retention,其定义了注解在那些阶段是可以用的,如:源码阶段、Class字节码阶段、运行时阶段(虚拟机)

    @Retention有三种阶段可以选择:

    a.RetentionPolicy.SOURCE,注解只存在源码中

    b.RetentionPolicy.CLASS,注解可以一直存活到字节码阶段

    c.RetentionPolicy.RUNTIME,注解可以一直存活,即使程序已经运行起来了

三、创建一个超级简单的ButterKnife

  本例我们将创建一个非常简单的ButterKnife。利用bind方法进行绑定view并给view赋值,而且还可以选择性的设置view的点击事件。

  类介绍:

  1.BindView.java绑定view的注解

  2.OnClick.java绑定点击事件的注解

  3.ButterKnife.java注解工具

  4.BindViewTestActivity.java测试类

  BindView:

package com.yw.annotationlib;

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

/**
 * 模仿ButterKnife实现View的绑定
 * create by yangwei
 * on 2020-02-24 13:09
 */
@Target(ElementType.FIELD)//标记该注解运行在字段上
@Retention(RetentionPolicy.RUNTIME)//标记该字段一直到程序运行时都有效
public @interface BindView {
    int value() default -1;//View的Id,如:R.id.btn
}

  OnClick.java

package com.yw.annotationlib;

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

/**
 * create by yangwei
 * on 2020-02-24 13:38
 */
@Target(ElementType.METHOD)//标记注解应用在方法上
@Retention(RetentionPolicy.RUNTIME)//标记注解直到运行时都可以存活
public @interface OnClick {
    int value() default -1;//比较view的id值
}

  ButterKnife.java

package com.yw.annotationlib;

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

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import androidx.annotation.RequiresApi;

/**
 * create by yangwei
 * on 2020-02-24 13:12
 */
public class ButterKnife {

    /**
     * 绑定对象用注解标注的所有方法和属性
     *
     * @param activity
     */
    @RequiresApi(api = Build.VERSION_CODES.N)
    public static void bind(final Activity activity) {
        try {


            Class clazz = activity.getClass();
            //获取class中的所有的注解
            Method[] methods = clazz.getDeclaredMethods();
            for (final Method method : methods) {
                final OnClick onClick = method.getDeclaredAnnotation(OnClick.class);
                if (onClick != null) {
                    final View view = activity.findViewById(onClick.value());
                    method.setAccessible(true);
                    //设置view的点击事件
                    view.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            try {
                                method.invoke(activity, view);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                BindView bindView = field.getDeclaredAnnotation(BindView.class);
                if (bindView != null) {
                    View view = activity.findViewById(bindView.value());
                    field.setAccessible(true);
                    field.set(activity, view);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  

BindViewTestActivity.java
package com.yw.rxjava3demo;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.yw.annotationlib.BindView;
import com.yw.annotationlib.ButterKnife;
import com.yw.annotationlib.OnClick;
import com.yw.annotationlib.retrofit.FieldMap;
import com.yw.annotationlib.retrofit.POST;
import com.yw.annotationlib.retrofit.RetrofitAnnotationBind;

import java.util.Arrays;
import java.util.Map;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

/**
 * create by yangwei
 * on 2020-02-24 13:21
 */
public class BindViewTestActivity extends Activity {
    @BindView(R.id.tv_name)
    TextView tv_name;
    @BindView(R.id.btn_click)
    Button btn_click;

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.bindviewtest_layout);
        ButterKnife.bind(this);
        tv_name.setText("我是杨洛峋,是一个小宝宝");

        //测试参数注解和方法注解
        RetrofitAnnotationBind.ServiceMethod serviceMethod = RetrofitAnnotationBind.bind(this);
        Log.e("获取注解中的请求方式:", serviceMethod.getMethod());
        Log.e("获取请求方法中的值:", serviceMethod.getParams());


    }
    @POST("POST")
    public void retrofitTest(@FieldMap Map<String,String> map){

    }

    @OnClick(R.id.btn_click)
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_click:
                Toast.makeText(BindViewTestActivity.this, "点击事件执行了", Toast.LENGTH_LONG).show();
                break;
        }

    }
}

  xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我是杨洛峋" />

    <Button
        android:id="@+id/btn_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="点击我试试" />

</LinearLayout>

  其最终的结果是TextView和Button按钮成功绑定上了findViewById。且Button按钮可以执行其注解标注的点击事件。  

四、模仿retrofit获取注解方法参数以及方法注解参数上的值

  本小节的主要内容是操作activty中的一个方法retrofitTest并的到这个方法注解的值和参数注解中的值并返回

  FieldMap.java参数注解

  POST.java方法注解

  RetrofitAnnotationBind.java注解解析器

  FieldMap.java

package com.yw.annotationlib.retrofit;

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

/**
 * 模仿Retrofit的FieldMap参数注解
 * create by yangwei
 * on 2020-02-24 14:03
 */
@Target(ElementType.PARAMETER)//标记注解附着到参数上
@Retention(RetentionPolicy.RUNTIME)//标记注解一直到运行时都有效果
public @interface FieldMap {
    boolean encoded() default false;
}

  POST.java

package com.yw.annotationlib.retrofit;

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

/**
 * create by yangwei
 * on 2020-02-24 14:01
 */
@Target(ElementType.METHOD)//在方法上运行
@Retention(RetentionPolicy.RUNTIME)//标记注解一直能存活到运行时
public @interface POST {
    String value() default "post_hello";
}

  

RetrofitAnnotationBind.java
package com.yw.annotationlib.retrofit;

import android.os.Build;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import androidx.annotation.RequiresApi;

/**
 * create by yangwei
 * on 2020-02-24 14:06
 */
public class RetrofitAnnotationBind {
    /**
     * 拿到方法注解和参数注解
     *
     * @param obj
     */
    @RequiresApi(api = Build.VERSION_CODES.N)
    public static ServiceMethod bind(Object obj) {
        ServiceMethod serviceMethod = new ServiceMethod();
        Class clazz = obj.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            POST post = method.getDeclaredAnnotation(POST.class);
            //说明此方法上有POST注解,拿到post上的值
            if (post != null) {
                String psotValue = post.value();
                serviceMethod.setMethod(psotValue);
                Annotation[][] annotations = method.getParameterAnnotations();
                if (annotations != null) {
                    StringBuffer sb = new StringBuffer();
                    for (Annotation[] paramsAnnotations : annotations) {
                        for (Annotation annotation : paramsAnnotations) {
                            if (annotation instanceof FieldMap) {
                                FieldMap fieldMap = (FieldMap) annotation;
                                sb.append(fieldMap.encoded()).append(",");
                            }
                        }
                    }
                    serviceMethod.setParams(sb.toString());
                }
            }
        }
        return serviceMethod;
    }

    public static class ServiceMethod {
        private String method;
        private String params;

        public ServiceMethod() {
        }

        public ServiceMethod(String method, String params) {
            this.method = method;
            this.params = params;
        }

        public String getMethod() {
            return method;
        }

        public void setMethod(String method) {
            this.method = method;
        }

        public String getParams() {
            return params;
        }

        public void setParams(String params) {
            this.params = params;
        }
    }
}

  测试类中的测试方法,解析的就是这个方法

 @POST("POST")
    public void retrofitTest(@FieldMap Map<String,String> map){

    }

  本例的最终结果会的到一个ServiceMethod类。此类中存储了POST注解的值和FieldMap注解中的值。

其打印结果为POST,false

 //测试参数注解和方法注解
        RetrofitAnnotationBind.ServiceMethod serviceMethod = RetrofitAnnotationBind.bind(this);
        Log.e("获取注解中的请求方式:", serviceMethod.getMethod());
        Log.e("获取请求方法中的值:", serviceMethod.getParams());

  

总结:本节的内容讲到这里就结束了,希望大家能够对注解的使用方式有一点自己的体会。如果想要更深入的了解注解,建议大家去看下相关框架的源代码。

ps:一旦你深入了,你会发现注解其实法力还是很大的。

标签:lang,java,自定义,public,ButterKnife,注解,import,Android,annotation
来源: https://www.cnblogs.com/tony-yang-flutter/p/12357988.html