1. 程式人生 > >Android 使用自定義註解代替重複寫findViewById程式碼

Android 使用自定義註解代替重複寫findViewById程式碼

效果

每次新建頁面控制元件的findViewById是每個android開發者的痛苦。在這方面已經有很多第三方框架幫我們解放了雙手,這次就是利用註解來解決findViewById。

public class ObserverActivity extends AppCompatActivity{
    @ViewInject(R.id.activity_btn)
    Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_observer);
        AnnotateUtils.injectViews(this
); button.setText("newText"); } }

一、實現原理

在Java中,通過反射,我們可以獲取每一個類的詳細資訊,比如有什麼屬性欄位,有什麼方法,類名等,我們通過註解和反射配合,使用反射呼叫類中的屬性,然後讀取註解的引數來進行屬性的賦值。簡單的說,就是其實我們還是會呼叫findViewById這個方法,但是,這個方法可以放到工具類中執行了,我們只需要像上面那樣給出引數就行了。這個形式就和註解框架ButterKnife一樣

二、建立自定義註解

新建一個註解。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public
@interface ViewInject { int value(); }

其中@Target的意思是我們註解的目標,這裡是ElementType.FIELD,也就是作用於屬性的。
它的型別有以下幾種:
- 1.CONSTRUCTOR:用於描述構造器
- 2.FIELD:用於描述欄位
- 3.LOCAL_VARIABLE:用於描述區域性變數
- 4.METHOD:用於描述方法
- 5.PACKAGE:用於描述包
- 6.PARAMETER:用於描述引數
- 7.TYPE:用於描述類、介面(包括註解型別) 或enum宣告

然後@Retention的意思是註解的執行級別
它的型別有以下幾種
- 1.SOURCE:在原始檔中有效(即原始檔保留)
- 2.CLASS:在class檔案中有效(即class保留)
- 3.RUNTIME:在執行時有效(即執行時保留)

三、注入註解的工具類

public class AnnotateUtils {
    public static void injectViews(Activity activity) {
        // 獲取activity的Class
        Class<? extends Activity> object = activity.getClass();
        // 通過Class獲取activity的所有屬性
        Field[] fields = object.getDeclaredFields();
        for (Field field : fields) {
            // 獲取欄位的註解,如果沒有ViewInject註解,則返回null
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {
                // 獲取屬性的註解的引數,這就是控制元件的id
                int viewId = viewInject.value();
                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的所有的Filed陣列,然後遍歷它們,檢驗有自定義註解ViewInfect的屬性。
然後獲取註解的引數Id,通過反射呼叫findViewById來獲取指定View給該屬性賦值。

這樣就可以做到ButterKnife一樣的效果了。