標籤:
本文主要是記錄註解的使用的學習筆記,如有錯誤請提出。
在通常的情況下,我們在Activity中有一個View,我們要獲得這個View的執行個體是要通過findViewById這個方法,然後這個方法返回的是一個Object類型,我們還需要進行強制的類型轉換,但是相信很多人都遇到過,當我們的一個布局中有很多個控制項的時候,每一個控制項都要進行上面的這個操作其實是很煩躁的,特別是強制類型轉換,即使是用Alt+Enter,多按幾次都累了。而今天要用的是通過註解的方式來簡化這一個複雜的步驟,在我們編寫好相應的代碼之後,擷取執行個體的方法就變成像下面這樣簡單了
@ViewInject(R.id.buy)private Button buy;
通過上面這兩行簡單的代碼,就能把id所對應的View執行個體化了,下面我們一步一步的來學習如何使用註解。
先說原理,我們都要知道,在Java中,通過反射,我們可以知道每一個類的詳細資料,比如有什麼欄位,有什麼方法,類名等等,我們通過註解和反射配合,使用反射調用類中的方法,然後讀取註解的參數來進行方法的執行。簡單的說,就是其實我們還是會調用findViewById這個方法,但是,這個方法可以放到工具類中執行了,我們只需要像上面那樣給出參數就行了。
先說下反射,反射就是允許我們動態操作一個類,擷取類的欄位,執行類的方法,擷取類的名字等等,在這裡我們一般會用到下面的這幾個方法:
getMethod:擷取類中的public方法
getDeclaredMethod:擷取類中的所有方法
getField:擷取類中的public欄位(屬性)
getDeclaredField:擷取類中的所有欄位
還有其他的請自行參考Class類的源碼
再說註解,我們看到的最多的註解擷取是@Override,當我們重寫父類中的方法的時候,這個註解會被編譯器自動產生出來,這個註解只是表明方法是重寫了父類方法。
那麼我們究竟該如何自訂我們想要的註解呢?其實很簡單,直接看代碼:
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface ViewInject{ int value();}
@Target的意思是我們註解作用的目標,這裡是ElementType.FIELD,也就是作用於欄位的
ElementType的類型有以下幾種:
1.CONSTRUCTOR:用於描述構造器
2.FIELD:用於描述欄位
3.LOCAL_VARIABLE:用於描述局部變數
4.METHOD:用於描述方法
5.PACKAGE:用於描述包
6.PARAMETER:用於描述參數
7.TYPE:用於描述類、介面(包括註解類型) 或enum聲明
@Retention的意思是註解的運行層級
RetentionPolicy的類型有以下幾種
1.SOURCE:在源檔案中有效(即源檔案保留)
2.CLASS:在class檔案中有效(即class保留)
3.RUNTIME:在運行時有效(即運行時保留)
@interface則是表明這個類是一個註解,@號不能漏掉,否則變成了介面了
如果想對註解瞭解得更多,可以參考:http://www.cnblogs.com/gmq-sh/p/4798194.html 這篇博文,很詳細,對於我們想要的功能,以上的內容就足夠了
定義好了註解,如果我們直接來使用,是沒有任何效果的,因為註解只是一段代碼,並沒有關聯上我們的控制項,所以我們要編寫一個工具類來做關聯
public class AnnotateUtils { public static void injectViews(Activity activity) { Class<? extends Activity> object = activity.getClass(); // 擷取activity的Class Field[] fields = object.getDeclaredFields(); // 通過Class擷取activity的所有欄位 for (Field field : fields) { // 遍曆所有欄位 // 擷取欄位的註解,如果沒有ViewInject註解,則返回null ViewInject viewInject = field.getAnnotation(ViewInject.class); if (viewInject != null) { int viewId = viewInject.value(); // 擷取欄位註解的參數,這就是我們傳進去控制項Id if (viewId != -1) { try { // 擷取類中的findViewById方法,參數為int Method method = object.getMethod("findViewById", int.class); // 執行該方法,返回一個Object類型的View執行個體 Object resView = method.invoke(activity, viewId); field.setAccessible(true); // 把欄位的值設定為該View的執行個體 field.set(activity, resView); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } }}
在工具類中,我們擷取到了activity的Class,接著獲得類中的所有欄位,遍曆欄位,如果有ViewInject註解,則擷取註解的中的值,通過擷取並執行類中的方法(findViewById)來獲得對應View的執行個體,最後把執行個體賦值給當前的欄位,整個過程就完成了。
當我們需要使用註解的時候,我們只需要在定義View的時候,在View的欄位上添加對應的註解,把Id傳入,然後在onCreate方法中調用上面這個工具類的方法即可。
@ViewInject(R.id.buy)private Button buy;@ViewInject(R.id.money)private TextView money;@ViewInject(R.id.tv_power)private TextView power;@ViewInject(R.id.tv_life)private TextView life;@ViewInject(R.id.tv_dex)private TextView dex;
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); AnnotateUtils.injectViews(this);}
最後,有幾點說明:
①如果註解中的值不是value,那麼在進行註解是時候,需要給出對應的值的名字,假如我們在註解中做了如下定義:
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface ViewInject{ int id();}
那麼在註解的時候,需要這樣:
@ViewInject(id = R.id.buy)private Button buy;
因為value這個名字是預設的,如果我們定義為value,那麼註解的時候可以省略
②註解還可以幫我們注入布局,設定監聽等,如果有興趣,可以繼續探討下去
③使用註解的時候,句末不能加“;”
Android開發學習之路-讓註解幫你簡化代碼,徹底拋棄findViewById