Android开发学习之路--Annotation注解简化view控件之初体验

来源:互联网 发布:天猫双11晚会网络直播 编辑:程序博客网 时间:2024/06/06 00:48

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

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. /** 
  9.  * Created by jared on 16/3/10. 
  10.  */  
  11. @Target(ElementType.TYPE)  
  12. @Retention(RetentionPolicy.RUNTIME)  
  13. public @interface EMLayoutBinder {  
  14.     int value();  
  15. }  

    这里的@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类:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import android.app.Activity;  
  4. import android.view.View;  
  5.   
  6. import java.lang.reflect.Field;  
  7.   
  8. /** 
  9.  * Created by jared on 16/3/10. 
  10.  */  
  11. public class EMAnnotationParser {  
  12.     public static void injectActivity(Activity activity) {  
  13.         if (null == activity) {  
  14.             return;  
  15.         }  
  16.         Class<Activity> activityClass = (Class<Activity>) activity.getClass();  
  17.         if (isEMLayoutBinder(activityClass)) {  
  18.             EMLayoutBinder layout = activityClass.getAnnotation(EMLayoutBinder.class);  
  19.             activity.setContentView(layout.value());  
  20.         }  
  21.         View decorView = activity.getWindow().getDecorView();  
  22.     }  
  23.   
  24.     private static boolean isEMLayoutBinder(Class<?> c) {  
  25.         return c.isAnnotationPresent(EMLayoutBinder.class);  
  26.     }  


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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import android.support.v7.app.AppCompatActivity;  
  4. import android.os.Bundle;  
  5.   
  6. @EMLayout(R.layout.activity_main)  
  7. public class MainActivity extends AppCompatActivity {  
  8.   
  9.     @Override  
  10.     protected void onCreate(Bundle savedInstanceState) {  
  11.         super.onCreate(savedInstanceState);  
  12.         EMAnnotationParser.injectActivity(this);  
  13.         //setContentView(R.layout.activity_main);  
  14.     }  
  15. }  

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import android.os.Bundle;  
  4. import android.support.v7.app.AppCompatActivity;  
  5.   
  6. /** 
  7.  * Created by jared on 16/3/10. 
  8.  */  
  9. public class BaseActivity extends AppCompatActivity {  
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         EMAnnotationParser.injectActivity(this);  
  14.     }  
  15. }  

    然后实现MainActivity继承BaseActivity:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import android.os.Bundle;  
  4. import android.widget.Button;  
  5. import android.widget.TextView;  
  6.   
  7. @EMLayoutBinder(R.layout.activity_main)  
  8. public class MainActivity extends BaseActivity {  
  9.   
  10.     @EMViewBinder(R.id.hello)  
  11.     private TextView mHello;  
  12.     @EMViewBinder(R.id.test1)  
  13.     private Button mTest1;  
  14.     @EMViewBinder(R.id.test2)  
  15.     private Button mTest2;  
  16.   
  17.     @Override  
  18.     protected void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         mHello.setText("Hello Annotation!");  
  21.     }  
  22. }  


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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. /** 
  9.  * Created by jared on 16/3/10. 
  10.  */  
  11.   
  12. @Target(ElementType.FIELD)  
  13. @Retention(RetentionPolicy.RUNTIME)  
  14. public @interface EMViewBinder {  
  15.     int value();  
  16. }  

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import android.app.Activity;  
  4. import android.view.View;  
  5.   
  6. import java.lang.reflect.Field;  
  7.   
  8. /** 
  9.  * Created by jared on 16/3/10. 
  10.  */  
  11. public class EMAnnotationParser {  
  12.     public static void injectActivity(Activity activity) {  
  13.         if (null == activity) {  
  14.             return;  
  15.         }  
  16.         Class<Activity> activityClass = (Class<Activity>) activity.getClass();  
  17.         if (isEMLayoutBinder(activityClass)) {  
  18.             EMLayoutBinder layout = activityClass.getAnnotation(EMLayoutBinder.class);  
  19.             activity.setContentView(layout.value());  
  20.         }  
  21.         View decorView = activity.getWindow().getDecorView();  
  22.         initViews(activityClass.getDeclaredFields(), decorView, activity);  
  23.     }  
  24.   
  25.     private static boolean isEMLayoutBinder(Class<?> c) {  
  26.         return c.isAnnotationPresent(EMLayoutBinder.class);  
  27.     }  
  28.   
  29.     private static boolean isEMViewBinder(Field filed) {  
  30.         return filed.isAnnotationPresent(EMViewBinder.class);  
  31.     }  
  32.   
  33.     private static void initViews(Field[] fields, View view, Object object) {  
  34.         View view1;  
  35.         for (Field field : fields) {  
  36.             if(isEMViewBinder(field)) {  
  37.                 EMViewBinder emView = field.getAnnotation(EMViewBinder.class);  
  38.                 view1 = view.findViewById(emView.value());  
  39.                 if(null != view1) {  
  40.                     try {  
  41.                         field.setAccessible(true);  
  42.                         field.set(object, view1);  
  43.                     } catch (IllegalAccessException e) {  
  44.                         e.printStackTrace();  
  45.                     } catch (IllegalArgumentException e) {  
  46.                         e.printStackTrace();  
  47.                     }  
  48.                 }  
  49.             }  
  50.         }  
  51.     }  
  52. }  

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

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     android:orientation="vertical"  
  8.     android:layout_margin="10dp"  
  9.     tools:context="com.jared.emannotationstudy.MainActivity">  
  10.   
  11.     <TextView  
  12.         android:id="@+id/hello"  
  13.         android:text="Hello World!"  
  14.         android:layout_width="wrap_content"  
  15.         android:layout_height="wrap_content"  
  16.         android:textSize="20sp"  
  17.         android:layout_gravity="center"/>  
  18.   
  19.     <Button  
  20.         android:id="@+id/test1"  
  21.         android:layout_width="match_parent"  
  22.         android:layout_height="wrap_content"  
  23.         android:text="test1"  
  24.         android:textAllCaps="false"/>  
  25.   
  26.     <Button  
  27.         android:id="@+id/test2"  
  28.         android:layout_width="match_parent"  
  29.         android:layout_height="wrap_content"  
  30.         android:text="test2"  
  31.         android:textAllCaps="false"/>  
  32. </LinearLayout>  

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import android.os.Bundle;  
  4. import android.widget.Button;  
  5. import android.widget.TextView;  
  6.   
  7. @EMLayoutBinder(R.layout.activity_main)  
  8. public class MainActivity extends BaseActivity {  
  9.   
  10.     @EMViewBinder(R.id.hello)  
  11.     private TextView mHello;  
  12.     @EMViewBinder(R.id.test1)  
  13.     private Button mTest1;  
  14.     @EMViewBinder(R.id.test2)  
  15.     private Button mTest2;  
  16.   
  17.     @Override  
  18.     protected void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         mHello.setText("Hello Annotation!");  
  21.     }  
  22. }  

    运行看下效果如下:


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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. /** 
  9.  * Created by jared on 16/3/10. 
  10.  */  
  11. @Target(ElementType.METHOD)  
  12. @Retention(RetentionPolicy.RUNTIME)  
  13. public @interface EMOnClickBinder {  
  14.     int[] value();  
  15. }  

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private static boolean isEMOnClickBinder(Method method) {  
  2.         return method.isAnnotationPresent(EMOnClickBinder.class);  
  3.     }  
  4.   
  5.     private static void initOnClick(Method[] allMethod, View root, Object object) {  
  6.         for (Method method : allMethod) {  
  7.             if (isEMOnClickBinder(method)) {  
  8.                 EMOnClickBinder onClick = method.getAnnotation(EMOnClickBinder.class);  
  9.                 MyOnClickListener click = new MyOnClickListener(method, object);  
  10.                 int[] ids = onClick.value();  
  11.                 for (int id : ids) {  
  12.                     root.findViewById(id).setOnClickListener(click);  
  13.                 }  
  14.             }  
  15.         }  
  16.     }  
  17.   
  18.     static class MyOnClickListener implements View.OnClickListener {  
  19.         private Method mMethod;  
  20.         private Object mReceiver;  
  21.   
  22.         public MyOnClickListener(Method method, Object receiver) {  
  23.             mMethod = method;  
  24.             mReceiver = receiver;  
  25.         }  
  26.   
  27.         @Override  
  28.         public void onClick(View v) {  
  29.             try {  
  30.                 mMethod.setAccessible(true);  
  31.                 mMethod.invoke(mReceiver, v);  
  32.             } catch (IllegalAccessException e) {  
  33.                 e.printStackTrace();  
  34.             } catch (IllegalArgumentException e) {  
  35.                 e.printStackTrace();  
  36.             } catch (InvocationTargetException e) {  
  37.                 e.printStackTrace();  
  38.             }  
  39.         }  
  40.     }  

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.jared.emannotationstudy;  
  2.   
  3. import android.os.Bundle;  
  4. import android.view.View;  
  5. import android.widget.Button;  
  6. import android.widget.TextView;  
  7.   
  8. @EMLayoutBinder(R.layout.activity_main)  
  9. public class MainActivity extends BaseActivity {  
  10.   
  11.     @EMViewBinder(R.id.hello)  
  12.     private TextView mHello;  
  13.     @EMViewBinder(R.id.test1)  
  14.     private Button mTest1;  
  15.     @EMViewBinder(R.id.test2)  
  16.     private Button mTest2;  
  17.   
  18.     @Override  
  19.     protected void onCreate(Bundle savedInstanceState) {  
  20.         super.onCreate(savedInstanceState);  
  21.         mHello.setText("Hello Annotation!");  
  22.     }  
  23.   
  24.     @EMOnClickBinder({R.id.test1, R.id.test2})  
  25.     public void myOnClick(View view) {  
  26.         switch (view.getId()) {  
  27.             case R.id.test1:  
  28.                 mHello.setText("I am test1");  
  29.                 break;  
  30.             case R.id.test2:  
  31.                 mHello.setText("I am test2");  
  32.             default:  
  33.                 break;  
  34.         }  
  35.     }  
  36. }  

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