当前位置: 代码迷 >> Android >> Android中透过注解代替findViewById方法
  详细解决方案

Android中透过注解代替findViewById方法

热度:81   发布时间:2016-04-28 05:32:29.0
Android中通过注解代替findViewById方法

尊重原创 http://write.blog.csdn.net/postedit/26507351


这篇文章主要讲解注解实现findViewById的功能,首先我们来熟悉一下在java中怎么定义一个注解和解析一个注解

注解的概念是在jdk5.0中提出来的,在java.lang的包中已经定义了三个注解:Override,Deprecated,SuppressWarnings
Override相信大家非常熟悉,就是表明这个方法是改写了父类的方法
Deprecated表示在新版本的 jdk中已经不建议使用这个方法或者属性
SuppressWarning就是屏蔽掉一些警告

知道了注解的概念后,我们就来自定义注解

注解的定义和接口的接口非常像,在interface的前面多了一个@

public @interface TestPerson{}

[email protected],弄掉了就成了接口的定义了,上面是一个最简单注解的定义,当然注解和类一样,也可以定义属性,如下:

public @interface TestPerson{  //name既是这个注解的属性,也是注解的方法,调用name()返回值就是name  String name() default "gavin";}

我现在想定义两个注解,一个注解用来说明某个类的意义,另一个注解用来说明类中的某个方法由谁测试的,便于追究责任,定义如下:

//用来标注某个类是用来干嘛的public @interface ClassFunction{  String value() default "";}//用来标注类中的方法是被谁测试的public @interface TestPerson{  //name是属性而不是方法,gavin是它的默认值,在定义的时候可以不用给定默认值  String name() default "gavin";}

那么怎么限定一个注解是用在类上还是用在方法上?比如Override就是用在方法上的注解,Deprecated是既可以用在方法上面,也可以用在类上面,我们直接看看Override是怎么实现的吧


@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}@Documented@Retention(RetentionPolicy.RUNTIME)@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})public @interface Deprecated {}

我们发现这两个注解在定义的过程中都使用了其他的注解,像Target和Retention这种注解叫做元注解,我们分别看看它们的意思吧
Target注解的功能就是表明你这个注解是用在什么地方的,它的值是一个枚举型
  1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明


Retention注解的功能差不多说明的就是你的注解的生命周期吧,就是什么时候失效,它的值如下
1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)


我就介绍这两个元注解吧,其他的感觉也用的不是很多,如果你感兴趣可以自己Google一下

那么我们就完善我们自己的注解吧


@Target(ElementType.METHOD)//作用于方法@Retention(RetentionPolicy.RUNTIME)//在运行时有效(即运行时保留)public @interface TestPerson{  //name是属性而不是方法,gavin是它的默认值,在定义的时候可以不用给定默认值  String name() default "gavin";}@Target(ElementType.TYPE)//作用于类上@Retention(RetentionPolicy.RUNTIME)//在运行时有效(即运行时保留)public @interface ClassFunction{  String value() default "";}

那么我们就来使用一下我们的注解吧

@ClassFunction("用于描述一个人的基本信息")public class Person{  private static final String TAG = "Person";    @TestPerson(name="jj")  public void setName()  {      }}

这里要说明一点就是:如果某个注解属性使用value作为名称如ClassFunction中的value,那么赋值[email protected]("用于描述一个人的基本信息"),但是如果你使用的是其他名称,[email protected](name="jj")这样调用

以上就是定义一个注解的过程,下面我们来解析一个注解
定义一个TestPerson注解

/** * com.annotation.TestPerson * @author yuanzeyao <br/> * create at 2014年5月21日 下午1:30:14 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface TestPerson {    String name();}

然后再Person类中加入注解

public class Person {  private static final String TAG = "Person";  @TestPerson(name="gavin")  public void getName()  {      }}

解析注解

public class Main {  private static final String TAG = "Main";  public static void main(String[] args)   {     Person person=new Person();	 //获得Person对应的Class     Class<Person> clazz=Person.class;     try {	  //找到getName方法      Method method=clazz.getMethod("getName",null);	  //判断是否被TestPerson标注      if(method.isAnnotationPresent(TestPerson.class))      {		//调用getName方法        method.invoke(person, null);		//得到TestPerson注解的实例        TestPerson test=method.getAnnotation(TestPerson.class);		//获得其name属性        String name=test.name();        System.out.println("this method is test by-->"+name);      }    } catch (SecurityException e) {    } catch (NoSuchMethodException e) {    } catch (IllegalArgumentException e) {    } catch (IllegalAccessException e) {    } catch (InvocationTargetException e) {    }  }}

已经将定义注解和解析注解讲解完了,下面来具体分析一下怎么在Android中使用注解来代替findViewById
定义一个注解

@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface InjectView {  //id就是控件id,在某一个控件上使用注解标注其id  int id() default -1;}

在Activity中加入注解

public class MainActivity extends Activity {  public static final String TAG="MainActivity";  //标注TextView的id  @InjectView(id=R.id.tv_img)  private TextView mText;    @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    try {      autoInjectAllField(this);    } catch (IllegalAccessException e) {    } catch (IllegalArgumentException e) {    }        if(mText!=null)      mText.setText("Hello Gavin");  }    public void autoInjectAllField(Activity activity) throws IllegalAccessException, IllegalArgumentException  {	//得到Activity对应的Class    Class clazz=this.getClass();	//得到该Activity的所有字段    Field []fields=clazz.getDeclaredFields();    Log.v(TAG, "fields size-->"+fields.length);    for(Field field :fields)    {	  //判断字段是否标注InjectView      if(field.isAnnotationPresent(InjectView.class))      {        Log.v(TAG, "is injectView");		//如果标注了,就获得它的id        InjectView inject=field.getAnnotation(InjectView.class);        int id=inject.id();        Log.v(TAG, "id--->"+id);        if(id>0)        {		  //反射访问私有成员,必须加上这句          field.setAccessible(true);		  //然后对这个属性复制          field.set(activity, activity.findViewById(id));        }      }    }  }}

好了,就写到这里吧,欢迎留言讨论

  相关解决方案