ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2020-02-24 18:00:57  阅读:264  来源: 互联网

标签:lang java 自定义 public ButterKnife 注解 import Android annotation


一、概述

  自从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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有