Android開發學習之路-讓注解幫你簡化代碼,徹底拋棄findViewById


本文主要是記錄注解的使用的學習筆記,如有錯誤請提出。

在通常的情況下,我們在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;
@Override
protected 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,那么注解的時候可以省略

②注解還可以幫我們注入布局,設置監聽等,如果有興趣,可以繼續探討下去

③使用注解的時候,句末不能加“;”

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM