Android中ENTER鍵(確認鍵)點擊響應

背景:在機頂盒中通過遙控器操作,與傳統手機交互方式不同。手機點擊是發送兩個TouchEvent(Down和Up),機頂盒是發送KeyEvent。所產生的效果看似相同,其實是兩種不同的機制。



先看兩段代碼

1.目錄android.view.View 

調度按鍵事件

public boolean dispatchKeyEvent(KeyEvent event) {
		if (mInputEventConsistencyVerifier != null) {
			mInputEventConsistencyVerifier.onKeyEvent(event, 0);
		}

		// Give any attached key listener a first crack at the event.
		// noinspection SimplifiableIfStatement
		ListenerInfo li = mListenerInfo;
		if (li != null && li.mOnKeyListener != null
				&& (mViewFlags & ENABLED_MASK) == ENABLED
				&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
			return true;
		}

		if (event.dispatch(this,
				mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null,
				this)) {
			return true;
		}
		if (mInputEventConsistencyVerifier != null) {
			mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
		}
		return false;
	}

2.目錄android.view.View 

ENTER鍵鬆開

    public boolean onKeyUp(int keyCode, KeyEvent event) {
        boolean result = false;

        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_CENTER:
            case KeyEvent.KEYCODE_ENTER: {
                if ((mViewFlags & ENABLED_MASK) == DISABLED) {
                    return true;
                }
                if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) {
                    setPressed(false);

                    if (!mHasPerformedLongPress) {
                        // This is a tap, so remove the longpress check
                        removeLongPressCallback();

                        result = performClick();
                    }
                }
                break;
            }
        }
        return result;
    }

以上代碼是對於一個普通控件的ENTER鍵響應,當控件enabled && clickable時會執行 performClick()方法。若是正常執行,返回True,表示這個按鍵事件已經被消費了,若是沒有正常執行,返回false。


注意下,對於Button之類控件默認clickable,而TextView等是不能點擊的。對於所有View在執行過setOnClickListener(Listener)後,即可點擊。因爲在API中有這麼一條。

    public void setOnClickListener(OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }



以上是一個普通控件處理點擊事件的流程。現在問題來了,在手機應用中當我們點擊一個無用的控件,也可能產生點擊事件(實質是其父控件消費了這個點擊事件),但是使用遙控器操作時,似乎一個控件點不了就是點不了,它的父控件也不會處理,這是爲什麼?


再看下ViewGroup中的代碼

3.目錄android.view.ViewGroup

    public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 1);
        }

        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
            if (super.dispatchKeyEvent(event)) {
                return true;
            }
        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
            if (mFocused.dispatchKeyEvent(event)) {
                return true;
            }
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
        }
        return false;
    }
這段代碼中,看到,當焦點在ViewGroup上時正常執行。當焦點在ViewGroup子控件上時,讓子控件消費這個按鍵事件。
而觸摸的點擊事件,在子控件未消費TouchEvent時,返回False,其父控件再響應點擊事件,如此循環。在按鍵的事件中並沒有這樣的一個機制,因此按鍵事件和點擊雖然最終都是調用目標的performClick()但是,對於boolean型的返回值的處理,兩者並不一樣。

最終進行如下修改:
4.目錄android.view.ViewGroup
	public boolean dispatchKeyEvent(KeyEvent event) {
		if (mInputEventConsistencyVerifier != null) {
			mInputEventConsistencyVerifier.onKeyEvent(event, 1);
		}

		if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
			if (super.dispatchKeyEvent(event)) {
				return true;
			}
		} else if (mFocused != null
				&& (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS) == PFLAG_HAS_BOUNDS) {
			if (mFocused.dispatchKeyEvent(event)) {

				return true;
			}
			// add by suntianrui
			else {
				if (super.dispatchKeyEvent(event)) {
					return true;
				}

			}
			// end
		}

		if (mInputEventConsistencyVerifier != null) {
			mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
		}

		return false;
	}

這段代碼在原來基礎上,增加判斷,如果子控件未處理按鍵事件,則由父控件處理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章