通过注解和反射编写一个android注解框架

1、概述

你可以 随便找个人依靠 那么寒冬后 炎夏前 谁会给你春一样的爱恋 日落后 最美的 时光已溜走——《三十岁的女人》

上篇文章《java反射机制和自定义注解原理分析和实例》已经介绍了java的反射机制和自定义注解的基本原理和简单实例。本篇文章模仿android的框架xUtils的ViewUtils模块,完全注解方式就可以进行UI绑定和事件绑定,无需findViewById和setClickListener等。

2、实战

(1)创建自定义注解,代表绑定控件,接收控件的id值,ViewInject.class

package com.chunsoft.viewbind;

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

/**
 * Developer:chunsoft on 2017/3/3 12:25
 * Email:[email protected]
 * Content:findViewById
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    int value();
}

(2)创建自定义注解,代表单击事件绑定,接收控件的id值,onClick.class

package com.chunsoft.viewbind;

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

/**
 * Developer:chunsoft on 2017/3/3 12:26
 * Email:[email protected]
 * Content:单击事件绑定
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface onClick {
    int value();
}

(3)实现具体的控件绑定和单击事件绑定,步骤详细,ViewUtils.class

package com.chunsoft.viewbind;

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

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

/**
 * Developer:chunsoft on 2017/3/3 12:27
 * Email:[email protected]
 * Content:实现具体的绑定方法
 */

public class ViewUtils{
    public static void inject(Activity activity) {
        //绑定控件
        bindView(activity);
        //绑定事件
        bindOnClick(activity);
    }

    private static void bindOnClick(final Activity activity) {
        /**
         * 1.获取Activity的字节码
         */
        Class clazz = activity.getClass();
        /**
         * 2.获取该字节码的所有方法
         */
        Method[] declaredMethods = clazz.getDeclaredMethods();
        /**
         * 3.遍历方法,找出方法上声明为onClick的方法
         */
        for (final Method method:declaredMethods) {
            /**
             * 4.获取字段上面指定的注解
             */

            onClick mOnClick = method.getAnnotation(onClick.class);
            if (mOnClick != null) {
                /**
                 * 5.获取当前注解值
                 */
                int resId = mOnClick.value();
                /**
                 * 6.通过调用Activity的findViewById方法,获取当前id为resId的控件
                 */
                final View view = activity.findViewById(resId);
                /**
                 * 7.给当前的View绑定点击监听事件
                 */
                view.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        /**
                         * 8.通过反射调用当前的用户方法
                         */
                        method.setAccessible(true); //暴力反射
                        try {
                            method.invoke(activity,view); //不能抛出异常,父类没有抛出异常,子类也不能抛出异常
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                });

            }

        }
    }

    private static void bindView(Activity activity) {
        /**
         * 1.获取Activity的字节码
         */
        Class clazz = activity.getClass();
        /**
         * 2.获取该字节码的所有的Field
         */
        Field[] declaredFields = clazz.getDeclaredFields();
        /**
         * 3.遍历字段,判断哪些是我们想要的字段(只有添加ViewInject注解的字段)
         */
        for (Field field:declaredFields) {
            //获取字段上面指定的注解
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {
                /**
                 * 4.获取当前注解值
                 */
                int resId = viewInject.value();
                /**
                 * 5.通过调用Activity的findViewById方法,获取当前id为resId的控件
                 */
                View view = activity.findViewById(resId);
                /**
                 * 6.通过依赖注入,将当前View设置为=给当前的Field
                 */
                // 暴力反射
                field.setAccessible(true);

                try {
                    field.set(activity,view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }else {

            }
        }

    }
}

(4)在Activity中测试,MainActivity.class

package com.chunsoft.viewbind;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    @ViewInject(R.id.tv1)
    private TextView tv1;

    @ViewInject(R.id.tv2)
    private TextView tv2;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewUtils.inject(this);

        Toast.makeText(this,tv1.getText().toString(),Toast.LENGTH_SHORT).show();
        Log.d("测试ViewInject",tv1.getText().toString()+tv2.getText().toString());
    }

    @onClick(R.id.btn1)
    private void Click(View view) {
        Toast.makeText(this,"点击成功",Toast.LENGTH_SHORT).show();
    }
}

我们通过上面的代码通过注解方式和反射就实现了一个简单的UI绑定和事件绑定。下面我们将框架打包成jar文件。

3.打包框架

框架写好后,通过Android Studio 导出框架,导出jar包。
(1)修改build.gradle文件

在Android Studio中会显示多个build.gradle文件,如果你想要将整个项目导出成jar包,就找到对应项目名目录下的build.gradle文件,对其进行修改。有如下几个地方需要修改:

将最前面的apply plugin: 'com.android.application' 修改为apply plugin: 'com.android.library'
将defaultConfig修改为:
defaultConfig {
minSdkVersion 15
targetSdkVersion 24
}

SDK的版本可以根据自己的版本来改

在最后加上:

task deleteOldJar(type: Delete) {
delete 'build/outputs/ViewUtils.jar'
}

task exportJar(type: Copy) {
from('build/intermediates/bundles/release/')
into('build/libs/')
include('classes.jar')
rename ('classes.jar', 'ViewUtils.jar')
}

exportJar.dependsOn(deleteOldJar, build)

导出的路径,这个可以自定义,rename里的ViewUtils.jar是要导出的jar包的名字,也是自定义的。

这些修改完后就是一下可视化操作了。

(2)可视化操作

首先打开右上角Gradle:
通过注解和反射编写一个android注解框架_第1张图片

再打开app->Tasks->other->exportJar
通过注解和反射编写一个android注解框架_第2张图片

然后双击exportJar就可以了,在设置的路径下可以找到对应的jar文件,ViewUtils.jar。框架比较简单,大家最好自己实现下,需要源码请留言。

你可能感兴趣的:(android系统,android开源框架)