Android开发学习之路-让注解帮你简化代码,彻底抛弃findViewById
本文主要是记录注解的使用的学习笔记,如有错误请提出。
在通常的情况下,我们在Activity中有一个View,我们要获得这个View的实例是要通过findViewById这个方法,然后这个方法返回的是一个Object类型,我们还需要进行强制的类型转换,但是相信很多人都遇到过,当我们的一个布局中有很多个控件的时候,每一个控件都要进行上面的这个操作其实是很烦躁的,特别是强制类型转换,即使是用Alt+Enter,多按几次都累了。而今天要用的是通过注解的方式来简化这一个复杂的步骤,在我们编写好相应的代码之后,获取实例的方法就变成像下面这样简单了
@ViewInject(R.id.buy)
private Button buy;
通过上面这两行简单的代码,就能把id所对应的View实例化了,下面我们一步一步的来学习如何使用注解。
先说原理,我们都要知道,在Java中,通过反射,我们可以知道每一个类的详细信息,比如有什么字段,有什么方法,类名等等,我们通过注解和反射配合,使用反射调用类中的方法,然后读取注解的参数来进行方法的执行。简单的说,就是其实我们还是会调用findViewById这个方法,但是,这个方法可以放到工具类中执行了,我们只需要像上面那样给出参数就行了。
先说下反射,反射就是允许我们动态的操作一个类,获取类的字段,执行类的方法,获取类的名字等等,在这里我们一般会用到下面的这几个方法:
getMethod:获取类中的public方法
getDeclaredMethod:获取类中的所有方法
getField:获取类中的public字段(属性)
getDeclaredField:获取类中的所有字段
还有其他的请自行参考Class类的源码
再说注解,我们看到的最多的注解获取是@Override,当我们重写父类中的方法的时候,这个注解会被编译器自动生成出来,这个注解只是表明方法是重写了父类方法。
那么我们究竟该如何自定义我们想要的注解呢?其实很简单,直接看代码:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject{
int value();
}
@Target的意思是我们注解作用的目标,这里是ElementType.FIELD,也就是作用于字段的
ElementType的类型有以下几种:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述字段
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention的意思是注解的运行级别
RetentionPolicy的类型有以下几种
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
@interface则是表明这个类是一个注解,@号不能漏掉,否则变成了接口了
如果想对注解了解得更多,可以参考:http://www.cnblogs.com/gmq-sh/p/4798194.html 这篇博文,很详细,对于我们想要的功能,以上的内容就足够了
定义好了注解,如果我们直接来使用,是没有任何效果的,因为注解只是一段代码,并没有关联上我们的控件,所以我们要编写一个工具类来做关联
public class AnnotateUtils {
public static void injectViews(Activity activity) {
Class<? extends Activity> object = activity.getClass(); // 获取activity的Class
Field[] fields = object.getDeclaredFields(); // 通过Class获取activity的所有字段
for (Field field : fields) { // 遍历所有字段
// 获取字段的注解,如果没有ViewInject注解,则返回null
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
int viewId = viewInject.value(); // 获取字段注解的参数,这就是我们传进去控件Id
if (viewId != -1) {
try {
// 获取类中的findViewById方法,参数为int
Method method = object.getMethod("findViewById", int.class);
// 执行该方法,返回一个Object类型的View实例
Object resView = method.invoke(activity, viewId);
field.setAccessible(true);
// 把字段的值设置为该View的实例
field.set(activity, resView);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
}
在工具类中,我们获取到了activity的Class,接着获得类中的所有字段,遍历字段,如果有ViewInject注解,则获取注解的中的值,通过获取并执行类中的方法(findViewById)来获得对应View的实例,最后把实例赋值给当前的字段,整个过程就完成了。
当我们需要使用注解的时候,我们只需要在定义View的时候,在View的字段上添加对应的注解,把Id传入,然后在onCreate方法中调用上面这个工具类的方法即可。
@ViewInject(R.id.buy)
private Button buy;
@ViewInject(R.id.money)
private TextView money;
@ViewInject(R.id.tv_power)
private TextView power;
@ViewInject(R.id.tv_life)
private TextView life;
@ViewInject(R.id.tv_dex)
private TextView dex;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AnnotateUtils.injectViews(this);
}
最后,有几点说明:
①如果注解中的值不是value,那么在进行注解是时候,需要给出对应的值的名字,假如我们在注解中做了如下定义:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject{
int id();
}
那么在注解的时候,需要这样:
@ViewInject(id = R.id.buy)
private Button buy;
因为value这个名字是默认的,如果我们定义为value,那么注解的时候可以省略
②注解还可以帮我们注入布局,设置监听等,如果有兴趣,可以继续探讨下去
③使用注解的时候,句末不能加“;”
Android开发学习之路-让注解帮你简化代码,彻底抛弃findViewById的更多相关文章
- Android开发学习之路--Annotation注解简化view控件之初体验
一般我们在写android Activity的时候总是会在onCreate方法中加上setContentView方法来加载layout,通过findViewById来实现控件的绑定,每次写这么多代码总 ...
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- Android开发学习之路--基于vitamio的视频播放器(二)
终于把该忙的事情都忙得差不多了,接下来又可以开始good good study,day day up了.在Android开发学习之路–基于vitamio的视频播放器(一)中,主要讲了播放器的界面的 ...
- Android开发学习之路--Android Studio cmake编译ffmpeg
最新的android studio2.2引入了cmake可以很好地实现ndk的编写.这里使用最新的方式,对于以前的android下的ndk编译什么的可以参考之前的文章:Android开发学习之路– ...
- Android开发学习之路--网络编程之xml、json
一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载 ...
- Android开发学习之路--Activity之初体验
环境也搭建好了,android系统也基本了解了,那么接下来就可以开始学习android开发了,相信这么学下去肯定可以把android开发学习好的,再加上时而再温故下linux下的知识,看看androi ...
- Android开发学习之路--Android系统架构初探
环境搭建好了,最简单的app也运行过了,那么app到底是怎么运行在手机上的,手机又到底怎么能运行这些应用,一堆的电子元器件最后可以运行这么美妙的界面,在此还是需要好好研究研究.这里从芯片及硬件模块-& ...
- Android开发学习之路--MAC下Android Studio开发环境搭建
自从毕业开始到现在还没有系统地学习android应用的开发,之前一直都是做些底层的驱动,以及linux上的c开发.虽然写过几个简单的app,也对android4.0.3的源代码做过部分的分析,也算入门 ...
- Android开发学习之路-该怎么学Android(Service和Activity通信为例)
在大部分地方,比如书本或者学校和培训机构,教学Android的方式都基本类似,就是告诉先上原理方法,然后对着代码讲一下. 但是,这往往不是一个很好的方法,为什么? ① 学生要掌握这个方法的用途,只能通 ...
随机推荐
- sqlite like 通配符 ,匹配区分大小写(默认不区分大小写)
在查询前先执行这个语句 , 1 时区分大小写,0时不区分 PRAGMA case_sensitive_like =0; select prod_name,PROD_PRICEfrom products ...
- 杨氏矩阵定义及其查找的实现C++
先介绍一下这个数据结构的定义,Young Tableau有一个m*n的矩阵,然后有一数组 a[k], 其中 k<=m*n ,然后把a[k]中的数填入 m*n 的矩阵中,填充规则为: 1. 每一 ...
- 背压(Backpressure)机制
作者:张铁蕾链接:https://www.zhihu.com/question/49618581/answer/117107570来源:知乎著作权归作者所有,转载请联系作者获得授权. 首先,从大的方面 ...
- SQLServer CASE WHEN 用法
SELECT sc.NAME AS 学校名称 ,xueyuan.NAME AS 院系 ,StudentNo AS 学号 ,st.StudentName AS 学生姓名 ,st.sex AS 性别 ,s ...
- 常用str函数
echo stripslashes("Who\'s Bill Gates?"),'<br />';//去掉反斜杠 echo strtolower("AABbb ...
- swift-string(字符串的一些语法)
1 isEmpty 返回一个布尔值,确定该字符串是否为空 2 hasPrefix(prefix: String) 函数检查给定的参数字符串是否以 string 为前缀 3 hasSuffix(suff ...
- clang: error: linker command failed with exit code 1 (use -v to see invocati
安装了 pod 的项目,如果仍旧打开 xcodeproject 文件, 运行时会弹出此 bug
- ASP.NET导出Excel文件
第一种最常见,并且最简单的方式,直接把GridView导出,导出格式为文本表格形式. protected void btnSaveExcel_Click(object sender, EventArg ...
- 从源代码构建 Go 开发环境
从源代码构建 Go 开发环境 Go 1.5 之前的版本 安装C 语言开发环境 在Go 1.5 之前的版本(比如 1.3.1.4),都会部分的依赖 C 语言的工具链,所以如果你有C 语言的开发环境,就可 ...
- Django动态渲染多层菜单
为后续给菜单设置权限管理方便,通过给页面模版菜单动态渲染,通过数据菜单表进行匹配需要渲染的菜单 #Django表结构 class Menus(models.Model): name = models. ...