native层实现touch事件转key事件

需求

    最近公司来了一个需求,需要将touch事件转成key事件,只针对滑动事件与触摸事件。


需求分析

    首先这个需求是可以在kernel里面做的,由于我们没有kernel代码,因此这个方案就被pass掉了。第二个想到的是在java层做,想找一个拦截touch事件的方法,类似PhoneWindowMananger中拦截key事件的方法,可是没找到,找到的地方都已经到app层了,这样就可能有点晚了(也可能java层有,只是我还没找到合适的地方)。最后没办法将android事件分发机制重新看了一遍,决定直接在native层做,也就是读取event的地方----InputReader.cpp中。


具体实现

    这个需求里面最重要的就是找到合适的地方拦截touch然后转成key。

touch事件分发的函数:

void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
        int32_t action, int32_t actionButton, int32_t flags,
        int32_t metaState, int32_t buttonState, int32_t edgeFlags,
        const PointerProperties* properties, const PointerCoords* coords,
        const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId,
        float xPrecision, float yPrecision, nsecs_t downTime) {
    PointerCoords pointerCoords[MAX_POINTERS];
    PointerProperties pointerProperties[MAX_POINTERS];
    uint32_t pointerCount = 0;
    while (!idBits.isEmpty()) {
        uint32_t id = idBits.clearFirstMarkedBit();
        uint32_t index = idToIndex[id];
        pointerProperties[pointerCount].copyFrom(properties[index]);
        pointerCoords[pointerCount].copyFrom(coords[index]);

        if (changedId >= 0 && id == uint32_t(changedId)) {
            action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
        }

        pointerCount += 1;
    }

    ALOG_ASSERT(pointerCount != 0);

    if (changedId >= 0 && pointerCount == 1) {
        // Replace initial down and final up action.
        // We can compare the action without masking off the changed pointer index
        // because we know the index is 0.
        if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
            action = AMOTION_EVENT_ACTION_DOWN;
        } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
            action = AMOTION_EVENT_ACTION_UP;
        } else {
            // Can't happen.
            ALOG_ASSERT(false);
        }
    }

    NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
            action, actionButton, flags, metaState, buttonState, edgeFlags,
            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
            xPrecision, yPrecision, downTime);
    getListener()->notifyMotion(&args);
}

可以看到dispatchMotion这个函数的最后,构建了一个NotifyMotionArgs,然后通过getListener()->notifyMotion(&args)函数分发,NotifyMotionArgs这个类构造函数中的参数就是touch事件的参数,比如eventTime(也就是when)、action、xy坐标等等都是后面我们要用的。


InputReader.cpp中也有key事件的分发函数,我们可以利用这个函数来转key事件:

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;

    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
        keyCode = AKEYCODE_UNKNOWN;
        keyMetaState = mMetaState;
        policyFlags = 0;
    }

    if (down) {
        // Rotate key codes according to orientation if needed.
        if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
            keyCode = rotateKeyCode(keyCode, mOrientation);
        }

        // Add key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            // key repeat, be sure to use same keycode as before in case of rotation
            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
        } else {
            // key down
            if ((policyFlags & POLICY_FLAG_VIRTUAL)
                    && mContext->shouldDropVirtualKey(when,
                            getDevice(), keyCode, scanCode)) {
                return;
            }
            if (policyFlags & POLICY_FLAG_GESTURE) {
                mDevice->cancelTouch(when);
            }

            mKeyDowns.push();
            KeyDown& keyDown = mKeyDowns.editTop();
            keyDown.keyCode = keyCode;
            keyDown.scanCode = scanCode;
        }

        mDownTime = when;
    } else {
        // Remove key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            // key up, be sure to use same keycode as before in case of rotation
            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
            mKeyDowns.removeAt(size_t(keyDownIndex));
        } else {
            // key was not actually down
            ALOGI("Dropping key up from device %s because the key was not down.  "
                    "keyCode=%d, scanCode=%d",
                    getDeviceName().string(), keyCode, scanCode);
            return;
        }
    }

    if (updateMetaStateIfNeeded(keyCode, down)) {
        // If global meta state changed send it along with the key.
        // If it has not changed then we'll use what keymap gave us,
        // since key replacement logic might temporarily reset a few
        // meta bits for given key.
        keyMetaState = mMetaState;
    }

    nsecs_t downTime = mDownTime;

    // Key down on external an keyboard should wake the device.
    // We don't do this for internal keyboards to prevent them from waking up in your pocket.
    // For internal keyboards, the key layout file should specify the policy flags for
    // each wake key individually.
    // TODO: Use the input device configuration to control this behavior more finely.
    if (down && getDevice()->isExternal()) {
        policyFlags |= POLICY_FLAG_WAKE;
    }

    if (mParameters.handlesKeyRepeat) {
        policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
    }

    if (down && !isMetaKey(keyCode)) {
        getContext()->fadePointer();
    }

    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);
}

函数的最后我们可以看到一个与touch事件类似的分发调用,getListener()->notifyKey(&args)。

补充:其实这个getListener就是InputDispatcher对象。


总结

    找到这两个地方,转换的需求也就简单了,就是在TouchInputMapper::dispatchMotion中拦截touch事件,然后构建一个NotifyKeyArgs对象,通过调用getListener()->notifyKey(&args)来转发key事件。代码涉及到公司私密的逻辑我就不贴出来了,大概就是根据滑动的坐标转成相应的键值再转发key事件。


<think>嗯,用户想用vue-seamless-scroll组件实现横向滚动图片,同时添加点击事件。但是遇到了点击失效的问题。首先,我需要回忆一下这个组件的使用方法和常见问题。 根据提供的引用,特别是引用[3]里的代码,用户是在Vue的全局组件里注册了vue-seamless-scroll,并命名为vListScroll。所以用户应该在模板里使用v-list-scroll标签。横向滚动的话,应该设置direction为2,因为引用里提到水平方向是2。那用户需要检查自己的配置是否正确。 然后,点击事件失效可能是因为滚动内容中的元素被组件的动画或事件冒泡干扰了。比如,组件可能使用了touch事件来处理滚动,导致点击事件无法触发。这时候可能需要添加native修饰符,或者阻止事件冒泡。或者在事件处理函数里使用.stop修饰符。 另外,动态生成的内容如果使用v-for渲染,要确保每个图片元素都正确绑定了@click事件,并且没有在父元素上阻止了子元素的事件。比如,检查是否有父元素上的事件监听器拦截了点击事件。 还有,可能滚动组件内部的元素在动画过程中,导致点击坐标变化,或者元素被重新渲染,使得事件绑定失效。这时候可能需要确保图片元素在滚动过程中保持稳定,或者使用事件委托的方式绑定点击事件到父容器,然后通过事件对象的目标来判断点击的是哪个图片。 另外,引用[2]提到的是vue3-seamless-scroll,如果用户用的是Vue3,可能需要确认是否使用了正确的版本和对应的API。比如,Vue3中事件监听的方式是否有变化,或者组件是否有不同的配置参数。 最后,用户可能需要检查浏览器控制台是否有错误信息,比如事件处理函数未定义,或者组件未正确导入。如果一切配置正确,但点击仍不生效,可能需要查看组件的源码或文档,确认是否有已知的问题或需要特定的处理方式。</think>### 在vue-seamless-scroll中实现横向滚动图片并绑定点击事件 #### 步骤说明 1. **组件配置**:设置`direction: 2`启用水平滚动,并通过`listData`传递图片数据 ```html <v-list-scroll :data="listData" :direction="2" :step="1" :hover-stop="false"> <div class="scroll-content"> <img v-for="(item,index) in listData" :key="index" :src="item.img" @click="handleClick(item)" > </div> </v-list-scroll> ``` 2. **事件绑定要点**: - **事件穿透处理**:添加`.native`修饰符穿透组件事件拦截 ```html <v-list-scroll @click.native="handleWrapperClick"> <!-- 滚动内容 --> </v-list-scroll> ``` - **动态内容处理**:使用`v-if`确保DOM更新后事件绑定有效 ```html <img v-if="item.loaded" @click="handleClick(item)"> ``` 3. **CSS关键配置**: ```css .scroll-content { display: flex; /* 水平布局 */ white-space: nowrap; /* 禁止换行 */ } img { pointer-events: auto; /* 覆盖组件默认样式 */ flex-shrink: 0; /* 防止图片压缩 */ } ``` #### 常见问题解决 1. **点击失效场景**: - 组件默认禁用子元素事件,需在组件标签添加`@click.native` - 图片动态加载未完成时绑定事件,使用`v-if`控制渲染时机 2. **性能优化**: ```js // 分页加载大数据量 const loadChunk = () => { this.listData = [...data.slice(0, 20)] this.$nextTick(() => { this.$refs.scroll.update() // 手动更新滚动容器[^3] }) } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值