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一樣的效果了。