紅橙Darren視訊筆記 IOC註解框架 自己寫個註解框架
阿新 • • 發佈:2021-02-19
技術標籤:架構篇1上
目標
目標就是不需要進行一大堆的findviewbyid以及各種setListener的雜亂程式碼,本文以findviewbyid和setOnClickListener為例 介紹如何進行屬性繫結和事件繫結
開始實現
1.建立測試module
public class ActivityIocTest extends AppCompatActivity { @ViewById(R.id.tv) TextView mTextView; @ViewById(R.id.btn) Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_ioc_test); ViewUtils.injectActivity(this); mTextView.setText("Tv text!!!!"); mButton.setText("Button text!!!!"); } @OnClick({R.id.tv,R.id.btn}) public void onItemClick(View view){ switch (view.getId()){ case R.id.tv: Toast.makeText(this,"text view clicked",Toast.LENGTH_SHORT).show(); break; case R.id.btn: Toast.makeText(this,"button clicked",Toast.LENGTH_SHORT).show(); break; } } } <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Ioc Test" /> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Ioc Test" /> </LinearLayout>
2.建立ioc module 專門生成註解,進行屬性和事件繫結
3.測試module依賴ioc模組
dependencies {
...
implementation project(path: ':ioc')//新增ioc模組註解依賴
}
4.編寫ioc模組
4.1 註解的宣告
/** * Created by hjcai on 2021/2/7. */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OnClick { //可以傳入多個引數 int[] value(); }
/**
* Created by hjcai on 2021/2/5.
* 註解宣告
*/
@Target(ElementType.FIELD)//該註解表示只能用於類的屬性
//其他常見屬性如 TYPE用在類上 CONSTRUCTOR用在建構函式上 METHOD用於方法
@Retention(RetentionPolicy.RUNTIME)
public//該註解表示在執行時生效
//其他常見屬性如 CLASS編譯時 RUNTIME執行時 SOURCE原始碼級別
@interface ViewById {
//value代表可以該註解可以新增一個引數
int value();
}
4.2 工具類編寫
/**
* Created by hjcai on 2021/2/5.
* <p>
* 主要是呼叫findViewById
*/
class ViewFinder {
private Activity mActivity;
public ViewFinder(Activity activity) {
this.mActivity = activity;
}
public View findViewById(int viewId) {
return mActivity.findViewById(viewId);
}
}
4.3 實際注入屬性與方法的類
/**
* Created by hjcai on 2021/2/5.
*/
public class ViewUtils {
//Activity繫結
public static void injectActivity(Activity activity) {
injectActivity(new ViewFinder(activity), activity);
}
private static void injectActivity(ViewFinder finder, Object object) {
injectActivityField(finder, object);
injectActivityEvent(finder, object);
}
//注入屬性
private static void injectActivityField(ViewFinder finder, Object object) {
//1 反射 獲取class裡面所有屬性
Class<?> clazz = object.getClass();
Field[] fields = clazz.getDeclaredFields();//獲取Activity的所有屬性包括私有和共有
for (Field field : fields) {
//2 遍歷fields 找到添加了註解ViewById的filed
ViewById viewById = field.getAnnotation(ViewById.class);
if (viewById != null) {
//3 獲取註解裡面的id值
int viewId = viewById.value();
View view = finder.findViewById(viewId);//相當於呼叫Activity.findViewById
if (view != null) {
field.setAccessible(true);
try {
//4 動態的注入找到的View
field.set(object, view);//利用反射 將object(activity)中的聲明瞭ViewById註解的地方 替換成剛剛find的view
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
private static void injectActivityEvent(ViewFinder finder, Object object) {
//1 反射 獲取class裡面所有屬性
Class<?> clazz = object.getClass();
Method[] methods = clazz.getDeclaredMethods();//獲取Activity的所有方法包括私有和共有
for (Method method : methods) {
//2 遍歷fields 找到添加了註解OnClick的method
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
//3 獲取註解裡面的id值
int[] viewIds = onClick.value();
for (int viewId : viewIds) {
View view = finder.findViewById(viewId);//相當於呼叫Activity.findViewById
if (view != null) {
// 4. 給每個view設定點選事件
view.setOnClickListener(new DeclaredOnClickListener(method, object));
}
}
}
}
}
private static class DeclaredOnClickListener implements View.OnClickListener {
private Object mObject;
private Method mMethod;//實際定義的方法 在這裡是 ActivityIocTest.onItemClick
public DeclaredOnClickListener(Method method, Object object) {
this.mObject = object;
this.mMethod = method;
}
@Override
public void onClick(View v) {
try {
// 所有方法都可以 包括私有共有
mMethod.setAccessible(true);
// 反射執行方法
// 當View被點選時執行
mMethod.invoke(mObject, v);//通過反射 呼叫mObject的聲明瞭onClick註解了的方法 引數為v
} catch (Exception e) {
e.printStackTrace();
try {
mMethod.invoke(mObject, null);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
}
小結
這裡的原理和xUtil3裡面的ioc註解原理一樣都是利用annotation+反射實現的