由於項目需要,開始研究ThinkAndroid。
個人認為該框架的注解機制十分新穎,所以先研究這個,順便學習下 Java 的annotation。
粗略的看了看,該機制在BaseActivity中初始化。而BaseActivity是所有Activity的基類。
對BaseActivity進行了代碼剖離,發現在BaseActivity中在onCreate函數裡啟動注解機制。
首先注入布局資源(綁定layout布局)
其次注入成員資源(綁定組件資源)
然後注入成員變量(初始化普通變量)
暫時先搞清楚第二個,怎麼綁定組件資源的:
我寫了個小demo,MyAnno
InjectView.java
[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
[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
[html]
<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向上翻會發現一個警告
即非法參數異常
定位到這一行
[java]
field.set(activity, view);
通過觀察Log打印的日志
發現第一二行是對的,即獲得注解的類型和ID(ImageView 2131230720)都是正確的,但是通過findViewById獲取view的時候,Log第三行顯示的卻是TextView。
百思不得其解,最後無賴之下,清理一下項目,搞定。
我不知道是怎麼回事,暫且推測為資源緩存吧。