一般我们在写android Activity的时候总是会在onCreate方法中加上setContentView方法来加载layout,通过findViewById来实现控件的绑定,每次写这么多代码总觉得很烦躁。近来看了下android中有Annotation来实现这方面的简化,对于java不是很了解,就简单的看了下。上次玩web的时候,springmvc也有很多的注解,不知道怎么实现的,这里其实基本上类似。

Annotation注解这里主要还是讲讲怎么使用吧,单纯的原理会把人绕进去的,没办法,java基础只能后面再补了,c搞久了,很多面向对象的思想只停留在大学的时候,除了linux内核的一些面向对象的思想。说了那么多的废话,接着继续我们的Annotation的学习吧,先新建工程emAnnotationStudy,新建EMLayoutBinder.java,代码如下:

package com.jared.emannotationstudy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* Created by jared on 16/3/10.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EMLayoutBinder {
int value();
}

这里的@Target,@Retention和@interface,先来简单的介绍下这几个内容吧。

@Target:说明了Annotation修饰的对象范围,Annotation可被用于packages、types等类,接口,枚举,Annotation类型;还可以是类成员方法,构造方法,成员变量,枚举值;方法参数和本地变量等。其一般有如下几种类型:

ElementType.CONSTRUCTOR:  构造器声明;

ElementType.FIELD:                    成员变量、对象、属性;

ElementType.LOCAL_VARIABLE:  局部变量声明;

ElementType.METHOD:                 方法声明;

ElementType.PACKAGE:                包声明;

ElementType.PARAMETER:           参数声明;

ElementType.TYPE:                        类、接口(包括注解类型)或enum声明;

这用到了TYPE。

@Retention:表示在什么级别保存该注解信息。其一般级别如下:

RetentionPolicy.SOURCE:    停留在java源文件,编译器被丢掉。

RetentionPolicy.CLASS:     停留在class文件中,但会被VM丢弃。

RetentionPolicy.RUNTIME:内存中的字节码,VM将在运行时也保留注解,因此可以通过反射机制读取注解的信息。

这里给了最高级别RUNTIME。

@interface:这个就表示注解了,和interface很像,不过多了一个@符号。

int value():表示传入的参数是int类型的。

好了,既然定义好了那么怎么使用呢?单单一个注解怎么个搞搞?其实注解一般都是和java的反射原理一起使用的。还是简单学习下java的反射吧,网上资料很多,这里就简单理解理解了。在java中的反射机制,被称为Reflection,它允许运行中的java程序对自身进行检查,并能直接操作程序的内部属性或方法。

利用Reflection APIs可以获取任何已知名称的类的内部信息,包括package、type parameters、superclass、implemented
interfaces、inner classes、outer classes、fields constructors、methods、modifiers等。

Class:  
       表示某个具体的类或接口

Object:        每个类都使用Object 做为超类,所有对象都实现这个类的方法

Constructor:封装了Class的构造方法

Field:           提供有关类或接口的属性信息,以及对它的动态访问权限

Method:  
   提供类或者接口上的方法的信息

Modifier:     封装了Class(method、fields)的修饰域。

简单了解下java的发射机制,其实反射就是通过反向调用类的一些功能,可能会觉得很难理解,还是继续我们的学习吧,新建EMAnnotationParser类:

package com.jared.emannotationstudy;

import android.app.Activity;
import android.view.View; import java.lang.reflect.Field; /**
* Created by jared on 16/3/10.
*/
public class EMAnnotationParser {
public static void injectActivity(Activity activity) {
if (null == activity) {
return;
}
Class<Activity> activityClass = (Class<Activity>) activity.getClass();
if (isEMLayoutBinder(activityClass)) {
EMLayoutBinder layout = activityClass.getAnnotation(EMLayoutBinder.class);
activity.setContentView(layout.value());
}
View decorView = activity.getWindow().getDecorView();
} private static boolean isEMLayoutBinder(Class<?> c) {
return c.isAnnotationPresent(EMLayoutBinder.class);
}

这里实现了injectActivity的方法,通过getClass获取当前的Activity的class,然后通过isAnnotationPresent查看该Annotation,再通过getAnnotation获取该注解,接着就是把注解传入的那个layout通过activity的setContentView方法来加载到activity
中了。好了,那么我们来实现下MainActivity吧:

package com.jared.emannotationstudy;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; @EMLayout(R.layout.activity_main)
public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EMAnnotationParser.injectActivity(this);
//setContentView(R.layout.activity_main);
}
}

去掉了setContentView,直接一个@EMLayout就搞定了,是不是很方便,这里通过EMAnnotationParser的injectActivity方法。其实一般项目中会定义一个BaseActivity,MainActivity通过继承BaseActivity来实现,那样看上去会更加的清晰,那就实现下BaseActivity吧:

package com.jared.emannotationstudy;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; /**
* Created by jared on 16/3/10.
*/
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EMAnnotationParser.injectActivity(this);
}
}

然后实现MainActivity继承BaseActivity:

package com.jared.emannotationstudy;

import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView; @EMLayoutBinder(R.layout.activity_main)
public class MainActivity extends BaseActivity { @EMViewBinder(R.id.hello)
private TextView mHello;
@EMViewBinder(R.id.test1)
private Button mTest1;
@EMViewBinder(R.id.test2)
private Button mTest2; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHello.setText("Hello Annotation!");
}
}

运行后依然没有任何问题。既然layout通过注解了,那么控件也是可以通过注解的,接下去就去实现下了。首先新建Annotation为EMViewBinder:

package com.jared.emannotationstudy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* Created by jared on 16/3/10.
*/ @Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EMViewBinder {
int value();
}

这里就很好理解了,和LayoutBinder一样,只是target是FIFLD,因为是成员的变量。接着简单实现下反射:

package com.jared.emannotationstudy;

import android.app.Activity;
import android.view.View; import java.lang.reflect.Field; /**
* Created by jared on 16/3/10.
*/
public class EMAnnotationParser {
public static void injectActivity(Activity activity) {
if (null == activity) {
return;
}
Class<Activity> activityClass = (Class<Activity>) activity.getClass();
if (isEMLayoutBinder(activityClass)) {
EMLayoutBinder layout = activityClass.getAnnotation(EMLayoutBinder.class);
activity.setContentView(layout.value());
}
View decorView = activity.getWindow().getDecorView();
initViews(activityClass.getDeclaredFields(), decorView, activity);
} private static boolean isEMLayoutBinder(Class<?> c) {
return c.isAnnotationPresent(EMLayoutBinder.class);
} private static boolean isEMViewBinder(Field filed) {
return filed.isAnnotationPresent(EMViewBinder.class);
} private static void initViews(Field[] fields, View view, Object object) {
View view1;
for (Field field : fields) {
if(isEMViewBinder(field)) {
EMViewBinder emView = field.getAnnotation(EMViewBinder.class);
view1 = view.findViewById(emView.value());
if(null != view1) {
try {
field.setAccessible(true);
field.set(object, view1);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
}
}
}

这里通过getDecorView来获取view,通过getDeclaredFields来获取Fields,然后通过getAnnotation来获取EMViewBinder注解,接着调用findViewById来找到这个控件。如果找到了,那么需要调用setAccessible为true,因为变量一般都是private的。大概的意思就这样了,下面我们修改下layout:

<?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"
android:layout_margin="10dp"
tools:context="com.jared.emannotationstudy.MainActivity"> <TextView
android:id="@+id/hello"
android:text="Hello World!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:layout_gravity="center"/> <Button
android:id="@+id/test1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="test1"
android:textAllCaps="false"/> <Button
android:id="@+id/test2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="test2"
android:textAllCaps="false"/>
</LinearLayout>

接着我们在MainActivity中添加代码如下:

package com.jared.emannotationstudy;

import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView; @EMLayoutBinder(R.layout.activity_main)
public class MainActivity extends BaseActivity { @EMViewBinder(R.id.hello)
private TextView mHello;
@EMViewBinder(R.id.test1)
private Button mTest1;
@EMViewBinder(R.id.test2)
private Button mTest2; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHello.setText("Hello Annotation!");
}
}

运行看下效果如下:

完全达到了我们的预期,而且编写代码十分方便,不需要再引入一大堆的findViewById了。即使再添加更多的控件也轻松搞定。既然控件绑定好了,那么接下去还需要做的就是事件的绑定了。这里主要实现button的事件,新建EMOnClickBinder:

package com.jared.emannotationstudy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* Created by jared on 16/3/10.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EMOnClickBinder {
int[] value();
}

这里实现的是方法,所以使用了METHOD,而且button可能很多,所以用了int[] 数组。接着我们实现具体的反射:

private static boolean isEMOnClickBinder(Method method) {
return method.isAnnotationPresent(EMOnClickBinder.class);
} private static void initOnClick(Method[] allMethod, View root, Object object) {
for (Method method : allMethod) {
if (isEMOnClickBinder(method)) {
EMOnClickBinder onClick = method.getAnnotation(EMOnClickBinder.class);
MyOnClickListener click = new MyOnClickListener(method, object);
int[] ids = onClick.value();
for (int id : ids) {
root.findViewById(id).setOnClickListener(click);
}
}
}
} static class MyOnClickListener implements View.OnClickListener {
private Method mMethod;
private Object mReceiver; public MyOnClickListener(Method method, Object receiver) {
mMethod = method;
mReceiver = receiver;
} @Override
public void onClick(View v) {
try {
mMethod.setAccessible(true);
mMethod.invoke(mReceiver, v);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

这里使用ID查找,调用setOnClickListener方法来注册方法,通过MyOnClickListener来实现具体的操作。当有事件触发的时候会调用onClick方法,进而调用method的invoke方法。就会调用到注解下的自定义方法了,这里传入的就是View。接着具体MainActivity的实现如下:

package com.jared.emannotationstudy;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView; @EMLayoutBinder(R.layout.activity_main)
public class MainActivity extends BaseActivity { @EMViewBinder(R.id.hello)
private TextView mHello;
@EMViewBinder(R.id.test1)
private Button mTest1;
@EMViewBinder(R.id.test2)
private Button mTest2; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHello.setText("Hello Annotation!");
} @EMOnClickBinder({R.id.test1, R.id.test2})
public void myOnClick(View view) {
switch (view.getId()) {
case R.id.test1:
mHello.setText("I am test1");
break;
case R.id.test2:
mHello.setText("I am test2");
default:
break;
}
}
}

是不是非常的简单清晰,以后把这几个文件当作工具,简单封装下,就可以不用每次写那么多的findViewById和setOnClickListener了。基本上Annotation就先学习到这里了。

Android开发学习之路--Annotation注解简化view控件之初体验的更多相关文章

  1. Android开发学习之路-让注解帮你简化代码,彻底抛弃findViewById

    本文主要是记录注解的使用的学习笔记,如有错误请提出. 在通常的情况下,我们在Activity中有一个View,我们要获得这个View的实例是要通过findViewById这个方法,然后这个方法返回的是 ...

  2. Android 开发学习进程0.15 adb cardview framelayout 控件设置状态获取焦点

    Android设备调试桥 即adb 使用adb进行无线调试的一些常用命令 adb tcpip 5555 设置调试端口为5555 防止冲突 adb shell ifconfig wlan0 查询局域网中 ...

  3. Android开发学习之路-RecyclerView滑动删除和拖动排序

    Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...

  4. Android开发学习之路--基于vitamio的视频播放器(二)

      终于把该忙的事情都忙得差不多了,接下来又可以开始good good study,day day up了.在Android开发学习之路–基于vitamio的视频播放器(一)中,主要讲了播放器的界面的 ...

  5. Android开发学习之路--Android Studio cmake编译ffmpeg

      最新的android studio2.2引入了cmake可以很好地实现ndk的编写.这里使用最新的方式,对于以前的android下的ndk编译什么的可以参考之前的文章:Android开发学习之路– ...

  6. Android开发学习之路--网络编程之xml、json

    一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载 ...

  7. Android开发学习之路--Activity之初体验

    环境也搭建好了,android系统也基本了解了,那么接下来就可以开始学习android开发了,相信这么学下去肯定可以把android开发学习好的,再加上时而再温故下linux下的知识,看看androi ...

  8. Android开发学习之路--Android系统架构初探

    环境搭建好了,最简单的app也运行过了,那么app到底是怎么运行在手机上的,手机又到底怎么能运行这些应用,一堆的电子元器件最后可以运行这么美妙的界面,在此还是需要好好研究研究.这里从芯片及硬件模块-& ...

  9. Android开发学习之路--MAC下Android Studio开发环境搭建

    自从毕业开始到现在还没有系统地学习android应用的开发,之前一直都是做些底层的驱动,以及linux上的c开发.虽然写过几个简单的app,也对android4.0.3的源代码做过部分的分析,也算入门 ...

随机推荐

  1. 【IOI1998】Picture(扫描线+线段树)

    问题来源:IOI1998 D2T1 题意:就是在一个平面内给出n个矩形,叫你计算将这些矩形合并以后,新图形的周长. 例如: 上图是原本的矩形们 ---------->合并后的图形 解题思路:拿一 ...

  2. 【分解爪UVA11396-二分图染色模板】

    ·Rujia:"稍加推理即可解决该题--" ·英文题,述大意:      一张无向连通图,每个点连有三条边.询问是否可以将这个图分成若干个爪子,并满足条件:①每条边只能属于一个爪子 ...

  3. hdu 3939(勾股+容斥)

    题意: 给定一个整数L(L<=1e12),计算(x,y,z)组的个数.其中x<y<z,x^2+y^2=z^2,gcd(x,y)==1,gcd(x,z)==1,gcd(y,z)==1. ...

  4. java9学习之模块化

    截止到目前JDK的版本已经更新到10了,虽然java9的生命周期才半年,但是我认为这个版本带来的变革是不可磨灭的,它是第一次深层次的针对架构以及依赖上的革新.下面我们就来学习一下. 一.模块化项目构建 ...

  5. Python-闭包详解

    在函数编程中经常用到闭包.闭包是什么,它是怎么产生的及用来解决什么问题呢.给出字面的定义先:闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)(想想Erlang的外层函数传入一个 ...

  6. Splay讲解

    Splay讲解 Splay是平衡树的一种,是一种二叉搜索树,我们先讲解一下它的核心部分. Splay的核心部分就是splay,可能有些人会说什么鬼?这样讲解是不是太不认真了?两个字回答:不是.第一个S ...

  7. spring基本原理

    作者:王奕然链接:https://www.zhihu.com/question/21346206/answer/101789659来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  8. Database Design Guidelines

    Database Design Guidelines Principles Support popular databases Name Style Table Name Style: Pascal ...

  9. Future学习

    接着上一篇继续并发包的学习,本篇说明的是Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果.        Callable接口类似于Runnable,从名字就可以看出来了,但 ...

  10. Oracle监听启动时报TNS-00507问题

    Linux系统中,启动oracle监听的时候报如下错误: [oracle@centos ~]$ lsnrctl start LSNRCTL :: Copyright (c) , , Oracle. A ...