Android注解框架IOC(设置事件)

前言:如下是通过对注解框架的阅读和理解,自个整理的一些笔记,想学习注解原理的朋友可以参考下。
建议:如果使用AS开发,需要用到注解,建议安装Android ButterKnife Zelezny插件,可以提高开发效率。(注意:AS默认有butterknife的jar包,使用该插件时需要先导入butterknife包才能使用,就像导入jar一样的操作)

总结:

  1. 这里注解框架和xUtils注解框架同理,我们可以自己封装起来,方便使用
  2. 注解事件是不需要控件对象的,其实在注解事件里面已经有获取控件对象的操作,并通过代理对象给控件对象设置了事件监听
  3. 注解事件还是需要布局视图的,不然没地方触发事件
  4. 下面代码其实是完整的框架代码,后面把控件的注解的注释去掉即可,这里是为了方便说明注解事件

getDeclaredMethod()和getMethod()的区别:
getDeclaredMethod*()获取的是类自身声明的所有方法,包含public、protected和private方法。
getMethod*()获取的是类的所有公有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。
注意:getDeclaredMethod方法对自身的类有完整的访问权限,但针对当前类;getMethod方法只对public的方法有访问权限,由于是公有,那么对自身的,基类继承的,接口实现的方法都有访问权限

主Activity中(注意:即使处理的是控件的注解事件,也要加入布局,可以直接加入或者注解加入)

@ContentView(value = R.layout.activity_main)
public class MainActivity extends Activity {
//	@ViewInject(R.id.id_btn)
//	private Button mBtn1;
//	@ViewInject(R.id.id_btn02)
//	private Button mBtn2;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		ViewInjectUtils.inject(this);
	}
	@OnClick({ R.id.id_btn, R.id.id_btn02 })
	public void clickBtnInvoked(View view) {
		switch (view.getId()) {
		case R.id.id_btn:
			Toast.makeText(this, "Inject Btn01 !", Toast.LENGTH_SHORT).show();
			break;
		case R.id.id_btn02:
			Toast.makeText(this, "Inject Btn02 !", Toast.LENGTH_SHORT).show();
			break;
		}
	}
	
}

主界面的注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
	int value();//@ContentView(value = R.layout.activity_main)
}

onClick的注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick")
public @interface OnClick {
	int[] value();//{ R.id.id_btn, R.id.id_btn02 } 注入到这个int数组中
}

onClick注解上的@EventBase注解:

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
	Class<?> listenerType();//监听类型,泛型---》View.OnClickListener.class
	String listenerSetter();//设置监听的方法---》setOnClickListener
	String methodName();//监听回调方法名---》onClick
}

注解主要的处理类:

/**
 * 主要处理类
 * @author huangweicai
 *
 */
public class ViewInjectUtils {
	private static final String METHOD_SET_CONTENTVIEW = "setContentView";
	private static final String METHOD_FIND_VIEW_BY_ID = "findViewById";
	public static void inject(Activity activity) {
		injectContentView(activity);//设置主界面视图
//		injectViews(activity);//给控件注入资源对象(这里不需要)
		injectEvents(activity);//事件处理
	}
	/**
	 * 注入所有的事件
	 * @param activity
	 */
	private static void injectEvents(Activity activity) {
		//继承Activity的子类的反射
		Class<? extends Activity> clazz = activity.getClass();
		Method[] methods = clazz.getMethods();//获取所有方法,用方法数组接收
		// 遍历方法数组,得到每一个方法的注解
		for (Method method : methods) {
			//也用注解数组接收,因为一个方法上可能不止一个注解
			Annotation[] annotations = method.getAnnotations();
			// 取得注解数组中的每一个注解
			for (Annotation annotation : annotations) {
				//得到注解的注解类型对象,该对象可以获取注解上的注解
				Class<? extends Annotation> annotationType = annotation.annotationType();
				// 拿到注解上的注解
				EventBase eventBaseAnnotation = annotationType.getAnnotation(EventBase.class);
				// eventBaseAnnotation存在
				if (eventBaseAnnotation != null) {
					//注意:注解对象是可以直接点出来其中的成员对象的(默认修饰符:protect)
					//setOnClickListener
					String listenerSetter = eventBaseAnnotation.listenerSetter();
					//View.OnClickListener.class
					Class<?> listenerType = eventBaseAnnotation.listenerType();
					//onClick
					String methodName = eventBaseAnnotation.methodName();
					try {
						//得到MainActivity中注解值为value的方法
						Method aMethod = annotationType.getDeclaredMethod("value");//当前类所有方法(包括公有、私有)
						//执行该方法并传入注解对象,返回注入值的数组,{ R.id.id_btn, R.id.id_btn02 }
						int[] viewIds = (int[]) aMethod.invoke(annotation, null);
						// 通过InvocationHandler设置代理
						DynamicHandler handler = new DynamicHandler(activity);
						// 往haspmap集合中,添加需要拦截的事件方法,通过“循环+判断”得到的事件方法名来指定:methodName
						handler.addMethod(methodName, method);
						
						// 创建listenertype的代理对象-----
						Object listener = Proxy.newProxyInstance(
								listenerType.getClassLoader(),//View.OnClickListener.class对象获取类加载器
								new Class<?>[] { listenerType },
								handler
								);
						
						// 遍历所有的View,设置事件
						for (int viewId : viewIds) {
							//得到资源对象
							View view = activity.findViewById(viewId);//传入资源id
							//这步就是:setOnClickListener(new View.OnClickListener.class)
							Method setEventListenerMethod = view.getClass()
									.getMethod(listenerSetter, listenerType);
							//这步就是:view通过代理对象来执行设置监听
							setEventListenerMethod.invoke(view, listener);//listener 代理对象传进来
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	/**
	 * 注入所有的控件
	 * @param activity
	 */
	private static void injectViews(Activity activity) {
		Class<? extends Activity> clazz = activity.getClass();
		Field[] fields = clazz.getFields();
		// 遍历所有成员变量
		for (Field field : fields) {
			ViewInject viewInjectAnnotation = field
					.getAnnotation(ViewInject.class);
			if (viewInjectAnnotation != null) {
				int viewId = viewInjectAnnotation.value();
				if (viewId != -1) {
					// 初始化View
					try {
						Method method = clazz.getMethod(METHOD_FIND_VIEW_BY_ID,
								Integer.class);
						Object resView = method.invoke(clazz, viewId);
						field.setAccessible(true);
						field.set(clazz, resView);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	/**
	 * 注入主布局文件
	 * @param activity
	 */
	private static void injectContentView(Activity activity) {
		Class<? extends Activity> clazz = activity.getClass();
		// 查询类上是否存在ContentView注解
		ContentView contentView = clazz.getAnnotation(ContentView.class);
		if (contentView != null)// 存在
		{
			int contentViewLayoutId = contentView.value();
			try {
				Method method = clazz.getMethod(METHOD_SET_CONTENTVIEW,
						int.class);
				method.setAccessible(true);
				method.invoke(activity, contentViewLayoutId);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

拦截代理类:

/**
 * 代理拦截类
 * @author huangweicai
 *
 */
public class DynamicHandler implements InvocationHandler {
	private WeakReference<Object> handlerRef;//弱引用
	private final HashMap<String, Method> methodMap = new HashMap<String, Method>(1);
	
	/**
	 * 构造函数
	 * @param handler
	 */
	public DynamicHandler(Object handler) {
		this.handlerRef = new WeakReference<Object>(handler);
	}
	
	/**
	 * 添加拦截的方法到对应列表
	 * @param handler
	 */
	public void addMethod(String name, Method method) {
		methodMap.put(name, method);
	}
	
	public Object getHandler() {
		return handlerRef.get();
	}
	
	public void setHandler(Object handler) {
		this.handlerRef = new WeakReference<Object>(handler);
	}
	
	//执行拦截回调
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object handler = handlerRef.get();//拦截到的onClick方法
		if (handler != null) {
			String methodName = method.getName();//得到方法名,即onClick
			method = methodMap.get(methodName);//得到该方法名的方法对象
			if (method != null) {
				return method.invoke(handler, args);
			}
		}
		return null;
	}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值