精华内容
下载资源
问答
  • Android按键事件传递机制

    千次阅读 2014-06-10 15:23:34
    Android按键事件处理主要在View和Activity两个级别。和Touch事件相比,简单很多。 按键事件的处理机制如下: 1、默认情况下,如果没有View获得焦点,事件将传递给Activity处理。 2、如果View获得焦点,事件首先...

    Android按键事件处理主要在View和Activity两个级别。和Touch事件相比,简单很多。
    按键事件的处理机制如下:
    1、默认情况下,如果没有View获得焦点,事件将传递给Activity处理。
    2、如果View获得焦点,事件首先传递到View的回调方法中。view回调方法返回false,事件继续传递到Activity处理。反之,事件不会继续传递。
    Android按键事件顺序
    PS:使用View.SetFocusable(true)设置可以获得焦点。
    需要注意的是:
    1、按键的onKeyDown和onKeyUp是相互独立的,不会相互影响。
    2、无论是View还是Activity中,建议重写事件回调方法时,只对处理过的按键返回true,没有处理的事件应该调用其父类方法。

    否则,其他未处理事件不会被传递到合适的目标组件中,例如:Back按键失效问题。

    转载地址:http://orgcent.com/android-key-event-mechanism/

    展开全文
  • android 按键事件上报机制

    千次阅读 2019-01-13 08:28:56
    Dispatcher分发的事件最后会通过jni上报到InputManagerService,然后通过接口最后传递给PhoneWindow,这里再根据不同的按键事件类型来做不同的处理。 1.1 输入事件处理准备工作 当系统开机之后...

    1. 按键上报和分发机制

    按键处理设计的整体思路是驱动层会有一个消息队列来存放事件,会有一个Reader来不停的读取事件,一个Dispatcher来分发消息队列中的事件。Dispatcher分发的事件最后会通过jni上报到InputManagerService,然后通过接口最后传递给PhoneWindow,这里再根据不同的按键事件类型来做不同的处理。

    1.1 输入事件处理准备工作

    当系统开机之后SystemServer会启动InputManagerService,在SystemServer.java中的startOtherServices()方法中启动:

        Slog.i(TAG, "Input Manager");
        inputManager = new InputManagerService(context);
    
        Slog.i(TAG, "Window Manager");
        wm = WindowManagerService.main(context, inputManager,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                !mFirstBoot, mOnlyCore);
        ServiceManager.addService(Context.WINDOW_SERVICE, wm);
        ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
    
        mActivityManagerService.setWindowManager(wm);
    
        inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
        inputManager.start();
    复制代码

    看一下InputManagerService的构造方法:

        public InputManagerService(Context context) {
            this.mContext = context;
            this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
    
            mUseDevInputEventForAudioJack =
                    context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
            Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                    + mUseDevInputEventForAudioJack);
            mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    
            LocalServices.addService(InputManagerInternal.class, new LocalService());
        }
    复制代码

    主要是通过JNI的方式调用nativeInit方法传入一个消息队列作为参数,nativeInit对应是com_android_server_input_InputManagerService.cpp中的nativeInit。

        static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
                jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
            sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
            if (messageQueue == NULL) {
                jniThrowRuntimeException(env, "MessageQueue is not initialized.");
                return 0;
            }
        
            NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
                    messageQueue->getLooper());
            im->incStrong(0);
            return reinterpret_cast<jlong>(im);
        }
    复制代码

    这里又new了一个NativeInputManager的对象,构造的参数主要还是这个消息队列的Looper。继续看一下NativeInputManager的构造函数,还是在com_android_server_input_InputManagerService.cpp中。

        NativeInputManager::NativeInputManager(jobject contextObj,
                jobject serviceObj, const sp<Looper>& looper) :
                mLooper(looper), mInteractive(true) {
            JNIEnv* env = jniEnv();
        
            mContextObj = env->NewGlobalRef(contextObj);
            mServiceObj = env->NewGlobalRef(serviceObj);
        
            {
                AutoMutex _l(mLock);
                mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
                mLocked.pointerSpeed = 0;
                mLocked.pointerGesturesEnabled = true;
                mLocked.showTouches = false;
            }
            mInteractive = true;
        
            sp<EventHub> eventHub = new EventHub();
            mInputManager = new InputManager(eventHub, this, this);
        }
    复制代码

    这里主要就是new出了InputManager的实例,最后再看一下构造函数和initialize方法,代码位于frameworks/native/services/inputflinger/InputManager.cpp中。

        InputManager::InputManager(
                const sp<EventHubInterface>& eventHub,
                const sp<InputReaderPolicyInterface>& readerPolicy,
                const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
            mDispatcher = new InputDispatcher(dispatcherPolicy);
            mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
            initialize();
        }
        
        InputManager::InputManager(
                const sp<InputReaderInterface>& reader,
                const sp<InputDispatcherInterface>& dispatcher) :
                mReader(reader),
                mDispatcher(dispatcher) {
            initialize();
        }
        
        InputManager::~InputManager() {
            stop();
        }
        
        void InputManager::initialize() {
            mReaderThread = new InputReaderThread(mReader);
            mDispatcherThread = new InputDispatcherThread(mDispatcher);
        }
    复制代码

    在initialize中初始化了mReader和mDispatcher,以及两个相关的Thread。至此所需的关键对象都已经创建准备好,但是线程并未run起来。

    1.2 启动事件读取和分发线程

    在SystemServer中启动InputManagerService之后,调用了start方法:

        inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
        inputManager.start();
    复制代码

    在InputManagerService中的start方法中又调用了nativeStart方法,这个方法也是本地方法,具体实现在com_android_server_input_InputManagerService.cpp中。

    static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
        NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    
        status_t result = im->getInputManager()->start();
        if (result) {
            jniThrowRuntimeException(env, "Input manager could not be started.");
        }
    }
    复制代码

    接下来又调用了InputManager.cpp的start方法:

        status_t InputManager::start() {
            status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
            if (result) {
                ALOGE("Could not start InputDispatcher thread due to error %d.", result);
                return result;
            }
        
            result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
            if (result) {
                ALOGE("Could not start InputReader thread due to error %d.", result);
        
                mDispatcherThread->requestExit();
                return result;
            }
        
            return OK;
        }
    复制代码

    在这里将DispatcherThread和ReaderThread运行起来。

    一张图总结下:

    1.3 事件获取和分发

    首先由InputReaderThread等待按键消息到来,该thread在threadLoop中无尽的调用InputReader的loopOnce方法,代码位于frameworks/native/services/inputflinger/InputReader.cpp:

    bool InputReaderThread::threadLoop() {
        mReader->loopOnce();
        return true;
    }
    复制代码

    在loopOnce方法中会通过EventHub来获取事件放入buffer中:

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    复制代码

    frameworks/native/services/inputflinger/EventHub.cpp中的getEvents方法一部分:

    size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
        ALOG_ASSERT(bufferSize >= 1);
    
        AutoMutex _l(mLock);
    
        struct input_event readBuffer[bufferSize];
    
        RawEvent* event = buffer;
    复制代码

    InputReader从设备文件(adb shell getevent就是读的这个文件)中读取的是RawEvent,在交给InputDispatcher进行分发之前,它需要先把RawEvent进行转化分类,拆分成KeyEvent、MotionEvent、TrackEvent各种类型等。如需了解拆分过程可以参照文章 Android输入事件流程中的EventHub分析及源码演示 来深入理解EventHub.cpp中的拆分过程。

    在getEvents方法的最后来将队列中事件刷给监听器,监听器实际上就是InputDispatcher事件分发器。

     // Flush queued events out to the listener. This must happen outside of the lock because the listener could potentially call back into the InputReader's methods, such as getScanCodeState, or become blocked on another thread similarly waiting to acquire the InputReader lock thereby resulting in a deadlock. This situation is actually quite plausible because the listener is actually the input dispatcher, which calls into the window manager, which occasionally calls into the input reader.     
     mQueuedListener->flush();
    复制代码

    然后会调用到frameworks/native/services/inputflinger/InputDispatcher.cpp的notifyKey方法。

    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->action,
            flags, keyCode, args->scanCode, metaState, 0,
            args->downTime, args->eventTime);
    
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    复制代码

    在notifyKey方法中通过InputDispatcherPolicyInterface接口来调用到NativeInputManager的interceptKeyBeforeQueueing方法通知是否需要在入队前对事件进行处理,然后接着调用InputDispatcher的enqueueInboundEventLocked方法将事件放入到队尾中。

        KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);
    
        needWake = enqueueInboundEventLocked(newEntry);
    复制代码
        bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
            bool needWake = mInboundQueue.isEmpty();
            mInboundQueue.enqueueAtTail(entry);
            traceInboundQueueLengthLocked();
        
            switch (entry->type) {
    复制代码

    以上流程是InputReader获取到设备事件通知分发器并存放到事件队列中。

    下面将介绍InputDispatcher如何从事件队列中读取事件并分发出去。首先在InputDispatcherThread的threadLoop中无尽的调用dispatchOnce方法,该方法两个功能:1、调用dispatchOnceInnerLocked分发事件;2、调用runCommandsLockedInterruptible来处理CommandQueue中的命令,出队并处理,直到队列为空。

    下面具体介绍事件的分发,如果当前没有挂起的命令即CommandQueue为空,则调用dispatchOnceInnerLocked方法来分发事件,这里也是android系统出现事件响应ANR的地方,在之前挂起的事件命令过多时就会导致新的事件无法分发导致ANR,在dispatchOnceInnerLocked中成功分发后会去调用resetANRTimeoutsLocked()来重置ANR的时间。

    bool InputDispatcherThread::threadLoop() {
        mDispatcher->dispatchOnce();
        return true;
    }
    
    void InputDispatcher::dispatchOnce() {
        nsecs_t nextWakeupTime = LONG_LONG_MAX;
        { // acquire lock             AutoMutex _l(mLock);
            mDispatcherIsAliveCondition.broadcast();
    
            // Run a dispatch loop if there are no pending commands.             // The dispatch loop might enqueue commands to run afterwards.             if (!haveCommandsLocked()) {
                dispatchOnceInnerLocked(&nextWakeupTime);
            }
    
            // Run all pending commands if there are any.             // If any commands were run then force the next poll to wake up immediately.             if (runCommandsLockedInterruptible()) {
                nextWakeupTime = LONG_LONG_MIN;
            }
        } // release lock     
        // Wait for callback or timeout or wake. (make sure we round up, not down)         nsecs_t currentTime = now();
        int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
        mLooper->pollOnce(timeoutMillis);
    }
    复制代码

    在dispatchOnceInnerLocked中会处理多种类型的事件,这里关注按键类型的(其他如触摸,设备重置等事件流程稍有区别)。如果Event类型为KEY,最后调用dispatchKeyLocked,再经过doInterceptKeyBeforeDispatchingLockedInterruptible这个方法之后就调用到了InputDispatcherPolicyInterface接口的interceptKeyBeforeDispatching方法。到这里就又很熟悉了,由NativeInputManager实现这个接口,然后又通过jni的方式调用到PhoneWindowManager的interceptKeyBeforeDispatching将按键事件分发传递给了java层。

    case EventEntry::TYPE_KEY: {// 如果事件为按键类型         KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        if (isAppSwitchDue) {
            if (isAppSwitchKeyEventLocked(typedEntry)) {
                resetPendingAppSwitchLocked(true);
                isAppSwitchDue = false;
            } else if (dropReason == DROP_REASON_NOT_DROPPED) {
                dropReason = DROP_REASON_APP_SWITCH;
            }
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }
    复制代码

    事件的处理包括了两条主线:

    • InputReader从EventHub中获取到按键事件,并通知InputDispatcher;InputDispatcher接到通知后调用interceptKeyBeforeQueueing方法进行相关的操作,并把按键事件加入到队列中,等待后面处理;
    • InputDispatcher从消息队列中获取按键消息,调用interceptKeyBeforeDispatching方法判断是否对此消息进行拦截。

    小结

    这一部分介绍了事件上报和处理机制,首先是准备工作,SystemServer启动InputManagerService,然后依次创建了NativeInputManager、InputManager、InputReader、InputDispatcher这几个关键的对象以及InputReaderThread和InputDispatcherThread这两个关键线程。然后让这个两个thread启动起来,在InputReaderThread无限循环运行时,通过InputReader从EventHub中不断读取events然后通知InputDispatcher将事件入队。而InputDispatcherThread则通过InputDispatcher不停的将队列中的事件分发出去,这就是整个input系统的基本机制。

    展开全文
  • Android 按键事件处理详解

    千次阅读 2013-11-06 16:22:40
    原文地址:Android按键事件处理详解作者:Apple 公司最近做一个按键触感就对这块研究了一番,以下是个人心得。 1.开始肯定先说的是驱动这块,硬件是软件服务的,在Android这块C和java交互,有两种方式:  1.1...
    原文地址:Android按键事件处理详解作者:Apple

    公司最近做一个按键触感就对这块研究了一番,以下是个人心得。

    1.开始肯定先说的是驱动这块,硬件是软件服务的,在Android这块C和java交互,有两种方式:

      1.1:驱动--JNI--服务-事件分发-上层应用处理。

      1.2:上层直接调用通过lib库的方式实现,中间使用回调机制,这种方式在Camera中有,下次再详解。

       先来看一下驱动按键映射部分的详解如下:

       映射实际是由KeyLayoutMap::map完成的,KeyLayoutMap类里读取配置文件qwerty.kl,由配置 文件 qwerty.kl决定键值的映射关系。你可以通过修改./development/emulator/keymaps/qwerty.kl来改变键值的映射关系。
      在frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文件中,向JAVA提供了函数android_server_KeyInputQueue_readEvent,用于读取输入设备事件。

    static jboolean  android_server_KeyInputQueue_readEvent(JNIEnv* env,jobject clazz,                                             jobject event)   {       gLock.lock();       sp hub = gHub;       if(hub == NULL) {          hub = new EventHub;          gHub = hub;       }       gLock.unlock();         int32_t deviceId;       int32_t type;       int32_t scancode, keycode;      uint32_t flags;       int32_t value;       nsecs_t when;       boolres = hub->getEvent(&deviceId,&type, &scancode,&keycode,              &flags, &value,&when);        env->SetIntField(event, gInputOffsets.mDeviceId,(jint)deviceId);       env->SetIntField(event, gInputOffsets.mType,(jint)type);       env->SetIntField(event, gInputOffsets.mScancode,(jint)scancode);       env->SetIntField(event, gInputOffsets.mKeycode,(jint)keycode);       env->SetIntField(event, gInputOffsets.mFlags,(jint)flags);       env->SetIntField(event, gInputOffsets.mValue,value);       env->SetLongField(event, gInputOffsets.mWhen,                          (jlong)(nanoseconds_to_milliseconds(when)));        returnres;   }  

     2.下面要讲重头代码WindowManagerService.java(frameworks\base\services\java\com\android\server),这个服务有承上启下的作用,读取用户输入的信息,是通过创建一个InputDeviceReader线程,KeyQ构建时,会启动一个线程去读取用户消息,具体代码在KeyInputQueue.mThread,在构造函数中,mThread会start,接下来,接研究一下mThread.run:
       //用户输入事件消息读取线程
        ThreadmThread = new Thread("InputDeviceReader") {
           public void run() {
               RawInputEvent ev = new RawInputEvent();
               while (true) {//开始消息读取循环
                   try {
                       InputDevice di;
                       //本地方法实现,读取用户输入事件
                       readEvent(ev);
                       //根据ev事件进行相关处理
                       ...
                       synchronized (mFirst) {//mFirst是keyQ队列头指针
                       ...
                       addLocked(di, curTimeNano,ev.flags,RawInputEvent.CLASS_TOUCHSCREEN, me);
                       ...
                       }
                   }
           }
          }

      3.在读取完用户输入的信息,在WindowManagerService中就要分发消息,具体实现是交给了InputDispatcherThread这个线程处理,下来详解InputDispatcherThread处理流程:

      -->WindowManagerService.main
            --mInputThread = newInputDispatcherThread();//创建一个消息分发线程,读取并处理mQueue中消息

        InputDispatcherThread.run
    -->windowManagerService.process{               
               ...
               while (true){               
                   // 从mQueue(KeyQ)获取一个用户输入事件,正上调用我上面提到的getEvent方法,若队列为空,线程阻塞挂起
                   QueuedEvent ev = mQueue.getEvent(
                       (int)((!configChanged && curTime< nextKeyTime)
                               ? (nextKeyTime-curTime) : 0));
                   ...
                   try {
                       if (ev != null) {
                           ...
                           if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN){//touch事件
                               eventType = eventType((MotionEvent)ev.event);
                           } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD ||
                                       ev.classType == RawInputEvent.CLASS_TRACKBALL) {//键盘输入事件
                               eventType = LocalPowerManager.BUTTON_EVENT;
                           } else {
                               eventType = LocalPowerManager.OTHER_EVENT;//其他事件
                           }
                           ...
                           switch (ev.classType) {
                               case RawInputEvent.CLASS_KEYBOARD:
                                   ...
                                   dispatchKey((KeyEvent)ev.event, 0, 0);//键盘输入,派发key事件
                                   mQueue.recycleEvent(ev);
                                   break;
                               case RawInputEvent.CLASS_TOUCHSCREEN:
                                   dispatchPointer(ev, (MotionEvent)ev.event, 0,0);//touch事件,派发touch事件
                                   break;
                               case RawInputEvent.CLASS_TRACKBALL:
                                   dispatchTrackball(ev, (MotionEvent)ev.event, 0,0);//滚轮事件,派发Trackball事件
                                   break;
                               case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
                                   configChanged = true;
                                   break;
                               default:
                                   mQueue.recycleEvent(ev);//销毁事件
                               break;
                           }

                       }
                   } catch (Exception e) {
                       Slog.e(TAG,
                           "Input thread received uncaught exception: " e, e);
                   }
               }       
       }

    4.KeyEvent事件的传递主要可以划分为三步:过滤器、View树、Activity.

      过滤器部分对应的是(frameworks/base/policy/base/phone/com/Android/internal/policy/impl/PhoneWindowManager.java)PhoneWindowManager.java中的interceptKeyTq和interceptKeyTi这两个方法。它们的代码可以在中看到。

    这两个过滤器最大的不同就是interceptKeyTq用于RawEvent,而interceptKeyTi用于KeyEvent。

    在一个没有实体键盘的机器上,Power键会被interceptKeyTq这个过滤器吃掉用来调用关机对话框或者使机器休眠。而Home键会被interceptKeyTi这个过滤器吃掉,用来把当前Activity切换到后台并把桌面程序切换到前台。所以,应用程序在View和Activity的onKeyDown/Up中是监听不到这两个按键的。除了这两个键以外的按键,都有机会继续前进。接下来,KeyEvent会先经过interceptKeyTi过滤器,如果这个过滤器不吃掉的话,就会继续前进,进入View树,如果没有被哪个View吃掉的话,最后进入到Activity的onKeyDown/Up方法中。

    当一个KeyEvent经过上面的过程还没有被吃掉的话,系统就会利用它做一些定制的功能。比如音量键被系统用来调整声音,多媒体按键用来控制媒体播放,搜索键用来快速打开搜索功能,返回键用来退出当前Activity等。


     

    展开全文
  • android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。  一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->...
    android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。 
    一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP
    当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是ViewGroup来处理Touch事件,还是子view来处理Touch事件呢?我只能很肯定的对你说不一定。呵呵,为什么呢?看看下面我的调查结果你就明白了。
    android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:
    1)public boolean dispatchTouchEvent(MotionEvent ev)  这个方法用来分发TouchEvent
    2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
    3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent

    当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View, TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由  dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。


    让子先处理的方法是 从写父的onInterceptTouchEvent事件并返回false

    public boolean onInterceptTouchEvent(MotionEvent ev) {
            return false;
        }
    展开全文
  • Android事件传递机制【按键事件】 作者:lenomon 发布:...Android按键事件处理主要在View和Activity两个级别。和Touch事件相比,简单很多。 按键事件的处理机制如下: 1、默认情况下,如果没有View获得焦点,
  • Android事件处理机制

    2014-11-12 10:09:37
    学习Android以来一直搞不懂Android的... android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件。所以我们就以Touch事件为例来探讨一下。  我写了一个测试项目,它的结构图如下  
  • 公司最近做一个按键触感就对这块研究了一番,以下是个人心得。 1.开始肯定先说的是驱动这块,硬件是软件服务的,... 1.1:驱动--JNI--服务-事件分发-上层应用处理。  1.2:上层直接调用通过lib库的方式实现,中
  • Android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件。 常见的动作常量(MotionEvent): public static final int ACTION_DOWN = 0;单点触摸动作 public sta...
  • Android+按键事件处理详解

    千次阅读 2014-06-12 11:16:15
    Android+按键事件处理详解     公司最近做一个按键触感就对这块研究了一番,以下是个人心得。 1.开始肯定先说的是驱动这块,硬件是软件服务的,在Android这块C和java交互,有两种方式:  ...
  • 按键事件(KeyEvent) 触摸事件(TouchEvent) 点击事件(clickEvent) 二:处理机制分类 基于监听接口的事件处理 基于回调的事件处理 Handler消息传递机制 三:事件传递与处理遵循的规则 1、如果界面控件设置了...
  • android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE-&...
  • android应用开发中经常会处理点击按键事件,那么当用户点击一个按钮后系统是怎么把点击事件分配给一个某控件处理的呢? 当用户点击一个按钮后系统处理顺序为Activity->Window->View既事件先传递给Actity在传...
  • android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE-&...
  • android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。  一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->...
  • android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件。  一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->...
  • Cocos2dx事件处理机制按键事件

    千次阅读 2014-03-06 15:19:12
    一、按键事件    使用IOS系统设备中只有Home键,按下Home键,程序进入后台。而使用Android系统的部分设备会有Menu键和 返回键,因此有时需要检测设备的键盘输入,使用键盘事件代理CCKeypadDelegate处理相应的...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 156
精华内容 62
关键字:

android按键事件处理机制