Android注解解析,注解用法,仿xUtils用注解初始化控件、点击事件(一)
2016-09-08 18:16
666 查看
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:
看上面代码,方法体里面有一个int型的参数,因为布局文件R.layout.XXX就是int型,之前的@Override为什么方法体里面是空的呢?没错,因为不需要参数!接下来看MainActivity代码:
InjectUtils.InjectAll(this)这行代码的作用就是将activity传进去,然后在里面实现对注解的解析与运用,代码如下:
很容易理解吧,注解写的很清楚。首先看InjectContentView的代码:
好了,代码就这么多,接下来运行一下!
呃....平板截图,貌似有点大!先不管,小憩一下,接着看后面的代码,用注解来实现findViewBiID的功能,首先创建一个注解InjectViews:
代码很简单,接着看 InjectUtils 的 InjectViews 方法:
代码相对之前较多,但是很容易理解,注解很详细,看一下大概就明白了,接下来在MainActivity 的代码:
接下来运行一下:
好了,没有报空指针,并且在UI上也做出了相应的改变,很成功!
最后就是点击事件了,这才是最精彩的!!!接下来写的比较多,所以打算在下一篇博客里接着写!! 朋友们,喜欢的留下言吧~~~~
先来说一下注解,很多人都有看到过注解,比如最常见的@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文件里,在程序运行时需要用注解来初始化控件。
接下来看代码,最简单的布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".ui.MainActivity"> <TextView android:id="@+id/tv_main" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="我是一个TextView" android:textColor="#ff0000" /> <Button android:id="@+id/btn_login" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="登录" /> <Button android:id="@+id/btn_logoff" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="注销" /> </LinearLayout>
不多说,继续看下面,写一个注解实现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<? extends Activity> 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(); } }
好了,代码就这么多,接下来运行一下!
呃....平板截图,貌似有点大!先不管,小憩一下,接着看后面的代码,用注解来实现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<? extends Activity> 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_ 9568 main.setText("我是一个改变了文字的TextView"); btn_login.setText("登录再登录"); btn_logoff.setText("注销在注销"); } }
接下来运行一下:
好了,没有报空指针,并且在UI上也做出了相应的改变,很成功!
最后就是点击事件了,这才是最精彩的!!!接下来写的比较多,所以打算在下一篇博客里接着写!! 朋友们,喜欢的留下言吧~~~~
相关文章推荐
- android 加载大图的原理和实现
- Android开发艺术探索--第二章IPC机制(3)之Android中的IPC方式
- android 开发之坑系列_实体序列化问题
- Android Studio提交库至Bintray jCenter从入门到放弃
- Android Studio提交库至Bintray jCenter从入门到放弃
- android屏幕监听,手机是否处于锁屏状态,在锁屏之上弹出Toast.makeText消息
- Android 百度地图打包后获取不到地址问题
- [Android]之一:Android系统下载管理DownloadManager
- AsyncTask 工作原理(下)
- android launcher开发(6) Launcher暂停,停止,销毁
- Android中的ViewPager在更新内容的时候报java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0异常
- android filter
- android 4.4删除短信
- Android draw9patch 图片制作与使用详解
- Android 系统信息的获取
- 【Android】提升用户体验,ListView显示加载中动画及空数据视图
- 隐藏android系统的状态栏和导航栏
- JNI详解(一)
- Qt for Android 程序启动闪一下黑屏的优化方法
- 正则验证相关操作的类