事件监听:诀别Android繁琐的事件注册机制——view.setOnXXXXListener
本版本为1.0,支持较少,使用不够方便。相关封装逻辑结构已升级至2.0,详情可参见:更完善的安卓事件监听实现
先简单扯两句这几天学习下来对java事件监听机制的一点感触。客观地讲,java的事件监听机制相比.net好原始,暂不说委托、lamda、泛型、反射等的繁琐,仅一个事件监听,就需要各种listener才能实现,比如安卓里到处都是view.setOnXXXXListener。被C#“语法糖”和宇宙第一IDE惯坏的我真心有点不习惯,于是就决定写个工具来封装这些烦人的listener。开始切入正题。
摆脱安卓里各种listener的繁琐,像写一般的方法似的写各种事件。
只要写一个类(这里以MainActivityEvent命名的类为例)继承EventManager,然后在对应的MainActivity里的onCreate方法里初始化这个类(new MainActivityEvent(this))即可完成注册。剩下的就只需要在MainActivityEvent类里写对应的事件响应逻辑就可以了。
3.1 MainActivity里注册。
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 }
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; }
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); } }
源码下载:code.rar
为了减少重复实现listener接口的繁琐,自己写了点api以便使用。谨以此做一下记录和分享,如有问题,欢迎斧正,如果建议,欢迎反馈,此谢!