手寫IOC 註解框架,由於代碼中有詳細註釋,這裏就基本只上代碼。
1. 先寫一個setContentView()、findViewById()、onClick()事件註解:
1 //----------setContentView()註解---------- 2 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7 8 @Target(ElementType.TYPE) //將註解寫到類上面 9 @Retention(RetentionPolicy.RUNTIME) //程序運行時執行 10 public @interface ContentView { 11 int value() default -1; 12 } 13 14 //----------findViewById()註解---------- 15 16 import java.lang.annotation.ElementType; 17 import java.lang.annotation.Retention; 18 import java.lang.annotation.RetentionPolicy; 19 import java.lang.annotation.Target; 20 21 @Target(ElementType.FIELD) 22 @Retention(RetentionPolicy.RUNTIME) 23 public @interface ViewInject { 24 int value() default -1; 25 } 26 27 //----------onClick()事件註解---------- 28 29 import java.lang.annotation.ElementType; 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 import java.lang.annotation.Target; 33 34 /** 35 * 該註解在另一個註解上面使用 36 */ 37 @Target(ElementType.ANNOTATION_TYPE) 38 @Retention(RetentionPolicy.RUNTIME) 39 public @interface EventBase { 40 41 // 1.setOnClickListener 訂閱 42 String listenerSetter(); 43 44 // 2.new.View.OnClickListener() 事件 45 Class<?> listenerType(); 46 47 // 3.onClick(View v) 事件處理 48 String callbackMethod(); 49 } 50 51 import android.view.View; 52 53 import java.lang.annotation.ElementType; 54 import java.lang.annotation.Retention; 55 import java.lang.annotation.RetentionPolicy; 56 import java.lang.annotation.Target; 57 58 @Target(ElementType.METHOD) 59 @Retention(RetentionPolicy.RUNTIME) 60 @EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, callbackMethod = "onClick") 61 public @interface OnClick { 62 int[] value() default -1; 63 }
2.定義一個公共類 InjectUtils.java 這個類相當也是第三方,需要在BaseActivity中聲明。
import com.sprsoft.iocframe.annotation.ContentView; import com.sprsoft.iocframe.annotation.EventBase; import com.sprsoft.iocframe.annotation.ViewInject; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 這就是第三方 */ public class InjectUtils { public static void inject(Object context) { //注入的功能在這裏完成 injectLayout(context); injectView(context); injectClick(context); } /** * 佈局 * * @param context */ private static void injectLayout(Object context) { Class<?> clazz = context.getClass(); ContentView contentView = clazz.getAnnotation(ContentView.class); if (contentView != null) { int layoutID = contentView.value(); //反射執行setContentView(this); try { Method method = context.getClass().getMethod("setContentView", int.class); method.invoke(context, layoutID); } catch (Exception e) { e.printStackTrace(); } } } /** * 控件注入 * * @param context */ private static void injectView(Object context) { Class<?> aClass = context.getClass(); Field[] fields = aClass.getDeclaredFields(); for (Field field : fields) { ViewInject viewInject = field.getAnnotation(ViewInject.class); if (viewInject != null) { int valueId = viewInject.value(); try { Method method = aClass.getMethod("findViewById", int.class); View view = (View) method.invoke(context, valueId); field.setAccessible(true); field.set(context, view); } catch (Exception e) { e.printStackTrace(); } } } } /** * 事件 * 涉及事件三要素:事件源、事件、事件處理 * * @param context */ private static void injectClick(Object context) { Class<?> clazz = context.getClass(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { //這就寫死了 //OnClick annotation = method.getAnnotation(OnClick.class); Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { //得到OnClick上一級的EventBase Class<?> annotionClass = annotation.annotationType(); EventBase eventBase = annotionClass.getAnnotation(EventBase.class); //如果沒有EventBase則當前方法不是一個事件處理程序 if (eventBase == null) { continue; } //否則是事件處理回調的函數,是的開始得到相關的事件要素 //1.訂閱關係 String listenerSetter = eventBase.listenerSetter(); //2.事件 Class<?> listenerType = eventBase.listenerType(); //3.事件處理程序 String callbackMethod = eventBase.callbackMethod(); try { //反射得到ID 根據ID得到相應VIEW Method valueMethod = annotionClass.getDeclaredMethod("value"); int[] viewId = (int[]) valueMethod.invoke(annotation); //對每一個ID進行事件綁定 for (int id : viewId) { //找到ID對應的對象 Method findId = clazz.getMethod("findViewById", int.class); View view = (View) findId.invoke(context, id); if (view == null) { continue; } //得到ID對應的View以後,開始在View上執行監聽 //用代理執行View.OnClickListener()方法 //需要執行activity上的OnClick方法 ListenerInvocationHandler listenerHandler = new ListenerInvocationHandler(context, method); //去View.OnClickListener() == listenerType代理這個接口 Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, listenerHandler); //執行方法 Method onClickMethod = view.getClass().getMethod(listenerSetter, listenerType); onClickMethod.invoke(view, proxy); } } catch (Exception e) { e.printStackTrace(); } } } } }
3.去實現一個事件代理類(ListenerInvocationHandler)
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 事件代理類 * public void onClick(View v){} */ public class ListenerInvocationHandler implements InvocationHandler { private Object activity; private Method activityMethod; public ListenerInvocationHandler(Object activity, Method activityMethod) { this.activity = activity; this.activityMethod = activityMethod; } /** * 點擊按鈕後執行這個方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在這裏調用被註解的onClick方法 return activityMethod.invoke(activity, args); } }
4. BaseActivity.java中初始化
import android.content.Intent; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import com.sprsoft.iocframe.utils.InjectUtils; public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); InjectUtils.inject(this); } /** * 根據傳入的Class * * @param pClass */ public void openNewActivity(Class<?> pClass) { Intent _Intent = new Intent(); _Intent.setClass(this, pClass); startActivity(_Intent); } }
5.實現上面的以後下來就開始調用了。
import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.sprsoft.iocframe.annotation.ContentView; import com.sprsoft.iocframe.annotation.OnClick; import com.sprsoft.iocframe.annotation.OnLongClick; import com.sprsoft.iocframe.annotation.ViewInject; import com.sprsoft.practical.R; import com.sprsoft.practical.base.BaseActivity; /** * IOC 注入框架設計(註解) */ @ContentView(R.layout.activity_ioc) public class IOCActivity extends BaseActivity { @ViewInject(R.id.edt_old_password) EditText edtOldPassword; @ViewInject(R.id.edt_new_password) EditText edtNewPassword; @ViewInject(R.id.edt_reset_password) EditText edtResetPassword; @ViewInject(R.id.btn_password_save) Button btnPasswordSave; @ViewInject(R.id.btn_password_cancel) Button btnPasswordCancel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Toast.makeText(this, btnPasswordSave.getText().toString(), Toast.LENGTH_SHORT).show(); } @OnClick({R.id.btn_password_save, R.id.btn_password_cancel}) public void onClick(View view) { switch (view.getId()) { case R.id.btn_password_cancel: Toast.makeText(this, "取消成功", Toast.LENGTH_SHORT).show(); break; case R.id.btn_password_save: Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show(); break; } } @OnLongClick({R.id.btn_password_save, R.id.btn_password_cancel}) public boolean onLongClick(View view) { switch (view.getId()) { case R.id.btn_password_cancel: Toast.makeText(this, "長按取消成功", Toast.LENGTH_SHORT).show(); break; case R.id.btn_password_save: Toast.makeText(this, "長按保存成功", Toast.LENGTH_SHORT).show(); break; } return true; } }