zoukankan      html  css  js  c++  java
  • 按键驱动调试总结



    今天把6516的按键调试结束总结一下;

    本身mtk6516的工程已经整合的很好,客户在修改上很方便,有个专门的工具来修改按键,通过对pcb上的按键进行row colum的定位就知道你需要

    修改的按键在什么地方了, 当然这个要根据情况来修改, 说实话,真的太简单了。。


    调试后分析了一下,按键的整个调试和驱动的流程。

     \alps\frameworks\base\libs\ui\EventHub.cpp  这个文件就用户输入系统的中枢。 我分析了一下这个文件。

    一、Android底层按键事件处理过程
     
       在系统启动后,在文件。。。中,android 会通过
        static const char *device_path = "/dev/input"; 
        bool EventHub::penPlatformInput(void) 
        res = scan_dir(device_path);
        通过下面的函数打开设备。
    //从目录下查找设备
    int EventHub::scan_dir(const char *dirname)
    {
        char devname[PATH_MAX];
        char *filename;
        DIR *dir;
        struct dirent *de;
        dir = opendir(dirname);
        if(dir == NULL)
            return -1;
        strcpy(devname, dirname);
        filename = devname + strlen(devname);
        *filename++ = '/';
        while((de = readdir(dir))) {
            if(de->d_name[0] == '.' &&
               (de->d_name[1] == '\0' ||
                (de->d_name[1] == '.' && de->d_name[2] == '\0')))
                continue;
            strcpy(filename, de->d_name);
            open_device(devname);
        }
        closedir(dir);
        return 0;
    }
    找到后就调用    int EventHub::open_device(const char *deviceName) 打开设备。    static const char *device_path = "/dev/input"; 这个目录下的都是被搜索到。

     int EventHub::open_device(const char *deviceName)
    {
        int version;
        int fd;
        struct pollfd *new_mFDs;
        device_t **new_devices;
        char **new_device_names;
        char name[80];
        char location[80];
        char idstr[80];
        struct input_id id;
    
        LOGV("Opening device: %s", deviceName);
    
        AutoMutex _l(mLock);
    
        fd = open(deviceName, O_RDWR);
        if(fd < 0) {
            LOGE("could not open %s, %s\n", deviceName, strerror(errno));
            return -1;
        }
    
        if(ioctl(fd, EVIOCGVERSION, &version)) {
            LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno));
            return -1;
        }
        if(ioctl(fd, EVIOCGID, &id)) {
            LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno));
            return -1;
        }
        name[sizeof(name) - 1] = '\0';
        location[sizeof(location) - 1] = '\0';
        idstr[sizeof(idstr) - 1] = '\0';
        if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
            //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno));
            name[0] = '\0';
        }
    
        // check to see if the device is on our excluded list
        List<String8>::iterator iter = mExcludedDevices.begin();
        List<String8>::iterator end = mExcludedDevices.end();
        for ( ; iter != end; iter++) {
            const char* test = *iter;
            if (strcmp(name, test) == 0) {
                LOGI("ignoring event id %s driver %s\n", deviceName, test);
                close(fd);
                fd = -1;
                return -1;
            }
        }
    
        if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
            //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno));
            location[0] = '\0';
        }
        if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
            //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno));
            idstr[0] = '\0';
        }
    
        int devid = 0;
        while (devid < mNumDevicesById) {
            if (mDevicesById[devid].device == NULL) {
                break;
            }
            devid++;
        }
        if (devid >= mNumDevicesById) {
            device_ent* new_devids = (device_ent*)realloc(mDevicesById,
                    sizeof(mDevicesById[0]) * (devid + 1));
            if (new_devids == NULL) {
                LOGE("out of memory");
                return -1;
            }
            mDevicesById = new_devids;
            mNumDevicesById = devid+1;
            mDevicesById[devid].device = NULL;
            mDevicesById[devid].seq = 0;
        }
    
        mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
        if (mDevicesById[devid].seq == 0) {
            mDevicesById[devid].seq = 1<<SEQ_SHIFT;
        }
    
        new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1));
        new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1));
        if (new_mFDs == NULL || new_devices == NULL) {
            LOGE("out of memory");
            return -1;
        }
        mFDs = new_mFDs;
        mDevices = new_devices;
    
    #if 0
        LOGI("add device %d: %s\n", mFDCount, deviceName);
        LOGI("  bus:      %04x\n"
             "  vendor    %04x\n"
             "  product   %04x\n"
             "  version   %04x\n",
            id.bustype, id.vendor, id.product, id.version);
        LOGI("  name:     \"%s\"\n", name);
        LOGI("  location: \"%s\"\n"
             "  id:       \"%s\"\n", location, idstr);
        LOGI("  version:  %d.%d.%d\n",
            version >> 16, (version >> 8) & 0xff, version & 0xff);
    #endif
    
        device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);
        if (device == NULL) {
            LOGE("out of memory");
            return -1;
        }
    
        mFDs[mFDCount].fd = fd;
        mFDs[mFDCount].events = POLLIN;
    
        // figure out the kinds of events the device reports
        
        // See if this is a keyboard, and classify it.  Note that we only
        // consider up through the function keys; we don't want to include
        // ones after that (play cd etc) so we don't mistakenly consider a
        // controller to be a keyboard.
        uint8_t key_bitmask[(KEY_MAX+7)/8];
        memset(key_bitmask, 0, sizeof(key_bitmask));
        LOGV("Getting keys...");
        if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
            //LOGI("MAP\n");
            //for (int i=0; i<((KEY_MAX+7)/8); i++) {
            //    LOGI("%d: 0x%02x\n", i, key_bitmask[i]);
            //}
            for (int i=0; i<((BTN_MISC+7)/8); i++) {
                if (key_bitmask[i] != 0) {
                    device->classes |= CLASS_KEYBOARD;
                    break;
                }
            }
            if ((device->classes & CLASS_KEYBOARD) != 0) {
                device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
                if (device->keyBitmask != NULL) {
                    memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
                } else {
                    delete device;
                    LOGE("out of memory allocating key bitmask");
                    return -1;
                }
            }
        }
        
        // See if this is a trackball.
        if (test_bit(BTN_MOUSE, key_bitmask)) {
            uint8_t rel_bitmask[(REL_MAX+7)/8];
            memset(rel_bitmask, 0, sizeof(rel_bitmask));
            LOGV("Getting relative controllers...");
            if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0)
            {
                if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
                    device->classes |= CLASS_TRACKBALL;
                }
            }
        }
        
        uint8_t abs_bitmask[(ABS_MAX+7)/8];
        memset(abs_bitmask, 0, sizeof(abs_bitmask));
        LOGV("Getting absolute controllers...");
        ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask);
        
        // Is this a new modern multi-touch driver?
        if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)
                && test_bit(ABS_MT_POSITION_X, abs_bitmask)
                && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
            device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT;
            
        // Is this an old style single-touch driver?
        } else if (test_bit(BTN_TOUCH, key_bitmask)
                && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
            device->classes |= CLASS_TOUCHSCREEN;
        }
    
    #ifdef EV_SW
        // figure out the switches this device reports
        uint8_t sw_bitmask[(SW_MAX+7)/8];
        memset(sw_bitmask, 0, sizeof(sw_bitmask));
        if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
            for (int i=0; i<EV_SW; i++) {
                //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
                if (test_bit(i, sw_bitmask)) {
                    if (mSwitches[i] == 0) {
                        mSwitches[i] = device->id;
                    }
                }
            }
        }
    #endif
    
        if ((device->classes&CLASS_KEYBOARD) != 0) {
            char tmpfn[sizeof(name)];
            char keylayoutFilename[300];
    
            // a more descriptive name
            device->name = name;
    
            // replace all the spaces with underscores
            strcpy(tmpfn, name);
            for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
                *p = '_';
    
            // find the .kl file we need for this device
            const char* root = getenv("ANDROID_ROOT");//取得root权限
            snprintf(keylayoutFilename, sizeof(keylayoutFilename),
                     "%s/usr/keylayout/%s.kl", root, tmpfn);
            bool defaultKeymap = false;
            if (access(keylayoutFilename, R_OK)) {
                snprintf(keylayoutFilename, sizeof(keylayoutFilename),//不能调用特定的kl文件就要用默认的。
                         "%s/usr/keylayout/%s", root, "qwerty.kl");
                defaultKeymap = true;
            }
            device->layoutMap->load(keylayoutFilename);//交给keylayoutmap 解析,这个是一个内部类
    
            // tell the world about the devname (the descriptive name)
            if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
                // the built-in keyboard has a well-known device ID of 0,
                // this device better not go away.
                mHaveFirstKeyboard = true;
                mFirstKeyboardId = device->id;
                property_set("hw.keyboards.0.devname", name);
            } else {
                // ensure mFirstKeyboardId is set to -something-.
                if (mFirstKeyboardId == 0) {
                    mFirstKeyboardId = device->id;
                }
            }
            char propName[100];
            sprintf(propName, "hw.keyboards.%u.devname", device->id);
            property_set(propName, name);
    
            // 'Q' key support = cheap test of whether this is an alpha-capable kbd
            if (hasKeycode(device, kKeyCodeQ)) {
                device->classes |= CLASS_ALPHAKEY;
            }
            
            // See if this has a DPAD.
            if (hasKeycode(device, kKeyCodeDpadUp) &&
                    hasKeycode(device, kKeyCodeDpadDown) &&
                    hasKeycode(device, kKeyCodeDpadLeft) &&
                    hasKeycode(device, kKeyCodeDpadRight) &&
                    hasKeycode(device, kKeyCodeDpadCenter)) {
                device->classes |= CLASS_DPAD;
            }
            
            LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
                    device->id, name, propName, keylayoutFilename);
        }
    
        LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
             deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
             
        LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n",
             deviceName, device, mFDCount, devid, device->classes);
    
        mDevicesById[devid].device = device;
        device->next = mOpeningDevices;
        mOpeningDevices = device;
        mDevices[mFDCount] = device;
    
        mFDCount++;
        return 0;
    }
    
    
     

    看到这个一段程序
     // find the .kl file we need for this device
            const char* root = getenv("ANDROID_ROOT");//取得root权限
            snprintf(keylayoutFilename, sizeof(keylayoutFilename),
                     "%s/usr/keylayout/%s.kl", root, tmpfn);
            bool defaultKeymap = false;
            if (access(keylayoutFilename, R_OK)) {
                snprintf(keylayoutFilename, sizeof(keylayoutFilename),//不能调用特定的kl文件就要用默认的。
                         "%s/usr/keylayout/%s", root, "qwerty.kl");
                defaultKeymap = true;
            }
            device->layoutMap->load(keylayoutFilename);//交给keylayoutmap 解析,这个是一个内部类
    
    还有一个消息获取函数
    //获取事件,无限循环得到事件,调用阻塞函数等待
    bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
            int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
            int32_t* outValue, nsecs_t* outWhen)
    {
        *outDeviceId = 0;
        *outType = 0;
        *outScancode = 0;
        *outKeycode = 0;
        *outFlags = 0;
        *outValue = 0;
        *outWhen = 0;
    
        status_t err;
    
        fd_set readfds;
        int maxFd = -1;
        int cc;
        int i;
        int res;
        int pollres;
        struct input_event iev;
    
        // Note that we only allow one caller to getEvent(), so don't need
        // to do locking here...  only when adding/removing devices.
    
        if (!mOpened) {
            mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
            mOpened = true;
        }
    
        while(1) {
    
            // First, report any devices that had last been added/removed.
            if (mClosingDevices != NULL) {
                device_t* device = mClosingDevices;
                LOGV("Reporting device closed: id=0x%x, name=%s\n",
                     device->id, device->path.string());
                mClosingDevices = device->next;
                *outDeviceId = device->id;
                if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
                *outType = DEVICE_REMOVED;
                delete device;
                return true;
            }
            if (mOpeningDevices != NULL) {
                device_t* device = mOpeningDevices;
                LOGV("Reporting device opened: id=0x%x, name=%s\n",
                     device->id, device->path.string());
                mOpeningDevices = device->next;
                *outDeviceId = device->id;
                if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
                *outType = DEVICE_ADDED;
                return true;
            }
    
            release_wake_lock(WAKE_LOCK_ID);
    // 阻塞等待!!!!!
            pollres = poll(mFDs, mFDCount, -1);
    
            acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    
            if (pollres <= 0) {
                if (errno != EINTR) {
                    LOGW("select failed (errno=%d)\n", errno);
                    usleep(100000);
                }
                continue;
            }
    
            //printf("poll %d, returned %d\n", mFDCount, pollres);
    
            // mFDs[0] is used for inotify, so process regular events starting at mFDs[1]
            for(i = 1; i < mFDCount; i++) {
                if(mFDs[i].revents) {
                    LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
                    if(mFDs[i].revents & POLLIN) {
                        res = read(mFDs[i].fd, &iev, sizeof(iev));//读取信息!!事件代码
                        if (res == sizeof(iev)) {
                            LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
                                 mDevices[i]->path.string(),
                                 (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                                 iev.type, iev.code, iev.value);
                            *outDeviceId = mDevices[i]->id;
                            if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
                            *outType = iev.type;
                            *outScancode = iev.code;
                            if (iev.type == EV_KEY) {
                                err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
                                LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
                                    iev.code, *outKeycode, *outFlags, err);
                                if (err != 0) {
                                    *outKeycode = 0;
                                    *outFlags = 0;
                                }
    #ifdef HAVE_AEE_FEATURE
    #ifdef AEE_MANUAL_DB_DUMP
                                aee_aed_dump_db((unsigned int)iev.code, 
                                    (signed int)iev.value, 0);
    #endif
    #endif
                            } else {
                                *outKeycode = iev.code;
                            }
                            *outValue = iev.value;
                            *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
                            return true;
                        } else {
                            if (res<0) {
                                LOGW("could not get event (errno=%d)", errno);
                            } else {
                                LOGE("could not get event (wrong size: %d)", res);
                            }
                            continue;
                        }
                    }
                }
            }
            
            // read_notify() will modify mFDs and mFDCount, so this must be done after
            // processing all other events.
            if(mFDs[0].revents & POLLIN) {
                read_notify(mFDs[0].fd);
            }
        }
    }



      打开设备的时候,如果 device->classes&CLASS_KEYBOARD 不等于 0 表明是键盘。 
      常用输入设备的定义有: 
      enum { 
            CLASS_KEYBOARD      = 0x00000001, //键盘 
            CLASS_ALPHAKEY      = 0x00000002, // 
            CLASS_TOUCHSCREEN   = 0x00000004, //触摸屏 
            CLASS_TRACKBALL     = 0x00000008  //轨迹球 
      };
      打开键盘设备的时候通过上面的 ioctl 获得设备名称,命令字 EVIOCGNAME 的定义在文件:  
      kernel/include/linux/input.h 中。
      对于按键事件,调用mDevices->layoutMap->map进行映射,调用的是文件 KeyLayoutMap.cpp
      (frameworks\base\libs\ui)中的函数:
      status_t KeyLayoutMap::load(const char* filename)通过解析 <Driver name>.kl 把按键的
      映射关系保存在 :KeyedVector<int32_t,Key> m_keys; 中。
      当获得按键事件以后调用: status_t KeyLayoutMap::map(int32_t scancode, int32_t
      *keycode, uint32_t *flags)
      由映射关系 KeyedVector<int32_t,Key> m_keys 把扫描码转换成andorid上层可以识别的按键。

    二、按键映射
     
      Key layout maps的路径是 /system/usr/keylayout,第一个查找的名字是按键驱动的名字,例如
      mxckpd.kl。如果没有的话,默认为qwerty.kl。
      Key character maps的路径是 /system/usr/keychars,第一个查找的名字是按键驱动的名字,例如
      mxckpd.kcm。如果没有的话,默认为qwerty.kl。
      
      qwerty.kl是 UTF-8类型的,格式为:key SCANCODE KEYCODE [FLAGS...]。
      
      SCANCODE表示按键扫描码;
      KEYCODE表示键值,例如HOME,BACK,1,2,3...
      FLAGS有如下定义:
        SHIFT: While pressed, the shift key modifier is set 
        ALT: While pressed, the alt key modifier is set 
        CAPS: While pressed, the caps lock key modifier is set 
        WAKE: When this key is pressed while the device is asleep, the device will
                      wake up and the key event gets sent to the app. 
        WAKE_DROPPED: When this key is pressed while the device is asleep, the device
                      will wake up and the key event does not get sent to the app
      
      qwerty.kcm文件为了节省空间,在编译过程中会用工具makekcharmap转化为二进制文件qwerty.bin。
    三、按键分发
     
      1、输入事件分发线程
      
        在frameworks/base/services/java/com/android/server/WindowManagerService.java里创
        建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。
        
        在WindowManagerService类的构造函数WindowManagerService()中:
            mQueue = new KeyQ(); //读取按键
            mInputThread = new InputDispatcherThread();  //创建分发线程     
            ...     
            mInputThread.start();
          
        在启动的线程InputDispatcherThread中:
            run() 
            process(); 
            QueuedEvent ev = mQueue.getEvent(...) 
          
          在process() 方法中进行处理事件:
            switch (ev.classType) 
              case RawInputEvent.CLASS_KEYBOARD: 
                 ... 
                 dispatchKey((KeyEvent)ev.event, 0, 0); 
                 mQueue.recycleEvent(ev); 
                 break; 
              case RawInputEvent.CLASS_TOUCHSCREEN: 
                 //Log.i(TAG, "Read next event " + ev); 
                 dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); 
                 break; 
             case RawInputEvent.CLASS_TRACKBALL:
                 dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
                 break;
                 
      2、上层读取按键的流程
     
        WindowManagerService()  //(frameworks\base\services\java\com\android\server
                                                        \WindowManagerService.java)
          |
        KeyQ()  //KeyQ 是抽象类 KeyInputQueue 的实现
          |
        InputDeviceReader //在 KeyInputQueue 类中创建的线程 
         |
        readEvent()  //
         |
        android_server_KeyInputQueue_readEvent() //frameworks\base\services\jni\
                                                  com_android_server_KeyInputQueue.cpp
         |
        hub->getEvent()
         |
        EventHub::getEvent() //frameworks\base\libs\ui\EventHub.cpp
         |
        res = read(mFDs.fd, &iev, sizeof(iev)); //


    摘取网上的一段资料

    键盘消息是window manager从驱动里面获取的,然后分发给应用程序框架的

    具体参考如下:
    2.1 第一步:用户数据收集及其初步判定
         KeyInputQ在WindowMangerService中建立一个独立的线程InputDeviceReader,使用Native函数readEvent来读取Linux Driver的数据构建RawEvent,放入到KeyQ消息队列中。

    preProcessEvent()@KeyInptQ@KeyInputQueue.java这个是在输入系统中的第一个拦截函数原型。KeyQ重载了preProcessEvent()@WindowManagerService.java。在该成员函数中进行如下动作:
    (1) 根据PowerManager获取的Screen on,Screen off状态来判定用户输入的是否WakeUPScreen。
    (2) 如果按键式应用程序切换按键,则切换应用程序。
    (3) 根据WindowManagerPolicy觉得该用户输入是否投递。
    2.2 第二步 消息分发第一层面
    InputDispatcherThread从KeyQ中读取Events,找到Window Manager中的Focus Window,通过Focus Window记录的mClient接口,将Events专递到Client端。

    如何将KeyEvent对象传到Client端:
    在前面的章节(窗口管理ViewRoot,Window Manager Proxy)我们已经知道:在客户端建立Window Manager Proxy后,添加窗口到Window Manager service时,带了一个跟客户ViewRoot相关的IWindow接口实例过去,记录在WindowState中的mClient成员变量中。通过IWindow这个AIDL接口实例,Service可以访问客户端的信息,IWindow是Service连接View桥梁。

    看看在Client ViewRootKeyEvent的分发过程
    IWindow:dispatchKey(event)
    dispatchKey(event)@W@ViewRoot@ViewRoot.java
           ViewRoot.dispatchKey(event)@ViewRoot.java
                         message>
                         sendMessageAtTime(msg)@Handler@Handler.java
    至此我们通过前面的Looper,Handler详解章节的分析结论,我们可以知道Key Message已经放入到应用程序的消息队列。
    2.3第三步:应用消息队列分发
       消息的分发,在Looper,Handler详解章节我们分析了Looper.loop()在最后后面调用了handleMesage.
              …
               ActivityThread.main()
                    Looper.loop()
                      ViewRoot$RootHandler().dispatch()
                          handleMessage
                              ....
          注意到在分发的调用msg.target.dispatch(),而这个target在第二层将消息sendMessageAtTime到消息队列时填入了mag.target=this即为msg.target=ViewRoot实例。所有此时handleMessage就是ViewRoot重载的handleMessage函数。
    handlerMessage@ViewRoot@ViewRoot.java
           deliverkeyEvent
                如果输入法存在,dispatchKey到输入法服务。
                否则deliverKeyEventToViewHierarchy@ViewRoot.java
         在这里需要强调的是,输入法的KeyEvent的拦截并没有放入到Window Manager Service中,而是放入到了客户端的RootView中来处理。
    2.4第四步:向焦点进发,完成焦点路径的遍历。

    分发函数调用栈
    deliverKeyEventToViewHierarchy@ViewRoot.java
    mView.dispatchKeyEvent:mView是与ViewRoot相对应的Top-Level View.如果mView是一个ViewGroup则分发消息到他的mFocus。
    mView.dispatchKeyEvent @ViewGroup  (ViewRoot@root)
                   Event.dispatch
                            mFocus.dispatchKeyEevnet
        如果此时的mFocu还是一个ViewGroup,这回将事件专递到下一层的焦点,直到mFocus为一个View。通过这轮调用,就遍历了焦点Path,至此,用户事件传递完成一个段落。
    2.5第五步 缺省处理
    如果事件在上述Focus View没有处理掉,并且为方向键之类的焦点转换相关按键,则转移焦点到下一个View。


  • 相关阅读:
    Linux C编程之十二 信号
    折腾vue--vue自定义组件(三)
    折腾vue--使用vscode创建vue项目(二)
    折腾vue--环境搭建(一)
    .net生成PDF文件的几种方式
    星星评分-依赖jquery
    组织机构树
    Newtonsoft--自定义格式化日期
    .net mvc 自定义错误页面
    js模拟form提交 导出数据
  • 原文地址:https://www.cnblogs.com/yuzaipiaofei/p/4124421.html
Copyright © 2011-2022 走看看