好久没写过随笔了......windows phone生态没起来,属于.net阵营的我最近工作不是太忙,闲暇之余就心血来潮开始研究安卓。先简单扯两句这几天学习下来对java事件监听机制的一点感触。客观地讲,java的事件监听机制相比.net好原始,暂不说委托、lamda、泛型等的繁琐,仅一个事件监听,就需要各种listener才能实现,比如安卓里到处都是view.setOnXXXXListener。被C#“语法糖”和宇宙第一IDE惯坏的我真心有点不习惯,于是就决定写个工具来封装这些烦人的listener。开始切入正题。
- 目标
- 成果
- 实现概况
- 具体实现及代码
- 总结
一、目标
摆脱安卓里各种listener的繁琐,像写一般的方法似的写各种事件。
二、成果
只要写一个类(这里以MainActivityEvent命名的类为例)继承EventManager,然后在对应的MainActivity里的onCreate方法里初始化这个类(new MainActivityEvent(this))即可完成注册。剩下的就只需要在MainActivityEvent类里写对应的事件响应逻辑就可以了。
三、实现概况
3.1 MainActivity里注册。
images/loading.gif' data-original="http://images2015.cnblogs.com/blog/446443/201512/446443-20151224204130546-315366757.png" />
3.2 MainActivityEvent的实现。
3.3 封装的相关类型。
四、具体实现及代码
4.1 EventType.java
package com.example.personal.events;/** * Event type. */public enum EventType { /** * signature: (View v, int keyCode, KeyEvent event) * return: boolean. */ OnKey, /** * signature: (View v, MotionEvent event) * return: boolean. */ OnTouch, /** * signature: (View v, MotionEvent event) * return: boolean. */ OnHover, /** * signature: (View v, MotionEvent event) * return: boolean. */ OnGenericMotion, /** * signature: (View v) * return: boolean. */ OnLongClick, /** * signature: (View v, DragEvent event) * return: boolean. */ OnDrag, /** * signature: (View v, boolean hasFocus) * return: void. */ OnFocusChange, /** * signature: (View v) * return: void. */ OnClick, /** * signature: (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) * return: void. */ OnCreateContextMenu, //TODO: Not supported for api version issues or other special reasons.// /**// * signature: (View v)// */// OnViewAttachedToWindow,// /**// * signature: (View v)// */// OnViewDetachedFromWindow,// /**// * signature: (View v)// */// OnContextClick,// /**// * signature: (int visibility)// */// OnSystemUiVisibilityChange}
View Code
4.2 EventAnnotation.java
package com.example.personal.events;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Documented@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface EventAnnotation { /** * View id. * * @return the view id that the event binds to. */ int value(); /** * Event type. If not specified, onClick will be set by default. * * @return the event type of the method binds to. */ EventType eventType() default EventType.OnClick;}
View Code
4.3 EventManager.java
package com.example.personal.events;import android.app.Activity;import android.view.ContextMenu;import android.view.DragEvent;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.View;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;/** * A abstract class to encapsulate most of the view event listeners. When bind an event to a view, in derived class, * you just need to declare a method that same with the event's signature, and annotate with {@link EventAnnotation}. * Note: the return value match is not required but recommended. For method that requires return boolean type, * {@code true} will be returned by default if the method return type is not. */public abstract class EventManager implements View.OnKeyListener, View.OnTouchListener, View.OnHoverListener, View.OnGenericMotionListener, View.OnLongClickListener, View.OnDragListener, View.OnFocusChangeListener, View.OnClickListener, View.OnCreateContextMenuListener { private final Map<Integer, Map<EventType, Method>> eventMap; protected final Activity activity; protected EventManager(Activity activity) { this.activity = activity; eventMap = new HashMap<>(); registerEvents(); } private void setListener(View view, EventType eventType) { switch (eventType) { case OnKey: view.setOnKeyListener(this); break; case OnTouch: view.setOnTouchListener(this); break; case OnHover: view.setOnHoverListener(this); break; case OnGenericMotion: view.setOnGenericMotionListener(this); break; case OnLongClick: view.setOnLongClickListener(this); break; case OnDrag: view.setOnDragListener(this); break; case OnFocusChange: view.setOnFocusChangeListener(this); break; case OnClick: view.setOnClickListener(this); break; case OnCreateContextMenu: view.setOnCreateContextMenuListener(this); break; } } private void registerEvents() { for (Method method : this.getClass().getDeclaredMethods()) { if (method.isAnnotationPresent(EventAnnotation.class)) { EventAnnotation annotation = method.getAnnotation(EventAnnotation.class); int viewId = annotation.value(); View view = activity.findViewById(viewId); if (view != null) { method.setAccessible(true); EventType eventType = annotation.eventType(); setListener(view, eventType); Map<EventType, Method> actionMap; if (eventMap.containsKey(viewId)) { actionMap = eventMap.get(viewId); actionMap.put(eventType, method); } else { actionMap = new HashMap<>(); actionMap.put(eventType, method); eventMap.put(viewId, actionMap); } } } } } private Object invokeAction(EventType eventType, Object... args) { View view = null; for (Object obj : args) { if (obj instanceof View) { view = (View) obj; break; } } if (view == null) { return null; } Method action = eventMap.get(view.getId()).get(eventType); Object result = null; if (action != null) { try { result = action.invoke(this, args); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } return result; } private boolean invokeActionReturnStatus(EventType eventType, Object... args) { Object result = invokeAction(eventType, args); return result instanceof Boolean ? true : (boolean) result; } @Override public void onClick(View v) { invokeAction(EventType.OnClick, v); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { invokeAction(EventType.OnCreateContextMenu, menu, v, menuInfo); } @Override public boolean onDrag(View v, DragEvent event) { return invokeActionReturnStatus(EventType.OnDrag, v, event); } @Override public void onFocusChange(View v, boolean hasFocus) { invokeAction(EventType.OnFocusChange, v, hasFocus); } @Override public boolean onGenericMotion(View v, MotionEvent event) { return invokeActionReturnStatus(EventType.OnGenericMotion, v, event); } @Override public boolean onHover(View v, MotionEvent event) { return invokeActionReturnStatus(EventType.OnHover, v, event); } @Override public boolean onKey(View v, int keyCode, KeyEvent event) { return invokeActionReturnStatus(EventType.OnKey, v, keyCode, event); } @Override public boolean onLongClick(View v) { return invokeActionReturnStatus(EventType.OnLongClick, v); } @Override public boolean onTouch(View v, MotionEvent event) { return invokeActionReturnStatus(EventType.OnTouch, v, event); }}
View Code
五、总结
源码下载:code.rar
为了减少重复实现listener接口的繁琐,自己写了点api以便使用。通过封装,比较深刻的理解了java事件监听的机制,为安卓的入门打开了一个缝隙。有空打算再对SQLiteOpenHelper和SQLiteDatabase封装一个类似TableManager和Constraint之类的东西,来方便与Sqlite进行交互。并打算对Adapter封装一个ModelAdapter之类的东西来实现list item的缓存(不得不吐槽一下,adapter似乎在数据绑定的机制上做的不太好)。谨以此做一下记录和分享,如有问题,欢迎斧正,如果建议,欢迎反馈,此谢!
原标题:事件监听:诀别Android繁琐的事件注册机制——view.setOnXXXXListener 滚犊子
关键词:Android