码迷,mamicode.com
首页 > 移动开发 > 详细

事件监听:诀别Android繁琐的事件注册机制——view.setOnXXXXListener 滚犊子

时间:2015-12-24 22:10:22      阅读:1282      评论:0      收藏:0      [点我收藏+]

标签:

  好久没写过随笔了......windows phone生态没起来,属于.net阵营的我最近工作不是太忙,闲暇之余就心血来潮开始研究安卓。先简单扯两句这几天学习下来对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
}
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 滚犊子

标签:

原文地址:http://www.cnblogs.com/yuanlb/p/5073378.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!