Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 自定義注解框架

Android 自定義注解框架

編輯:關於Android編程

前言

在我們的項目中,我們幾乎天天和一些固定的代碼打交道,比如在Activity中你要寫findViewById(int)方法來找到控件,然而這樣子的代碼對於一個稍微有點資格的程序員來說,都是毫無營養的,你根本學不到任何的東西,但是你卻必須寫。這也就是注解框架的出現,極大的簡化了程序員的工作,並且讓代碼簡潔。也許你早就使用過了注解的框架,那麼你會自己自己寫麼?好了,今天就讓大家來完成一個注解的框架

閱讀的你需要掌握的知識

1.Java反射的知識

2.Java注解的知識

普通的寫法

xml布局文件,就一個按鈕
<relativelayout android:layout_height="match_parent" android:layout_width="match_parent" tools:context="${relativePackage}.${activityClass}" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"><button android:id="@+id/bt" android:layout_height="wrap_content" android:layout_width="match_parent" android:text="點我"></button></relativelayout>


Activity中的代碼

public class MainActivity extends Activity implements OnClickListener {

	/**
	 * 按鈕
	 */
	private Button bt = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		// 找到按鈕
		bt = (Button) findViewById(R.id.bt);
		// 設置點擊事件
		bt.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		Toast.makeText(this, "點我了", Toast.LENGTH_LONG).show();
	}

}
也是很簡單的,然後是效果圖 \   上面就是我們平常的寫法,那麼我們來看看用了注解框架的寫法,先體驗,再動手寫注解框架

用了注解框架的寫法

public class MainActivity extends Activity {

	/**
	 * 按鈕
	 */
	@Injection(value = R.id.bt,click = "clickView")
	private Button bt = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//讓注解起作用
		ViewInjectionUtil.injectView(this);
	}
	
	public void clickView() {
		Toast.makeText(this, "點我了,用了注解框架", Toast.LENGTH_LONG).show();
	}

}


就這麼少代碼?是的,你沒有看錯,就這麼點代碼,和剛剛的普通的寫法的效果是一模一樣的 可以注意到Activity實現的點擊事件的接口不見了,尋找控件和設置監聽的代碼都不見了,當一個項目足夠大了,是不是很省下很多的代碼呢? 只是多了一句讓注解起作用的代碼,看下用了注解框架的效果 \   可以看到,程序是沒有問題的,那麼讓我們來實現它吧!  

思路

1.首先明確一點,框架一定是幫你做了findViewById(int)和setOnClickListener(View.OnCLickListener)的這些操作,否則代碼是不可能正常運行的 2.findViewById(int)方法需要的是控件的id,從框架的使用上看,控件的id就是利用注解告訴了框架
@Injection(value = R.id.bt,click = "clickView")
此處的R.id.bt就是value的值,所以可以知道就是這裡告訴框架的,而我們的點擊事件起作用了,這裡告訴了框架需要調用的方法的名稱所以我們的一個和普通的方法可以被調用  

思路的總結

所以注解就是一個信息,它會被框架讀取並且框架會幫你做一些繁瑣的工作,從而讓你的代碼編輯簡潔,所以用了注解之後,比如讓框架來讀取,所以這也就是為什麼會有一句很陌生的語句:
//讓注解起作用
ViewInjectionUtil.injectView(this);
這句代碼就是框架讀取了注解中的信息並且幫你找到了控件,並且注冊相應的事件  

開工寫代碼

首先定義創建一個注解

就是上述使用的那個注解:@Injection,你可以理解為創建一個類差不多的概念,但是兩者是不同的,自己要清楚哦
/**
 * 運行時期有效,和針對的是字段
 * 
 * @author xiaojinzi
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Injection {

	/**
	 * 字段對應布局文件的id
	 * 
	 * @return
	 */
	int value();

	/**
	 * 點擊事件
	 * @return
	 */
	String click() default "";

	/**
	 * 長按的點擊事件
	 * @return
	 */
	String longClick() default "";

}
這裡對代碼做一下解釋@Target(ElementType.FIELD)表示我們聲明的注解是作用到字段上的,你肯定見過作用在方法上的注解,比如:@Override 這個不陌生吧? @Retention(RetentionPolicy.RUNTIME)這個表示我們聲明的注解是運行的時候有效的,還有其他的情況,這裡不做深入 好了,我們的注解就寫好了,注解中有三個屬性可以用,分別是: 1.value int類型 表示控件的id 2.click String類型 點擊事件的方法名稱 3.longClick String類型 長按事件的方法名稱 好了,我們的注解就寫完了,你可以在任何一個字段上面使用這個注解了,都是不會報錯的,但是還沒有實際的作用  

編寫核心代碼

首先創建一個類,用來讓注解起作用,也就是之前說過的,去讀取注解中的信息,然後完成繁瑣的操作 我就起一個名字為:ViewInjectionUtil,你可以自己起一個其他的名稱 框架幫你完成findViewById(int)的操作那麼是不是你需要把當前的Activity對象傳給框架呢?
/**
* 對activity中的字段進行注入
*
* @param act
*/
public static void injectView(Activity act) {
        // 獲取這個activity中的所有字段
        Field[] fields = act.getClass().getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
                // 循環拿到每一個字段
                Field field = fields[i];
                if (field.isAnnotationPresent(Injection.class)) { // 如果這個字段有注入的注解
                        // 獲取注解對象
                        Injection injection = field.getAnnotation(Injection.class);
                        int value = injection.value();
                        field.setAccessible(true); // 即使私有的也可以設置數據
                        Object view = null;
                        try {
                                view = act.findViewById(value);
                                // 設置字段的屬性
                                field.set(act, view);
                        } catch (Exception e) {
                                e.printStackTrace();
                                L.s(TAG, "注入屬性失敗:" + field.getClass().getName() + ":" + field.getName());
                        }

                        try {
                                if (view instanceof View) {
                                        View v = (View) view;
                                        // 獲取點擊事件的觸發的方法名稱
                                        String methodName = injection.click();
                                        EventListener eventListener = null;
                                        // 如果不是空字符串
                                        if (!"".equals(methodName)) {
                                                eventListener = new EventListener(act);
                                                // 設置點擊事件
                                                v.setOnClickListener(eventListener);
                                                eventListener.setClickMethodName(methodName);
                                        }
                                        methodName = injection.longClick();
                                        if (!"".equals(methodName)) {
                                                if (eventListener == null) {
                                                        eventListener = new EventListener(act);
                                                }
                                                // 設置點擊事件
                                                v.setOnLongClickListener(eventListener);
                                                eventListener.setLongClickMethodName(methodName);
                                        }
                                }
                                } catch (Exception e) {
                                        e.printStackTrace();
                                }

                        }
                }
}
 
/**
 * Created by cxj on 2016/1/21.
 * 
 * @author 小金子
 */
public class EventListener implements View.OnClickListener, View.OnLongClickListener {

	/**
	 * 類的標識
	 */
	private String tag = "EventListener";

	/**
	 * 設置是否有日志的輸出
	 */
	private boolean isLog = false;

	/**
	 * 反射中要被調用方法的對象,通過構造方法進來
	 */
	private Object receiver = null;

	/**
	 * 點擊事件的方法名字
	 */
	private String clickMethodName = "";

	/**
	 * 長按事件的方法的名字
	 */
	private String longClickMethodName = "";

	/**
	 * 設置點擊事件的方法名字
	 *
	 * @param clickMethodName
	 */
	public void setClickMethodName(String clickMethodName) {
		this.clickMethodName = clickMethodName;
	}

	/**
	 * 設置長按的點擊事件的方法的名字
	 *
	 * @param longClickMethodName
	 */
	public void setLongClickMethodName(String longClickMethodName) {
		this.longClickMethodName = longClickMethodName;
	}

	/**
	 * 構造函數
	 * 
	 * @param receiver
	 *            控件所在的activity或者Fragment
	 */
	public EventListener(Object receiver) {
		this.receiver = receiver;
	}

	@Override
	public void onClick(View v) {
		Method method = null;
		try {
			method = receiver.getClass().getMethod(clickMethodName);
			if (method != null) {
				// 調用該方法
				method.invoke(receiver);
			}
		} catch (Exception e) {
			if (isLog)
				L.s(tag, "尋找無參數列表的方法:" + clickMethodName + "失敗");
		}
		try {
			if (method == null) {
				method = receiver.getClass().getMethod(clickMethodName, View.class);
				if (method != null) {
					method.invoke(receiver, v);
				}
			}
		} catch (Exception e) {
			if (isLog)
				L.s(tag, "尋找帶有View類型參數的方法:" + clickMethodName + "失敗");
		}
	}

	@Override
	public boolean onLongClick(View v) {
		Method method = null;
		try {
			method = receiver.getClass().getMethod(longClickMethodName);
			if (method != null) {
				// 調用該方法
				method.invoke(receiver);
			}
		} catch (Exception e) {
			if (isLog)
				L.s(tag, "尋找無參數列表的方法:" + longClickMethodName + "失敗");
		}
		try {
			if (method == null) {
				method = receiver.getClass().getMethod(longClickMethodName, View.class);
				if (method != null) {
					method.invoke(receiver, v);
				}
			}
		} catch (Exception e) {
			if (isLog)
				L.s(tag, "尋找帶有View類型參數的方法:" + longClickMethodName + "失敗");
		}
		return true;
	}

}
  類EventListener:這個類主要是處理View的事件的,比如點擊事件,長按事件,類實現了這些接口 創建該類的時候需要傳入Activity對象或者Fragment對象,比如點擊事件觸發的時候該類中實現的方法onClick(View)方法被調用,但是並不是用戶指定的方法名的那個方法,所以這裡需要有注解中的click字符串信息,也就是Activity需要調用的方法的方法名,有了方法名和Activity,就可以利用反射從activity中調用指定方法名的方法,從而達到我們開始的時候演示的時候那樣子,點擊按鈕的時候,會調用activity中的bt字段上的注解中的click寫的那個方法名對應的方法

  這裡對方法injectView(Activity act)中的代碼做一個詳細的解釋: 1.利用反射獲取到activity中所有的字段 2.循環所有的字段,篩選出有Injection注解的字段 3.讀取字段中的注解中的信息: a)拿到控件的id信息,調用act中的findViewById(int)找到控件,利用反射賦值給字段,這個過程等同於普通代碼: bt = (Button)findViewById(R.id.bt); b)拿到點擊事件和長按事件觸發的時候調用的方法的方法名,然後創建EventListener對象,讓這個對象保存點擊事件的方法名稱和長按事件的方法名稱和activity的引用,並且給View對象注冊相應的事件,EventListener中已經實現了對應的接口,所以注冊的時候,傳入的對象就是EventListener的對象,後續事件生效的時候就是上述類EventListener的功能了   到這裡其實我們的代碼就寫完了,其實你懂反射的知識的話,這些代碼都是不難的,看起來會很快,當然了,如果你不懂反射的知識,這裡建議你去學習一下,作為一個JavaCoder,我覺得反射這麼棒的內容,你不應該錯過!因為在Java的世界中到處都是它的影子!

總結

博主只介紹了點擊事件和長按事件,其實你可以照貓畫虎的加入其他更多的事件,讓你的框架支持更多的操作,也更強大 另外,本篇博客其實給大家一個思路,我相信你們能寫出更好的小框架的! 只為了讓編碼變的更加輕松!

小框架下載

注解框架下載地址:https://github.com/xiaojinzi123/xiaojinzi-openSource-viewAnnotation
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved