功能机用上下键实现MoveEvent
這個(gè)功能的用處功能手機(jī)(這里說(shuō)的功能機(jī)只是沒(méi)有觸屏,單還是Android系統(tǒng))能在瀏覽器中使用上下鍵實(shí)現(xiàn)移動(dòng)光標(biāo)的目的,這里我們大致分析流程。
我們知道普通按鍵,會(huì)在KeyboardInputMapper的process執(zhí)行,比如這里我們要關(guān)注的上下左右按鍵。
void KeyboardInputMapper::process(const RawEvent* rawEvent) { #if CURSOR_LOGSLOGE("KeyboardInputMapper::process rawEvent->code =[%d], rawEvent->deviceId=[%d], ""rawEvent->type=[%d], rawEvent->value=[%d], rawEvent->when=[%f] \n", rawEvent->code,rawEvent->deviceId, rawEvent->type, rawEvent->value, rawEvent->when); #endifswitch (rawEvent->type) {case EV_KEY: {int32_t scanCode = rawEvent->code;int32_t usageCode = mCurrentHidUsage;mCurrentHidUsage = 0;if (isKeyboardOrGamepadKey(scanCode)) {int32_t keyCode;uint32_t flags;if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {//通過(guò)kl將掃描碼轉(zhuǎn)換成按鍵碼keyCode = AKEYCODE_UNKNOWN;flags = 0;} #ifdef SUPPORT_BROWSER_VIRTUAL_CURSOR//是否支持瀏覽器虛擬鍵 #if CURSOR_LOGSLOGE("KeyboardInputMapper::process keyCode =[%d]", keyCode);LOGE("KeyboardInputMapper::process down time =[%lf] and down time =[%le] \n",rawEvent->when, rawEvent->when); #endif/* Reset state & allow release event only if Browser has gone to background.& If Press event is fired already. */if(!g_bIsBrowserAppForeground && g_bNeedsEventCompletion && AKEYCODE_ENTER == keyCode){/* Store Browser g_bIsBrowserAppForeground state. */g_bCursorState = g_bIsBrowserAppForeground;/* Reset g_bIsBrowserAppForeground state to allow one last release event. */g_bIsBrowserAppForeground = true;}//checking four way navigation keys to enable and control mouse pointerif(g_bIsBrowserAppForeground && (AKEYCODE_DPAD_UP == keyCode//當(dāng)前是瀏覽器,上下左右 確定按鍵過(guò)濾|| AKEYCODE_DPAD_DOWN == keyCode|| AKEYCODE_DPAD_LEFT == keyCode|| AKEYCODE_DPAD_RIGHT == keyCode|| AKEYCODE_ENTER == keyCode)) {/* Set NeedsEventCompletion to true on Press event. */if(AKEYCODE_ENTER == keyCode && rawEvent->value == 1) {g_bNeedsEventCompletion = true;//確定事件,而且是down事件}/* Set NeedsEventCompletion to false on release event. */if(AKEYCODE_ENTER == keyCode && rawEvent->value == 0) {g_bNeedsEventCompletion = false;//確定鍵 up事件}HandleCursorPointerForDpad(rawEvent, keyCode);}else {processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);// 如果不是瀏覽器,正常流程處理} #elseprocessKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags); #endif}break;}case EV_MSC: {if (rawEvent->code == MSC_SCAN) {mCurrentHidUsage = rawEvent->value;}break;}case EV_SYN: { #ifdef SUPPORT_BROWSER_VIRTUAL_CURSORif(g_bIsBrowserAppForeground) {// handling sync event for cursor pointerHandleCursorPointerForDpad(rawEvent, AKEYCODE_UNKNOWN);/* Hide the cursor after handling the release event for long Press */if(!g_bCursorState && !g_bNeedsEventCompletion) {InputMapper* mKeyboardCursorInputMapper = KeyboardInputMapper::getKeyboardCursorPointer();if(mKeyboardCursorInputMapper) {// fade the cursor pointer when browser app is paused.mKeyboardCursorInputMapper->fadePointer();}/* Restore the state after hiding cursor*/g_bIsBrowserAppForeground = g_bCursorState;g_bCursorState = true;}} #endifif (rawEvent->code == SYN_REPORT) {mCurrentHidUsage = 0;}}} }函數(shù)HandleCursorPointerForDpad就是對(duì)長(zhǎng)按鍵進(jìn)行處理,然后調(diào)用了DispatchMouseEventForDPAD函數(shù)。
void KeyboardInputMapper::HandleCursorPointerForDpad(const RawEvent* KrawEvent, int32_t keyCode) { #if CURSOR_LOGSLOGE("KeyboardInputMapper::HandleCursorPointerForDpad keyCode = [%d]",keyCode); #endifif(keyCode != AKEYCODE_ENTER && keyCode != AKEYCODE_UNKNOWN) {// check if key pressedif(KrawEvent->value != 0) {G_LPData.rawEvent = *KrawEvent ;G_LPData.keyCode = keyCode ;// Increase initial timeout for long press.G_LPData.longPressTimeOut = KrawEvent->when + INITIAL_MAX_LONG_PRESS_NSECS;G_LPData.IsOn = true ; #if CURSOR_LOGSLOGE("KeyboardInputMapper::HandleCursorPointerForDpad calling ""getContext()->requestTimeoutAtTime keyCode = [%d]",keyCode); #endif//when key is pressed, mentaion the key state and request for timeout to handle key long pressgetContext()->requestTimeoutAtTime(G_LPData.longPressTimeOut);} else { #if CURSOR_LOGSLOGE("KeyboardInputMapper::HandleCursorPointerForDpad KrawEvent->when = [%le]",KrawEvent->when); #endif// Reset the scroll count if key up is received.g_bScrollCount = 0;// here reseting the key state which was preserved when key pressed.if(keyCode == G_LPData.keyCode && G_LPData.IsOn == true) {G_LPData.IsOn = false;G_LPData.keyCode = 0 ; #if CURSOR_LOGSLOGE("KeyboardInputMapper::HandleCursorPointerForDpad Resetting global ""event event keyCode = [%d]",keyCode); #endif}}}DispatchMouseEventForDPAD(mCursorInputMapper, KrawEvent, keyCode, CURSOR_MOVE_PIXELS);//最后一個(gè)參數(shù)就是每一次的步長(zhǎng) }函數(shù)DispatchMouseEventForDPAD,對(duì)RawEvent重新進(jìn)行封裝,然后調(diào)用了CursorInputMapper::process,CursorInputMapper是專(zhuān)門(mén)處理光標(biāo)的。
void DispatchMouseEventForDPAD(InputMapper* CursorMapper, const RawEvent* KrawEvent, int32_t keyCode, int iMovePixels) {/* Move the cursor only when key is pressed. On the key release,* pass the event but do not move the cursor. */if(KrawEvent->value == 0) {iMovePixels = 0;}RawEvent rawEvent ;rawEvent.when = KrawEvent->when;rawEvent.deviceId = KrawEvent->deviceId;switch(keyCode) {case AKEYCODE_DPAD_UP:rawEvent.type = EV_REL;rawEvent.code = REL_Y;rawEvent.value = -iMovePixels;//往上應(yīng)該是y的坐標(biāo)減步長(zhǎng)break;case AKEYCODE_DPAD_DOWN:rawEvent.type = EV_REL;rawEvent.code = REL_Y;rawEvent.value = iMovePixels;break;case AKEYCODE_DPAD_LEFT:rawEvent.type = EV_REL;rawEvent.code = REL_X;rawEvent.value = -iMovePixels;break;case AKEYCODE_DPAD_RIGHT:rawEvent.type = EV_REL;rawEvent.code = REL_X;rawEvent.value = iMovePixels;break;case AKEYCODE_ENTER:rawEvent.type = EV_KEY ;rawEvent.code = BTN_LEFT ;rawEvent.value = KrawEvent->value;break;case AKEYCODE_UNKNOWN:rawEvent.type = EV_SYN ;rawEvent.code = KrawEvent->code ;}// call process of cursorinputmapper to process the mouse event constructedCursorMapper->process(&rawEvent); }這個(gè)函數(shù)先調(diào)用三個(gè)Accumulator的process來(lái)保存rawEvent的值,最后調(diào)用了sync函數(shù)。
void CursorInputMapper::process(const RawEvent* rawEvent) {mCursorButtonAccumulator.process(rawEvent);//把rawEvent的值進(jìn)行保存mCursorMotionAccumulator.process(rawEvent);mCursorScrollAccumulator.process(rawEvent);#ifdef SUPPORT_BROWSER_VIRTUAL_CURSORif(g_bIsBrowserAppForeground && mPointerController == NULL) {// create and configure the pointer controllermSource = AINPUT_SOURCE_MOUSE;mXPrecision = 1.0f;mYPrecision = 1.0f;mXScale = 1.0f;mYScale = 1.0f;mPointerController = getPolicy()->obtainPointerController(0);//這個(gè)用來(lái)保存光標(biāo)的位置信息} #endifif (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {sync(rawEvent->when);//調(diào)用sync} }前面三個(gè)Accumulator主要是對(duì)RawEvent的各個(gè)值進(jìn)行保存
void CursorButtonAccumulator::process(const RawEvent* rawEvent) {if (rawEvent->type == EV_KEY) {switch (rawEvent->code) {case BTN_LEFT:mBtnLeft = rawEvent->value;break;case BTN_RIGHT:mBtnRight = rawEvent->value;break;case BTN_MIDDLE:mBtnMiddle = rawEvent->value;break;case BTN_BACK:mBtnBack = rawEvent->value;break;case BTN_SIDE:mBtnSide = rawEvent->value;break;case BTN_FORWARD:mBtnForward = rawEvent->value;break;case BTN_EXTRA:mBtnExtra = rawEvent->value;break;case BTN_TASK:mBtnTask = rawEvent->value;break;}} } void CursorMotionAccumulator::process(const RawEvent* rawEvent) {if (rawEvent->type == EV_REL) {switch (rawEvent->code) {case REL_X:mRelX = rawEvent->value;break;case REL_Y:mRelY = rawEvent->value;break;}} } void CursorScrollAccumulator::process(const RawEvent* rawEvent) {if (rawEvent->type == EV_REL) {switch (rawEvent->code) {case REL_WHEEL:mRelWheel = rawEvent->value;break;case REL_HWHEEL:mRelHWheel = rawEvent->value;break;}} }我們?cè)賮?lái)看下sync函數(shù),這里主要是用PointerController來(lái)設(shè)置光標(biāo)位置,最后再去notifyMotion發(fā)送MotionEvent事件。
void CursorInputMapper::sync(nsecs_t when) {int32_t lastButtonState = mButtonState;int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();mButtonState = currentButtonState;bool wasDown = isPointerDown(lastButtonState);bool down = isPointerDown(currentButtonState);bool downChanged;if (!wasDown && down) {mDownTime = when;//記錄DownTimedownChanged = true;} else if (wasDown && !down) {downChanged = true;} else {downChanged = false;}nsecs_t downTime = mDownTime;bool buttonsChanged = currentButtonState != lastButtonState;bool buttonsPressed = currentButtonState & ~lastButtonState;#ifdef SUPPORT_BROWSER_VIRTUAL_CURSORfloat deltaX = mCursorMotionAccumulator.getRelativeX();//這里保存的每一次移動(dòng)的步長(zhǎng)float deltaY = mCursorMotionAccumulator.getRelativeY();//上下左右 #elsefloat deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; #endifbool moved = deltaX != 0 || deltaY != 0;#ifdef SUPPORT_BROWSER_VIRTUAL_CURSOR// Remove orientation support as device does not support. // if (mParameters.orientationAware && mParameters.hasAssociatedDisplay // && (deltaX != 0.0f || deltaY != 0.0f)) { // rotateDelta(mOrientation, &deltaX, &deltaY); // } #else// Rotate delta according to orientation if neededif (mParameters.orientationAware && mParameters.hasAssociatedDisplay&& (deltaX != 0.0f || deltaY != 0.0f)) {rotateDelta(mOrientation, &deltaX, &deltaY);}#endif// Move the pointer.PointerProperties pointerProperties;pointerProperties.clear();pointerProperties.id = 0;pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;PointerCoords pointerCoords;pointerCoords.clear();float vscroll = mCursorScrollAccumulator.getRelativeVWheel();float hscroll = mCursorScrollAccumulator.getRelativeHWheel();bool scrolled = vscroll != 0 || hscroll != 0;mWheelYVelocityControl.move(when, NULL, &vscroll);mWheelXVelocityControl.move(when, &hscroll, NULL);mPointerVelocityControl.move(when, &deltaX, &deltaY);int32_t displayId;if (mPointerController != NULL) {if (moved || scrolled || buttonsChanged) {mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);if (moved) {mPointerController->move(deltaX, deltaY);//PointerController設(shè)置每一次移動(dòng)的一個(gè)相對(duì)坐標(biāo)}if (buttonsChanged) {mPointerController->setButtonState(currentButtonState);}mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);}float x, y;mPointerController->getPosition(&x, &y);//獲取現(xiàn)在光標(biāo)坐標(biāo)位置pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);displayId = ADISPLAY_ID_DEFAULT;} else {pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);displayId = ADISPLAY_ID_NONE;}pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);// Moving an external trackball or mouse should wake the device.// We don't do this for internal cursor devices to prevent them from waking up// the device in your pocket.// TODO: Use the input device configuration to control this behavior more finely.uint32_t policyFlags = 0;if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {policyFlags |= POLICY_FLAG_WAKE_DROPPED;}// Synthesize key down from buttons if needed.synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,policyFlags, lastButtonState, currentButtonState);// Send motion event.if (downChanged || moved || scrolled || buttonsChanged) {int32_t metaState = mContext->getGlobalMetaState();int32_t motionEventAction;if (downChanged) {motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;} else if (down || mPointerController == NULL) {motionEventAction = AMOTION_EVENT_ACTION_MOVE;} else {motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;}NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,motionEventAction, 0, metaState, currentButtonState, 0,displayId, 1, &pointerProperties, &pointerCoords,mXPrecision, mYPrecision, downTime);getListener()->notifyMotion(&args);//發(fā)送MotionEvent// Send hover move after UP to tell the application that the mouse is hovering now.if (motionEventAction == AMOTION_EVENT_ACTION_UP&& mPointerController != NULL) {NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags,AMOTION_EVENT_ACTION_HOVER_MOVE, 0,metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,displayId, 1, &pointerProperties, &pointerCoords,mXPrecision, mYPrecision, downTime);getListener()->notifyMotion(&hoverArgs);}// Send scroll events.if (scrolled) {pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,AMOTION_EVENT_ACTION_SCROLL, 0, metaState, currentButtonState,AMOTION_EVENT_EDGE_FLAG_NONE,displayId, 1, &pointerProperties, &pointerCoords,mXPrecision, mYPrecision, downTime);getListener()->notifyMotion(&scrollArgs);}}...... }我們來(lái)看下這個(gè)PointerController的move函數(shù)就是將之前的坐標(biāo)加上這個(gè)新的相對(duì)坐標(biāo)。
void PointerController::move(float deltaX, float deltaY) {if (deltaX == 0.0f && deltaY == 0.0f) {return;}AutoMutex _l(mLock);setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); }setPositionLocked函數(shù)原理很簡(jiǎn)單,就是算現(xiàn)在的值有沒(méi)有超過(guò)邊界。
void PointerController::setPositionLocked(float x, float y) {float minX, minY, maxX, maxY;if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {if (x <= minX) {mLocked.pointerX = minX;} else if (x >= maxX) {mLocked.pointerX = maxX;} else {mLocked.pointerX = x;}if (y <= minY) {mLocked.pointerY = minY;} else if (y >= maxY) {mLocked.pointerY = maxY;} else {mLocked.pointerY = y;}updatePointerLocked();} }updatePointerLocked函數(shù)會(huì)去繪制光標(biāo),已經(jīng)更新光標(biāo)在layer的位置等等。
void PointerController::updatePointerLocked() {mSpriteController->openTransaction();mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);if (mLocked.pointerAlpha > 0) {mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);mLocked.pointerSprite->setVisible(true);} else {mLocked.pointerSprite->setVisible(false);}if (mLocked.pointerIconChanged || mLocked.presentationChanged) {if (mLocked.presentation == PRESENTATION_POINTER) {mLocked.pointerSprite->setIcon(mLocked.pointerIcon);} else if(mLocked.presentation == PRESENTATION_STYLUS_HOVER) {mLocked.pointerSprite->setIcon(mLocked.hoverIcon.isValid()? mLocked.hoverIcon : mResources.stylusHover);} else { // PRESENTATION_SPOTmLocked.pointerSprite->setIcon(mResources.spotAnchor);}mLocked.pointerIconChanged = false;mLocked.presentationChanged = false;}mSpriteController->closeTransaction(); }我們?cè)賮?lái)看getBoundsLocked函數(shù)就是獲取邊界,這里原先android原生就是分辨率的長(zhǎng)和寬。也就是outMinY是0,outMaxY是mLocked.displayHeight - 1,這里我們需要適應(yīng)瀏覽器,上面和下面(下面是狀態(tài)欄,上面是導(dǎo)航欄吧)需要減去別的view的值,所以會(huì)根據(jù)density做適應(yīng)。
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,float* outMaxX, float* outMaxY) const {if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {return false;}char propBuf[PROPERTY_VALUE_MAX];property_get("ro.sf.lcd_density", propBuf, "160");int density = atoi(propBuf);*outMinX = 0;//*outMinY = 0;/* Restricting the cursor from floating on status bar, this* behavior is generic for all the devices that uses cursor like OTG mouse. */*outMinY = 1 + ((float)density/160)*24;switch (mLocked.displayOrientation) {case DISPLAY_ORIENTATION_90:case DISPLAY_ORIENTATION_270:*outMaxX = mLocked.displayHeight - 1;*outMaxY = mLocked.displayWidth - 1;break;default:*outMaxX = mLocked.displayWidth - 1;// Restricting mouse cursor from floating on the softkey menu in Browser and// HTMLViewer app.*outMaxY = mLocked.displayHeight - 1 - ((float)density/160)*32;break;}return true; }其實(shí)原因就是在普通按鍵處理KeyboardInputMapper::process的函數(shù)中重新封裝下發(fā)給CursorInputMapper處理,然后再去計(jì)算光標(biāo)坐標(biāo)等。
?
?
?
總結(jié)
以上是生活随笔為你收集整理的功能机用上下键实现MoveEvent的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 超简单的数论入门题
- 下一篇: Hexo博客中添加Live 2D模型