关于Android 5.1 LatinIME的分析

因为公司产品是一款vr,是没有触摸屏的,所有要想用蓝牙手柄操控LatinIME就得进行修改

LatinIME最主要的就是一个继承InputMethodService的服务,类名就是LatinIME.java

而MainKeyboardView.java就是画出来的那块键盘了,主要的响应触摸,传递键值都绕不过它调用的PointerTracker.java

直接看响应触摸的吧

通过onTouchEvent找到processMotionEvent(me);然后在processMotionEvent(me);看到tracker.processMotionEvent(me, mKeyDetector);

@Override
    public boolean onTouchEvent(final MotionEvent me) {
        if (getKeyboard() == null) {
            return false;
        }
        if (mNonDistinctMultitouchHelper != null) {
            if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) {
                // Key repeating timer will be canceled if 2 or more keys are in action.
                mKeyTimerHandler.cancelKeyRepeatTimers();
            }
            // Non distinct multitouch screen support
            mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector);
            return true;
        }
        return processMotionEvent(me);
    }

    public boolean processMotionEvent(final MotionEvent me) {
        final int index = me.getActionIndex();
        final int id = me.getPointerId(index);
        final PointerTracker tracker = PointerTracker.getPointerTracker(id);
        // When a more keys panel is showing, we should ignore other fingers' single touch events
        // other than the finger that is showing the more keys panel.
        if (isShowingMoreKeysPanel() && !tracker.isShowingMoreKeysPanel()
                && PointerTracker.getActivePointerTrackerCount() == 1) {
            return true;
        }
        tracker.processMotionEvent(me, mKeyDetector);
        return true;
    }

触摸是在PointerTracker.java的processMotionEvent(me, mKeyDetector);里进行处理的

而PointerTracker.java里还有两个接口DrawingProxy和TimerProxy

public interface DrawingProxy {
        public void invalidateKey(Key key);
        public void showKeyPreview(Key key);
        public void dismissKeyPreview(Key key);
        public void showSlidingKeyInputPreview(PointerTracker tracker);
        public void dismissSlidingKeyInputPreview();
        public void showGestureTrail(PointerTracker tracker, boolean showsFloatingPreviewText);
    }

    public interface TimerProxy {
        public void startTypingStateTimer(Key typedKey);
        public boolean isTypingState();
        public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay);
        public void startLongPressTimerOf(PointerTracker tracker, int delay);
        public void cancelLongPressTimerOf(PointerTracker tracker);
        public void cancelLongPressShiftKeyTimers();
        public void cancelKeyTimersOf(PointerTracker tracker);
        public void startDoubleTapShiftKeyTimer();
        public void cancelDoubleTapShiftKeyTimer();
        public boolean isInDoubleTapShiftKeyTimeout();
        public void startUpdateBatchInputTimer(PointerTracker tracker);
        public void cancelUpdateBatchInputTimer(PointerTracker tracker);
        public void cancelAllUpdateBatchInputTimers();

        public static class Adapter implements TimerProxy {
            @Override
            public void startTypingStateTimer(Key typedKey) {}
            @Override
            public boolean isTypingState() { return false; }
            @Override
            public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay) {}
            @Override
            public void startLongPressTimerOf(PointerTracker tracker, int delay) {}
            @Override
            public void cancelLongPressTimerOf(PointerTracker tracker) {}
            @Override
            public void cancelLongPressShiftKeyTimers() {}
            @Override
            public void cancelKeyTimersOf(PointerTracker tracker) {}
            @Override
            public void startDoubleTapShiftKeyTimer() {}
            @Override
            public void cancelDoubleTapShiftKeyTimer() {}
            @Override
            public boolean isInDoubleTapShiftKeyTimeout() { return false; }
            @Override
            public void startUpdateBatchInputTimer(PointerTracker tracker) {}
            @Override
            public void cancelUpdateBatchInputTimer(PointerTracker tracker) {}
            @Override
            public void cancelAllUpdateBatchInputTimers() {}
        }
    }

TimerProxy是按下去时计时的,不用管,而DrawingProxy就是画的接口了

包括按下去时字母背景变黑和弹出pop,抬起时恢复等等

接着看processMotionEvent

public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) {
        final int action = me.getActionMasked();
        final long eventTime = me.getEventTime();
        if (action == MotionEvent.ACTION_MOVE) {
            // When this pointer is the only active pointer and is showing a more keys panel,
            // we should ignore other pointers' motion event.
            final boolean shouldIgnoreOtherPointers =
                    isShowingMoreKeysPanel() && getActivePointerTrackerCount() == 1;
            final int pointerCount = me.getPointerCount();
            for (int index = 0; index < pointerCount; index++) {
                final int id = me.getPointerId(index);
                if (shouldIgnoreOtherPointers && id != mPointerId) {
                    continue;
                }
                final int x = (int)me.getX(index);
                final int y = (int)me.getY(index);
                final PointerTracker tracker = getPointerTracker(id);
                tracker.onMoveEvent(x, y, eventTime, me);
            }
            return;
        }
        final int index = me.getActionIndex();
        final int x = (int)me.getX(index);
        final int y = (int)me.getY(index);
        switch (action) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_POINTER_DOWN:
            onDownEvent(x, y, eventTime, keyDetector);
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_POINTER_UP:
            onUpEvent(x, y, eventTime);
            break;
        case MotionEvent.ACTION_CANCEL:
            onCancelEvent(x, y, eventTime);
            break;
        }
    }

响应触摸主要是onDownEvent(x, y, eventTime, keyDetector);和onUpEvent(x, y, eventTime);两个方法

private void onDownEvent(final int x, final int y, final long eventTime,
            final KeyDetector keyDetector) {
        setKeyDetectorInner(keyDetector);
        if (DEBUG_EVENT) {
            printTouchEvent("onDownEvent:", x, y, eventTime);
        }
        // Naive up-to-down noise filter.
        final long deltaT = eventTime - mUpTime;
        if (deltaT < sParams.mTouchNoiseThresholdTime) {
            final int distance = getDistance(x, y, mLastX, mLastY);
            if (distance < sParams.mTouchNoiseThresholdDistance) {
                if (DEBUG_MODE)
                    Log.w(TAG, String.format("[%d] onDownEvent:"
                            + " ignore potential noise: time=%d distance=%d",
                            mPointerId, deltaT, distance));
                cancelTrackingForAction();
                return;
            }
        }

        final Key key = getKeyOn(x, y);
        mBogusMoveEventDetector.onActualDownEvent(x, y);
        if (key != null && key.isModifier()) {
            // Before processing a down event of modifier key, all pointers already being
            // tracked should be released.
            sPointerTrackerQueue.releaseAllPointers(eventTime);
        }
        sPointerTrackerQueue.add(this);
        onDownEventInternal(x, y, eventTime);
        if (!sGestureEnabler.shouldHandleGesture()) {
            return;
        }
        // A gesture should start only from a non-modifier key. Note that the gesture detection is
        // disabled when the key is repeating.
        mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
                && key != null && !key.isModifier();
        if (mIsDetectingGesture) {
            mBatchInputArbiter.addDownEventPoint(x, y, eventTime,
                    sTypingTimeRecorder.getLastLetterTypingTime(), getActivePointerTrackerCount());
            mGestureStrokeDrawingPoints.onDownEvent(
                    x, y, mBatchInputArbiter.getElapsedTimeSinceFirstDown(eventTime));
        }
    }

    /* package */ boolean isShowingMoreKeysPanel() {
        return (mMoreKeysPanel != null);
    }

private void onUpEvent(final int x, final int y, final long eventTime) {
        if (DEBUG_EVENT) {
            printTouchEvent("onUpEvent  :", x, y, eventTime);
        }

        sTimerProxy.cancelUpdateBatchInputTimer(this);
        if (!sInGesture) {
            if (mCurrentKey != null && mCurrentKey.isModifier()) {
                // Before processing an up event of modifier key, all pointers already being
                // tracked should be released.
                sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
            } else {
                sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
            }
        }
        onUpEventInternal(x, y, eventTime);
        sPointerTrackerQueue.remove(this);
    }

onDownEvent中的onDownEventInternal(final int x, final int y, final long eventTime)和onUpEvent中的onUpEventInternal(final int x, final int y, final long eventTime)方法对触摸进行了处理

private void onDownEventInternal(final int x, final int y, final long eventTime) {
        Key key = onDownKey(x, y, eventTime);
        // Key selection by dragging finger is allowed when 1) key selection by dragging finger is
        // enabled by configuration, 2) this pointer starts dragging from modifier key, or 3) this
        // pointer's KeyDetector always allows key selection by dragging finger, such as
        // {@link MoreKeysKeyboard}.
        mIsAllowedDraggingFinger = sParams.mKeySelectionByDraggingFinger
                || (key != null && key.isModifier())
                || mKeyDetector.alwaysAllowsKeySelectionByDraggingFinger();
        mKeyboardLayoutHasBeenChanged = false;
        mIsTrackingForActionDisabled = false;
        resetKeySelectionByDraggingFinger();
        if (key != null) {
            // This onPress call may have changed keyboard layout. Those cases are detected at
            // {@link #setKeyboard}. In those cases, we should update key according to the new
            // keyboard layout.
            if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
                key = onDownKey(x, y, eventTime);
            }

            startRepeatKey(key);
            startLongPressTimer(key);
            setPressedKeyGraphics(key, eventTime);
        }
    }

private void onUpEventInternal(final int x, final int y, final long eventTime) {
        sTimerProxy.cancelKeyTimersOf(this);
        final boolean isInDraggingFinger = mIsInDraggingFinger;
        final boolean isInSlidingKeyInput = mIsInSlidingKeyInput;
        resetKeySelectionByDraggingFinger();
        mIsDetectingGesture = false;
        final Key currentKey = mCurrentKey;
        mCurrentKey = null;
        final int currentRepeatingKeyCode = mCurrentRepeatingKeyCode;
        mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
        // Release the last pressed key.
        setReleasedKeyGraphics(currentKey);

        if (isShowingMoreKeysPanel()) {
            if (!mIsTrackingForActionDisabled) {
                final int translatedX = mMoreKeysPanel.translateX(x);
                final int translatedY = mMoreKeysPanel.translateY(y);
                mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
            }
            dismissMoreKeysPanel();
            return;
        }

        if (sInGesture) {
            if (currentKey != null) {
                callListenerOnRelease(currentKey, currentKey.getCode(), true /* withSliding */);
            }
            if (mBatchInputArbiter.mayEndBatchInput(
                    eventTime, getActivePointerTrackerCount(), this)) {
                sInGesture = false;
            }
            showGestureTrail();
            return;
        }

        if (mIsTrackingForActionDisabled) {
            return;
        }
        if (currentKey != null && currentKey.isRepeatable()
                && (currentKey.getCode() == currentRepeatingKeyCode) && !isInDraggingFinger) {
            return;
        }
        detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
        if (isInSlidingKeyInput) {
            callListenerOnFinishSlidingInput();
        }
    }

而这两个方法中分别有setPressedKeyGraphics(key, eventTime);和setReleasedKeyGraphics(currentKey);两个方法通过DrawingProxy接口传到MainKeyboardView.java,

然后由MainKeyboardView.java画出触摸按下去和释放在键盘上的显示效果

onDownEventInternal中的

if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
                key = onDownKey(x, y, eventTime);
            }

是按下大写键后,整个键盘改变大小写状态的

而onUpEventInternal中的detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);方法调用callListenerOnCodeInput(key, code, x, y, eventTime, false /* isKeyRepeat */);

通过KeyboardActionListener接口将key传到LatinIME.java中的onCodeInput方法中进行处理







  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值