zoukankan      html  css  js  c++  java
  • 事件监听:诀别Android繁琐的事件注册机制——view.setOnXXXXListener

      本版本为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
    }
    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以便使用。谨以此做一下记录和分享,如有问题,欢迎斧正,如果建议,欢迎反馈,此谢!

  • 相关阅读:
    Leetcode-921 Minimum Add to Make Parentheses Valid(使括号有效的最少添加)
    Leetcode-922 Sort Array By Parity II(按奇偶排序数组 II)
    Leetcode-827 Making A Large Island(最大人工岛)
    Leetcode-766 Toeplitz Matrix(托普利茨矩阵)
    Leetcode-919 Complete Binary Tree Inserter(完全二叉树插入器)
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    LeetCode
  • 原文地址:https://www.cnblogs.com/yuanlb/p/5073378.html
Copyright © 2011-2022 走看看