本版本为1.0,支持较少,使用不够方便。相关封装逻辑结构已升级至2.0,详情可参见:更完善的安卓事件监听实现

  先简单扯两句这几天学习下来对java事件监听机制的一点感触。客观地讲,java的事件监听机制相比.net好原始,暂不说委托、lamda、泛型、反射等的繁琐,仅一个事件监听,就需要各种listener才能实现,比如安卓里到处都是view.setOnXXXXListener。被C#“语法糖”和宇宙第一IDE惯坏的我真心有点不习惯,于是就决定写个工具来封装这些烦人的listener。开始切入正题。

  1. 目标
  2. 成果
  3. 实现概况
  4. 具体实现及代码
  5. 总结

一、目标

  摆脱安卓里各种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以便使用。谨以此做一下记录和分享,如有问题,欢迎斧正,如果建议,欢迎反馈,此谢!

事件监听:诀别Android繁琐的事件注册机制——view.setOnXXXXListener的更多相关文章

  1. 高德地图marker事件监听-高德地图marker绑定事件就执行了[解决立即执行]

    官方的demo是这样的:地址:[http://lbs.amap.com/api/javascript-api/example/infowindow/add-infowindows-to-multipl ...

  2. Zookeeper Curator 事件监听 - 秒懂

    目录 写在前面 1.1. Curator 事件监听 1.1.1. Watcher 标准的事件处理器 1.1.2. NodeCache 节点缓存的监听 1.1.3. PathChildrenCache ...

  3. [JS]笔记12之事件机制--事件冒泡和捕获--事件监听--阻止事件传播

    -->事件冒泡和捕获-->事件监听-->阻止事件传播 一.事件冒泡和捕获 1.概念:当给子元素和父元素定义了相同的事件,比如都定义了onclick事件,点击子元素时,父元素的oncl ...

  4. 百度编辑器的内容改变事件监听bug

    先贴上我的初始化代码,可能是用法问题冤枉了百度编辑器,如果是我的用法有问题欢迎大侠们指正 <!DOCTYPE type> <html> <head> <met ...

  5. jQuery中的事件监听小记

    一,一个事件监听的简便写法 最近发现一个jQuery中事件监听的简洁写法,感觉方便好多.同时也深感自己基础薄弱,好多东西竟然都模棱两可.因此,记录的同时,也对jQuery事件监听做个小的总结 原文链接 ...

  6. Spring之事件监听(观察者模型)

    目录 Spring事件监听 一.事件监听案例 1.事件类 2.事件监听类 3.事件发布者 4.配置文件中注册 5.测试 二.Spring中事件监听分析 1. Spring中事件监听的结构 2. 核心角 ...

  7. 前端学习历程--js事件监听

    一.事件监听使用场景 1.事件触发多个方法的时候,后一个方法会把前一个方法覆盖掉. window.onload = function(){  var btn = document.getElement ...

  8. 背水一战 Windows 10 (66) - 控件(WebView): 监听和处理 WebView 的事件

    [源码下载] 背水一战 Windows 10 (66) - 控件(WebView): 监听和处理 WebView 的事件 作者:webabcd 介绍背水一战 Windows 10 之 控件(WebVi ...

  9. Spring Boot实践——事件监听

    借鉴:https://blog.csdn.net/Harry_ZH_Wang/article/details/79691994 https://blog.csdn.net/ignorewho/arti ...

随机推荐

  1. 解决iOS中 tabBarItem设置图片(image+title切图在一起)时造成的图片向上偏移

    解决iOS中 tabBarItem设置图片(image+title切图在一起)时造成的图片向上偏移 解决办法1:设置tabBarItem的imageInsets属性 代码示例: childContro ...

  2. Xcode7国际化(根据系统语言切换App显示的语言) - 元宵节快乐!

    老规矩, 上gif 下面是配置的大概流程: 这个是要显示中文的.strings文件的内容和格式 这个是要显示英文的.strings文件的内容和格式 下面是应用名部分: 然后下面是代码部分: impor ...

  3. 从零开始制作Minecraft启动器(C++开源)

    从零开始制作Minecraft启动器(C++开源) 新手飙车了~~~,MC启动器源码大放送,随心所欲打造自己的专属MC启动器,这不是易语言,是C++...分析原理,关键源码都有详细的注释,代码编好就打 ...

  4. asp.net跨域上传文件

    前端: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" con ...

  5. C语言初学 if-else语句判别在ASCII值中小于32的可控制符的类型

    #include<stdio.h> main() { char c; printf("输入一个符号\n"); c=getchar(); if(c<32) prin ...

  6. Swift类和结构体定义-备

    Swift中的类和结构体定义的语法是非常相似的.类使用class关键词定义类,使用struct关键词定义结构体,它们的语法格式如下: class 类名 { 定义类的成员 } struct 结构体名 { ...

  7. maven配置文件解析

    maven2配置文件主要分为settings.xml与pom.xml两种,下面将逐一介绍. ===================================settings.xml======= ...

  8. 了解 Windows Azure 存储的可伸缩性、可用性、持久性和计费

    借助 Windows Azure存储,应用程序开发者及其应用程序和用户可以在云中使用可用性更高.持久性更长.可伸缩性更强的海量存储.开发者可以构建能随时随地高效访问数据的服务,在所需的时间段内存储任意 ...

  9. C(m,n)%P

    program1 n!%P(P为质数) 我们发现n! mod P的计算过程是以P为周期的的,举例如下: n = 10, P = 3 n! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 ...

  10. (3)tomcat源代码分析环境的搭建

    他山之石,可以攻玉. 要想了解tomcat,咱必须先搭建tomcat的环境,下载tomcat的源码,学习其架构. 1.首先是SVM  Import 2.创建新的资源库位置:http://svn.apa ...