现在的位置: 首页 > 移动开发 > 正文

Android中ENTER键(确认键)点击响应

2019年11月14日 移动开发 ⁄ 共 3002字 ⁄ 字号 评论关闭

背景:在机顶盒中通过遥控器操作,与传统手机交互方式不同。手机点击是发送两个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;
	}

这段代码在原来基础上,增加判断,如果子控件未处理按键事件,则由父控件处理。

抱歉!评论已关闭.