由于项目需要,开始研究ThinkAndroid。
个人认为该框架的注解机制十分新颖,所以先研究这个,顺便学习下 Java 的annotation。
粗略的看了看,该机制在BaseActivity中初始化。而BaseActivity是所有Activity的基类。
对BaseActivity进行了代码剖离,发现在BaseActivity中在onCreate函数里启动注解机制。
暂时先搞清楚第二个,怎么绑定组件资源的:
我写了个小demo,MyAnno
InjectView.java
package com.myanno; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解,获取资源ID * @使用方法 * @InjectView(id = R.id.settingIv) * private ImageView imgSetting; * * @author [email protected] * @date 2013-10-29 * @description @Retention: 定义注解的保留策略 * @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含 * @Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得, * @Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到 * @Inherited //说明子类可以继承父类中的该注解 * * @Target(ElementType.TYPE) //接口、类、枚举、注解 * @Target(ElementType.FIELD) //字段、枚举的常量 * @Target(ElementType.METHOD) //方法 * @Target(ElementType.PARAMETER) //方法参数 * @Target(ElementType.CONSTRUCTOR) //构造函数 * @Target(ElementType.LOCAL_VARIABLE) //局部变量 * @Target(ElementType.ANNOTATION_TYPE)//注解 * @Target(ElementType.PACKAGE) //包 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface InjectView { /** View的ID */ public int id() default -1; }
MainActivity.java
package com.myanno; import java.lang.reflect.Field; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.TextView; public class MainActivity extends Activity { /** 注解绑定UI元素 */ @InjectView(id=R.id.myimgview) ImageView myimageview; @InjectView(id=R.id.mytext) TextView mytext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化注解绑定的成员变量 injectView(this); //直接使用UI元素 mytext.setText("Text0"); myimageview.setImageResource(R.drawable.junny); } /** 实例化@InjectView 注解的成员*/ public void injectView(Activity activity) { Field[] fields = activity.getClass().getDeclaredFields(); //得到Activity中的所有定义的字段 if (fields != null && fields.length > 0) { for (Field field : fields) { if (field.isAnnotationPresent(InjectView.class)) //方法返回true,如果指定类型的注解存在于此元素上 { Log.i("Field", field.toString()); InjectView mInjectView = field.getAnnotation(InjectView.class); //获得该成员的annotation int viewId = mInjectView.id(); //获得该注解的id View view=activity.findViewById(viewId); //获得ID为viewID的组件对象 Log.i("Field", String.valueOf(viewId)); Log.i("Field", view.getClass().toString()); try { field.setAccessible(true); //设置类的私有成员变量可以被访问 field.set(activity, view); //field.set(object,value)===object.fieldValue = value } catch (Exception e) { e.printStackTrace();} } else Log.i("Field", "该字段没有被注解"); } } } }
布局文件 activity_main.xml
<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:background="#000000" android:orientation="vertical" tools:ignore="ContentDescription" > <ImageView android:id="@+id/myimgview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"/> <TextView android:id="@+id/mytext" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="center" android:background="@android:color/darker_gray" android:textSize="30sp" /> </LinearLayout>
实验结果
即注入成功了
需要说下我遇到的问题,在以上的基础上,如果将布局文件里的TextView 和ImageView两个布局换个位置,这时候再运行下,会出现空指针异常。
将Log向上翻会发现一个警告
即非法参数异常
定位到这一行
field.set(activity, view);
通过观察Log打印的日志
发现第一二行是对的,即获得注解的类型和ID(ImageView 2131230720)都是正确的,但是通过findViewById获取view的时候,Log第三行显示的却是TextView。
百思不得其解,最后无赖之下,清理一下项目,搞定。
我不知道是怎么回事,暂且推测为资源缓存吧。
转载请注明出处!