Android注解解析,注解用法,仿xUtils用注解初始化控件、点击事件(一)

Xutils是一个很牛的框架,相信很多人都有用过,今天主要介绍的是Xutils的四大功能框架之一的ViewUtils,在这里我们会自己写一些注解,来实现与ViewUtils同样的功能,主要运用到的知识无非就是两种:注解和反射。

先来说一下注解,很多人都有看到过注解,比如最常见的@Override,但是并不知道这些注解的作用是什么,怎么来自己写一个注解,点进去@Override,看到如下的代码片段:

@Target(ElementType.METHOD)
 @Retention(RetentionPolicy.SOURCE)
 public @interface Override {
 }

没错,我们看到了@interface这个字段,这就是注解的修饰词,然后方法体里面是空的,这个先放一放,文章后面会讲到为什么是空的。另外在上面还有两个注解,是什么意思呢?下面我们就来说说:

1.@Target:这个注解有个参数:ElementType.XXX,这个参数的作用就是这个注解的用处、用在什么上

              CONSTRUCTOR:用于描述构造器
    FIELD:用于描述域,也可以理解成成员变量
    LOCAL_VARIABLE:用于描述局部变量
    METHOD:用于描述方法
    PACKAGE:用于描述包
    PARAMETER:用于描述参数
    TYPE:用于描述类、接口(包括注解类型) 或enum声明

2.@Retention:同样有一个参数RetentionPolicy.XXX

              SOURCE:在源文件中有效(即源文件保留)
    CLASS:在class文件中有效(即class保留)
    RUNTIME:在运行时有效(即运行时保留)

    有些人看到这里就不明白了,这个参数是什么意思?举个例子,@Override用的是SOURCE,也就是说,@Override的作用仅仅是告诉你,这是方法重写,而不需要出现在class里,因为即使class文件里没有这个注解也可以正常运行程序,而稍后我们要写的用注解初始化,咋需要用RUNTIME,因为需要把注解编译到.class文件里,在程序运行时需要用注解来初始化控件。

接下来看代码,最简单的布局文件:




    

    

不多说,继续看下面,写一个注解实现setContentView的功能  名为 InjectContentView:
@Target(ElementType.TYPE)//因为要在类上使用
@Retention(RetentionPolicy.RUNTIME)//之前已经说过,要编译到.class文件里
public @interface InjectContentView {
    int value();  //声明一个int参数
    //int value() default 0;//也可以设一个默认值
}
看上面代码,方法体里面有一个int型的参数,因为布局文件R.layout.XXX就是int型,之前的@Override为什么方法体里面是空的呢?没错,因为不需要参数!接下来看MainActivity代码:
@InjectContentView(R.layout.activity_main)  //这里就是之前写的注解,参数是布局文件
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.InjectAll(this);//这行代码什么意思呢?用过xUtils的人都知道,也需要有这么一行代码
    }
}

InjectUtils.InjectAll(this)这行代码的作用就是将activity传进去,然后在里面实现对注解的解析与运用,代码如下:
public class InjectUtils {


    public static void InjectAll(Activity activity) {
        InjectUtils.InjectContentView(activity);
        InjectUtils.InjectViews(activity);
        InjectUtils.OnClick(activity);
    }

    private static void OnClick(Activity activity) {  //点击事件的注解处理

    }
    
    public static void InjectContentView(Activity activity) {  //setContentView的注解处理
	    
    }

    public static void InjectViews(Activity activity) {  //findViewById的注解处理

    }
}

很容易理解吧,注解写的很清楚。首先看InjectContentView的代码:
public static void InjectContentView(Activity activity) {
	//获取MainActivity类
        Class clazz = activity.getClass();

	//获取MainActivity类上的注解,传入的是InjectContentView.class,注意返回的是InjectContentView
        InjectContentView contentView = clazz.getAnnotation(InjectContentView.class);

	//获取注解里的参数,也就是那个int的布局文件(R.layout.XXX)
        int value = contentView.value();

        try {
	    //通过反射获取Activity里的setContentView方法,参数是int型的布局文件id
            Method method = clazz.getMethod("setContentView", int.class);
	    //调用反射获取到的方法,value为上面通过注解获取到的layoutID
            method.invoke(activity, value);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
好了,代码就这么多,接下来运行一下!
Android注解解析,注解用法,仿xUtils用注解初始化控件、点击事件(一)_第1张图片


呃....平板截图,貌似有点大!先不管,小憩一下,接着看后面的代码,用注解来实现findViewBiID的功能,首先创建一个注解InjectViews:


@Target(ElementType.FIELD)  //需要的对应不再是类,而是成员变量
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectViews {
    int value() default 0;
}


代码很简单,接着看 InjectUtils 的 InjectViews 方法:

public static void InjectViews(Activity activity) {
	//获取MainActivity类
        Class clazz = activity.getClass();

	//获取全部的类中生命的的成员变量
        Field[] declaredFields = clazz.getDeclaredFields();

        for (Field declaredField : declaredFields) {
		
	    //获取对应的注解,传入的是InjectViews.class,注意返回的是InjectViews
            InjectViews annotation = declaredField.getAnnotation(InjectViews.class);

	    //这里要判断下注解是否为null,因为比如声明一个 public int i 时,是没有注解的
            if (annotation != null) {
		//当注解不为null,获取其参数
                int value = annotation.value();
                
                try {
		    //通过反射获取findViewById方法
                    Method method = clazz.getMethod("findViewById", int.class);
		    //调用方法
                    Object view = method.invoke(activity, value);
		    //这里要注意,类中的成员变量为private,故必须进行此操作,否则无法给控件赋值(即初始化的捆绑)
                    declaredField.setAccessible(true);
		    //将初始化后的控件赋值到MainActivity里的对应控件上
                    declaredField.set(activity, view);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

      代码相对之前较多,但是很容易理解,注解很详细,看一下大概就明白了,接下来在MainActivity 的代码:
@InjectContentView(R.layout.activity_main)
public class MainActivity extends Activity {

    @InjectViews(R.id.tv_main)
    private TextView tv_main;

    @InjectViews(R.id.btn_login)
    private Button btn_login;

    @InjectViews(R.id.btn_logoff)
    private Button btn_logoff;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //这行代码,别忘了
        InjectUtils.InjectAll(this);

        tv_main.setText("我是一个改变了文字的TextView");
        btn_login.setText("登录再登录");
        btn_logoff.setText("注销在注销");
    }
}
接下来运行一下:
Android注解解析,注解用法,仿xUtils用注解初始化控件、点击事件(一)_第2张图片


好了,没有报空指针,并且在UI上也做出了相应的改变,很成功!



最后就是点击事件了,这才是最精彩的!!!接下来写的比较多,所以打算在下一篇博客里接着写!!  朋友们,喜欢的留下言吧~~~~

你可能感兴趣的:(Android)