搜档网
当前位置:搜档网 › Android按键事件处理流程 -- KeyEvent

Android按键事件处理流程 -- KeyEvent

Android按键事件处理流程 -- KeyEvent
Android按键事件处理流程 -- KeyEvent

Android按键事件处理流程 -- KeyEvent

2014/6/24 13:18:58 xiaoweiz 程序员俱乐部我要评论(0)

?摘要:刚接触Android开发的时候,对touch、key事件的处理总是一知半解,一会是Activity 里的方法,一会是各种View中的,自己始终不清楚到底哪个在先哪个在后,总之对整个处理流程没能很好的把握。每次写这部分代码的时候都有些心虚,因为我不是很清楚什么时候、以什么样的顺序被调用,大都是打下log看看,没问题就算ok了。但随着时间流逝,这种感觉一直折磨着我。期间也在网上搜索了相关资料,但总感觉不是那么令人满意。自打开始研究Android源码起

?标签:事件android KEY流程事件处理

?

刚接触Android开发的时候,对touch、key事件的处理总是一知半解,一会是Activity里的方法,一会是各种View

中的,自己始终不清楚到底哪个在先哪个在后,总之对整个处理流程没能很好的把握。

每次写这部分代码的时候都有些心虚,

因为我不是很清楚什么时候、以什么样的顺序被调用,大都是打下log看看,没问题就算ok了。但随着时间流逝,这种感觉一直

折磨着我。期间也在网上搜索了相关资料,但总感觉不是那么令人满意。自打开始研究Android源码起,这部分内容的分析早就

被列在我的TODO list上了。因为弄懂这部分处理逻辑对明明白白地写android程序实在是太重要了,所以今天我就带领大家看看

这部分的处理逻辑。touch事件的处理我将放在另一篇博客中介绍(相比KeyEvent,大体都一样,只是稍微复杂些)。

为了突出本文的重点,我们直接从事件被派发到View层次结构的根节点DecorView 开始分析,这里我们先来看看DecorView#

dispatchKeyEvent方法,代码如下:

@Override

publicboolean dispatchKeyEvent(KeyEvent event) {

finalint keyCode = event.getKeyCode();

finalint action = event.getAction();

finalboolean isDown = action == KeyEvent.ACTION_DOWN;

/// 1. 第一次down事件的时候,处理panel的快捷键

if (isDown && (event.getRepeatCount() == 0)) {

// First handle chording of panel key: if a panel key is held

// but not released, try to execute a shortcut in it.

if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) { boolean handled = dispatchKeyShortcutEvent(event);

if (handled) {

returntrue;

}

}

// If a panel is open, perform a shortcut on it without the

// chorded panel key

if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {

if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) { returntrue;

}

}

}

/// 2. 这里是我们本文的重点,当window没destroy且其Callback非空的话,交给其Callback处理

if (!isDestroyed()) { // Activity、Dialog都是Callback接口的实现

final Callback cb = getCallback(); // mFeatureId < 0 表示是application 的DecorView,比如Activity、Dialog

finalboolean handled = cb != null&& mFeatureId < 0 ?

cb.dispatchKeyEvent(event) // 派发给callback的方法

: super.dispatchKeyEvent(event); // 否则直接派发到ViewGroup#dispatchKeyEvent(View层次结构)

if (handled) {

returntrue; // 如果被上面的步骤处理了则直接返回true,不再往下传递

}

}

/// 3. 这是key事件的最后一步,如果到这一步还没处理掉,则派发到PhoneWindow对应的onKeyDown, onKeyUp方法

return isDown ? PhoneWindow.this.onKeyDown(mFeatureId,

event.getKeyCode(), event)

: PhoneWindow.this.onKeyUp(mFeatureId,

event.getKeyCode(), event);

}

接下来我们按照这个派发顺序依次来看看相关方法的实现,这里先看看

Activity(Callback)的dispatchKeyEvent实现:

/**

* Called to process key events. You can override this to intercept all

* key events before they are dispatched to the window. Be sure to call

* this implementation for key events that should be handled normally. *

* @param event The key event.

*

* @return boolean Return true if this event was consumed.

*/

@Override

publicboolean dispatchKeyEvent(KeyEvent event) {

/// 2.1. 回调接口,实际开发中用处不大,你感兴趣可以参看其方法doc onUserInteraction();

Window win = getWindow();

/// 2.2. 从这里事件的处理交给了与之相关的window对象,实质是派发到了view层次结构

if (win.superDispatchKeyEvent(event)) {

returntrue;

}

View decor = mDecor;

if (decor == null) decor = win.getDecorView();

/// 2.3. 到这里如果view层次结构没处理则交给KeyEvent本身的dispatch

方法,Activity的各种回调方法会被触发

return event.dispatch(this, decor != null

? decor.getKeyDispatcherState() : null, this);

}

紧接着我们看看,Window#superDispatchKeyEvent方法,相关代码如下:

/**

* Used by custom windows, such as Dialog, to pass the key press event * further down the view hierarchy. Application developers should * not need to implement or call this.

*

*/

publicabstractboolean superDispatchKeyEvent(KeyEvent event);

@Override

publicboolean superDispatchKeyEvent(KeyEvent event) {

return mDecor.superDispatchKeyEvent(event);

}

publicboolean superDispatchKeyEvent(KeyEvent event) {

/// 2.2.1. 进入view层次结构了,即调用ViewGroup的对应实现了。。。

if (super.dispatchKeyEvent(event)) {

returntrue; // 如果被view层次结构处理了则直接返回true。

}

// Not handled by the view hierarchy, does the action bar want it

// to cancel out of something special?

/// 2.2.2. ActionBar对BACK key的特殊处理

if (event.getKeyCode() == KeyEvent.KEY CODE_BACK) {

finalint action = event.getAction();

// Back cancels action modes first.

if (mActionMode != null) {

if (action == KeyEvent.ACTION_UP) {

mActionMode.finish();

}

returntrue;

}

// Next collapse any expanded action views.

if (mActionBar != null&& mActionBar.has ExpandedActionView()) {

if (action == KeyEvent.ACTION_UP) {

mActionBar.collapseActionView();

}

returntrue;

}

}

/// 2.2.3. 最后返回false表示没处理掉,会接着2.3.步骤处理returnfalse;

}

然后我们接着看看2.2.1.包括的小步骤,即ViewGroup#dispatchKeyEvent的实现,代码如下:

@Override

publicboolean dispatchKeyEvent(KeyEvent event) {

/// 2.2.1.1. keyevent一致性检测用的,可忽略。。。

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onKeyEvent(event, 1);

}

if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))

== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {

/// 2.2.1.2. 如果此ViewGroup是focused或者具体的大小被设置了,则交给他处理,即调用View的实现

if (super.dispatchKeyEvent(event)) {

returntrue;

}

} elseif (mFocused != null&& (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)

== PFLAG_HAS_BOUNDS) {

/// 2.2.1.3. 否则,如果此ViewGroup中有focused的child,且child有具体的大小,则交给mFocused处理

if (mFocused.dispatchKeyEvent(event)) { // 注意这里可能是个递归调用returntrue; // 我们可以看到并不是每个child都能响应key事件,前提必须是focused child才有机会响应

}

}

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);

}

/// 2.2.1.4. 最后都没被处理返回false,2.2.2.步骤会接着执行。。。returnfalse;

}

这里我们可以看出对KeyEvent来说在View层次结构中,如果ViewGroup条件满足则会优先处理事件而不是先派发给其孩子view,

这一点和touch事件有所不同。这里我们看看View的dispatchKeyEvent实现:

/**

* Dispatch a key event to the next view on the focus path. This path runs

* from the top of the view tree down to the currently focused view. If this

* view has focus, it will dispatch to itself. Otherwise it will dispatch

* the next node down the focus path. This method also fires any key * listeners.

*

* @param event The key event to be dispatched.

* @return True if the event was handled, false otherwise.

*/

publicboolean 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;

/// 2.2.1.2(3).1. 调用onKeyListener,如果它非空且view是ENABLED状态,监听器优先触发

if(li != null&& li.mOnKeyListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED

&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) { returntrue;

}

/// 2.2.1.2(3).2. 调用KeyEvent.dispatch方法,并将view对象本身作为参数传递进去,view的各种callback方法在这里被触发

if (event.dispatch(this, mAttachInfo != null

? mAttachInfo.mKeyDispatchState : null, this)) { returntrue;

}

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);

}

/// 2.2.1.2(3).3. 还没处理掉返回false,接着2.2.1.4.执行

returnfalse;

}

不管是这里的2.2.1.2(3).2.步骤还是前面Activity里的2.3.步骤,都调到了KeyEvent.dispatch方法,不过在看其代码之前我们

先来看看这里用到的mAttachInfo.mKeyDispatchState对象是咋来的,代码如下:

// 这句代码位于View.AttachInfo类里final KeyEvent.DispatcherState mKeyDispatchState

= new KeyEvent.DispatcherState();

/**

* Return the global {@link KeyEvent.DispatcherState

KeyEvent.DispatcherState}

* for this view's window. Returns null if the view is not currently attached

* to the window. Normally you will not need to use this directly, but

* just use the standard high-level event callbacks like

* {@link #onKeyDown(int, KeyEvent)}.

*/

public KeyEvent.DispatcherState getKeyDispatcherState() {

return mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null;

}

// KeyEvent.DispatcherState类

/**

* Use with {@link KeyEvent#dispatch(Callback, DispatcherState, Object)}

* for more advanced key dispatching, such as long presses.

*/

publicstatic class DispatcherState {

int mDownKeyCode;

Object mDownTarget;

SparseIntArray mActiveLongPresses = new SparseIntArray();

/**

* Reset back to initial state.

*/

publicvoid reset() { // 清空内部状态

if (DEBUG) Log.v(TAG, "Reset: " + this);

mDownKeyCode = 0;

mDownTarget = null;

mActiveLongPresses.clear();

}

/**

* Stop any tracking associated with this target.

*/

publicvoid reset(Object target) { // 清空target对应的内部状态

if (mDownTarget == target) { // 只有相同时才清空,否则啥也不做

if (DEBUG) Log.v(TAG, "Reset in " + target + ": " + this);

mDownKeyCode = 0;

mDownTarget = null;

}

}

/**

* Start tracking the key code associated with the given event. This

* can only be called on a key down. It will allow you to see any

* long press associated with the key, and will result in

* {@link KeyEvent#isTracking} return true on the long press and up

* events.

*

*

This is only needed if you are directly dispatching events, rather

* than handling them in {@link Callback#onKeyDown}.

*/

publicvoid startTracking(KeyEvent event, Object target) {

if (event.getAction() != ACTION_DOWN) { // 状态检测

thrownew IllegalArgumentException(

"Can only start tracking on a down event");

}

if (DEBUG) Log.v(TAG, "Start trackingt in " + target + ": " + this); mDownKeyCode = event.getKeyCode(); // 赋值,表示正在track 某个keycode

mDownTarget = target;

}

/**

* Return true if the key event is for a key code that is currently * being tracked by the dispatcher.

*/

publicboolean isTracking(KeyEvent event) {

return mDownKeyCode == event.getKeyCode();

}

/**

* Keep track of the given event's key code as having performed an

* action with a long press, so no action should occur on the up. *

This is only needed if you are directly dispatching events, rather

* than handling them in {@link Callback#onKeyLongPress}.

*/

publicvoid performedLongPress(KeyEvent event) {// 用来记录发生了生理长按事件

mActiveLongPresses.put(event.getKeyCode(), 1);

}

/**

* Handle key up event to stop tracking. This resets the dispatcher state,

* and updates the key event state based on it.

*

This is only needed if you are directly dispatching events, rather

* than handling them in {@link Callback#onKeyUp}.

*/

publicvoid handleUpEvent(KeyEvent event) {

finalint keyCode = event.getKeyCode();

if (DEBUG) Log.v(TAG, "Handle key up " + event + ": " + this);

int index = mActiveLongPresses.indexOfKey(keyCode);

if (index >= 0) { // 如果发生过生理长按则设置event.mFlags为CACELED,这样在接下来的receiver.onKeyUp中有些处理就不会发生了

if(DEBUG) Log.v(TAG, " Index: " + index); // 因为事件被标记为CANCELED 了

event.mFlags |= FLAG_CANCELED |

FLAG_CANCELED_LONG_PRESS;

mActiveLongPresses.removeAt(index);

}

if (mDownKeyCode == keyCode) {

if (DEBUG) Log.v(TAG, " Tracking!");

event.mFlags |= FLAG_TRACKING; // 设置event正确的mFlags,接下来的receiver.onKeyUp可能会检测此状态

mDownKeyCode = 0; // reset,表示此keycode的tracking 到此结束了

mDownTarget = null;

}

}

}

大概了解了KeyEvent.DispatcherState类,我们就可以来看看KeyEvent.dispatch方法了,代码如下:

/**

* Deliver this key event to a {@link Callback} interface. If this is

* an ACTION_MULTIPLE event and it is not handled, then an attempt will

* be made to deliver a single normal event.

*

* @param receiver The Callback that will be given the event.

* @param state State information retained across events.

* @param target The target of the dispatch, for use in tracking. *

* @return The return value from the Callback method that was called. */

publicfinalboolean dispatch(Callback receiver, DispatcherState state, Object target) {

switch (mAction) {

case ACTION_DOWN: { // DOWN事件

mFlags &= ~FLAG_START_TRACKING; //先清掉START_TRACKING 标记

if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state

+ ": " + this);

boolean res = receiver.onKeyDown(mKeyCode, this); // 回调Callback接口的onKeyDown方法,View和Activity都是此接口的实现者

if (state != null) { // 一般都成立

if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) { if (DEBUG) Log.v(TAG, " Start tracking!"); // receiver.onKeyDown返回true了且不是repeated

state.startTracking(this, target); // 并且也没有开始tracking,则开始tracking当前的KeyEvent和target

} elseif (isLongPress() && state.isTracking(this)) { // 处理生理长按

try { // 检测到生理长按则调用receiver.onKeyLongPress方法

if (receiver.onKeyLongPress(mKeyCode, this)) {

if (DEBUG) Log.v(TAG, " Clear from long press!");

state.performedLongPress(this); // 记录此event已经有生理long press发生了。。。

res = true; // 设置为处理了

}

} catch (AbstractMethodError e) {

}

}

}

return res; // 返回down事件处理的结果

}

case ACTION_UP: // UP事件

if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state

+ ": " + this);

if (state != null) {

state.handleUpEvent(this); // reset state的内部状态,也改变了KeyEvent的某些状态

}

return receiver.onKeyUp(mKeyCode, this); // 最后调用receiver.onKeyUp

方法

case ACTION_MULTIPLE: // 这里可以忽略掉

finalint count = mRepeatCount;

finalint code = mKeyCode;

if (receiver.onKeyMultiple(code, count, this)) {

returntrue;

}

if (code != KeyEvent.KEYCODE_UNKNOWN) {

mAction = ACTION_DOWN;

mRepeatCount = 0;

boolean handled = receiver.onKeyDown(code, this);

if (handled) {

mAction = ACTION_UP;

receiver.onKeyUp(code, this);

}

mAction = ACTION_MULTIPLE;

mRepeatCount = count;

return handled;

}

returnfalse;

}

returnfalse;

}

看完了KeyEvent的具体实现,我们接着看看receiver(Callback接口)的onKeyDown、onKeyUp实现,先来看View相关的,代码如下:

/**

* Default implementation of {@link KeyEvent.Callback#onKeyDown(int, KeyEvent)

* KeyEvent.Callback.onKeyDown()}: perform press of the view

* when {@link KeyEvent#KEYCODE_DPAD_CENTER} or {@link

KeyEvent#KEYCODE_ENTER}

* is released, if the view is enabled and clickable.

*

this listener,

* although some may elect to do so in some situations. Do not rely on this to

* catch software key presses.

*

* @param keyCode A key code that represents the button pressed, from * {@link android.view.KeyEvent}.

* @param event The KeyEvent object that defines the button action. */publicboolean onKeyDown(int keyCode, KeyEvent event) {

boolean result = false;

if (KeyEvent.isConfirmKey(keyCode)) { // 只处理KEYCODE_DPAD_CENTER、KEYCODE_ENTER这2个按键

if ((mViewFlags & ENABLED_MASK) == DISABLED) {

returntrue; // 针对disabled View直接返回true表示处理过了 } // Long clickable items don't necessarily have to be clickable

if (((mViewFlags & CLICKABLE) == CLICKABLE ||

(mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) && (event.getRepeatCount() == 0)) { // clickable或者long_clickable且是第一次down事件

setPressed(true); // 标记pressed,你可能设置了View不同的background,这时候就会有所体现(比如高亮效果)

checkForLongClick(0); // 启动View的long click检测returntrue; // 到达这一步就表示KeyEvent被处理掉了 }

}

return result;

}

/**

* Default implementation of {@link KeyEvent.Callback#onKeyUp(int, KeyEvent)

* KeyEvent.Callback.onKeyUp()}: perform clicking of the view

* when {@link KeyEvent#KEYCODE_DPAD_CENTER} or

* {@link KeyEvent#KEYCODE_ENTER} is released.

this listener,

* although some may elect to do so in some situations. Do not rely on this to

* catch software key presses.

*

* @param keyCode A key code that represents the button pressed, from * {@link android.view.KeyEvent}.

* @param event The KeyEvent object that defines the button action. */

publicboolean onKeyUp(int keyCode, KeyEvent event) {

if (KeyEvent.isConfirmKey(keyCode)) { // 同onKeyDown,默认也只处理confirm key

if ((mViewFlags & ENABLED_MASK) == DISABLED) {

returntrue; // 同样的逻辑,如果是DISABLED view,直接返回true表示处理过了

}

if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) {

setPressed(false); // 重置pressed状态

if (!mHasPerformedLongPress) { // 长按没发生的话,

// This is a tap, so remove the longpress check removeLongPressCallback(); // 当up事件发生的时候,移除这些已经没用的callback

return performClick(); // 调用单击onClick监听器

}

}

}

returnfalse; // 其他所有的Key默认不处理

}

/**

* Sets the pressed state for this view.

*

* @see #isClickable()

* @see #setClickable(boolean)

* @param pressed Pass true to set the View's internal state to "pressed", or false to reverts

* the View's internal state from a previously set "pressed" state.

*/

publicvoid setPressed(boolean pressed) {

finalboolean needsRefresh = pressed != ((mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED);

if (pressed) {

mPrivateFlags |= PFLAG_PRESSED;

} else {

mPrivateFlags &= ~PFLAG_PRESSED;

}

if (needsRefresh) {

refreshDrawableState(); // 这行代码会刷新View的显示状态 }

dispatchSetPressed(pressed);

}

privatevoid checkForLongClick(int delayOffset) {

if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { // 必须得是LONG_CLICKABLE的View

mHasPerformedLongPress = false; // 设置初始值

if (mPendingCheckForLongPress == null) { // 只非空的时候才new一个

mPendingCheckForLongPress = new CheckForLongPress(); }

mPendingCheckForLongPress.rememberWindowAttachCount();

postDelayed(mPendingCheckForLongPress, // post一个Runnable,注意延迟是个差值,而不是delayOffset

ViewConfiguration.getLongPressTimeout() - delayOffset);

}

class CheckForLongPress implements Runnable {

privateint mOriginalWindowAttachCount;

publicvoid run() {

if (isPressed() && (mParent != null) // 当时间到了,此Runnable没被移除掉的话,并且这些条件都满足的时候,

&& mOriginalWindowAttachCount == mWindowAttachCount) {

if (performLongClick()) { // 客户端定义的onLongClickListener监听器被触发

mHasPerformedLongPress = true; // 只有当被上面的方法处理掉了,才表示LongPress发生过了

}

}

}

publicvoid rememberWindowAttachCount() {

mOriginalWindowAttachCount = mWindowAttachCount;

}

}

/**

* Call this view's OnLongClickListener, if it is defined. Invokes the context menu if the

* OnLongClickListener did not consume the event.

*

* @return True if one of the above receivers consumed the event, false otherwise.

*/

publicboolean performLongClick() {

sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);

boolean handled = false;

ListenerInfo li = mListenerInfo;

if (li != null&& li.mOnLongClickListener != null) { // 优先触发监听器 handled = li.mOnLongClickListener.onLongClick(View.this); }

if (!handled) { // 如果还没处理,显示ContextMenu如果定义了的话

handled = showContextMenu();

}

if (handled) {

performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);

}

return handled; // 返回处理结果

}

接下来,看看Activity对应的onKeyDown,onKeyUp方法:

/**

* Called when a key was pressed down and not handled by any of the views

* inside of the activity. So, for example, key presses while the cursor

* is inside a TextView will not trigger the event (unless it is a navigation

* to another object) because TextView handles its own key presses. *

*

If the focused view didn't want this event, this method is called.

*

*

The default implementation takes care of {@link

KeyEvent#KEYCODE_BACK}

* by calling {@link #onBackPressed()}, though the behavior varies based

* on the application compatibility mode: for

* {@link android.os.Build.VERSION_CODES#ECLAIR} or later applications,

* it will set up the dispatch to call {@link #onKeyUp} where the action

* will be performed; for earlier applications, it will perform the

* action immediately in on-down, as those versions of the platform * behaved.

*

*

Other additional default key handling may be performed

* if configured with {@link #setDefaultKeyMode}.

*

* @return Return true to prevent this event from being propagated

* further, or false to indicate that you have not handled

* this event and it should continue to be propagated.

* @see #onKeyUp

* @see android.view.KeyEvent

*/

publicboolean onKeyDown(int keyCode, KeyEvent event) {

if (keyCode == KeyEvent.KEYCODE_BACK) {

if (getApplicationInfo().targetSdkVersion

>= Build.VERSION_CODES.ECLAIR) {

event.startTracking();

} else {

onBackPressed();

}

returntrue;

}

if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {

returnfalse;

} elseif (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {

if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,

keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) { returntrue;

}

returnfalse;

} else {

// Common code for DEFAULT_KEYS_DIALER & DEFAULT_KEYS_SEARCH_*

boolean clearSpannable = false;

boolean handled;

if ((event.getRepeatCount() != 0) || event.isSystem()) {

clearSpannable = true;

handled = false;

} else {

handled = TextKeyListener.getInstance().onKeyDown( null, mDefaultKeySsb, keyCode, event);

if (handled && mDefaultKeySsb.length() > 0) {

// something useable has been typed - dispatch it now.

final String str = mDefaultKeySsb.toString();

clearSpannable = true;

switch (mDefaultKeyMode) {

case DEFAULT_KEYS_DIALER:

Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + str));

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(intent);

break;

case DEFAULT_KEYS_SEARCH_LOCAL:

startSearch(str, false, null, false); break;

case DEFAULT_KEYS_SEARCH_GLOBAL:

startSearch(str, false, null, true); break;

}

}

}

if (clearSpannable) {

mDefaultKeySsb.clear();

mDefaultKeySsb.clearSpans();

Selection.setSelection(mDefaultKeySsb,0);

安卓按钮单击事件

第一种:匿名内部类作为事件监听器类 大部分时候,事件处理器都没有什么利用价值(可利用代码通常都被抽象成了业务逻辑方法),因此大部分事件监听器只是临时使用一次,所以使用匿名内部类形式的事件监听器更合适,实际上,这种形式是目前是最广泛的事件监听器形式。上面的程序代码就是匿名内部类来创建事件监听器的!!! 对于使用匿名内部类作为监听器的形式来说,唯一的缺点就是匿名内部类的语法有点不易掌握,如果读者java基础扎实,匿名内部类的语法掌握较好,通常建议使用匿名内部类作为监听器。 第二种:内部类作为监听器 将事件监听器类定义成当前类的内部类。1、使用内部类可以在当前类中复用监听器类,因为监听器类是外部类的内部类,2、所以可以自由访问外部类的所有界面组件。这也是内部类的两个优势。上面代码就是内部类的形式!! 第三种:Activity本身作为事件监听器

这种形式使用activity本身作为监听器类,可以直接在activity类中定义事件处理器方法,这种形式非常简洁。但这种做法有两个缺点:(1)这种形式可能造成程序结构混乱。Activity 的主要职责应该是完成界面初始化;但此时还需包含事件处理器方法,从而引起混乱。(2)如果activity界面类需要实现监听器接口,让人感觉比较怪异。 上面的程序让Activity类实现了OnClickListener事件监听接口,从而可以在该Activity类中直接定义事件处理器方法:onClick(view v),当为某个组件添加该事件监听器对象时,直接使用this作为事件监听器对象即可。 第四种:外部类作为监听器 ButtonTest类 当用户单击button按钮时,程序将会触发MyButtonListener监听器 外部MyButtonListener类

AndroidUI基本控件与事件处理

《Android基础应用》 AndroidUI基本控件与事件处理 ?本章任务 ?使用Android开发本息计算器程序 ?使用Android开发华氏-摄氏温度转换器 ?本章目标 ?熟悉掌握本章基本控件的使用 ?熟练掌握Android常用事件 1.Android基本控件 Android应用开发的一项内容就是用户界面的开发,Android提供了大量功能丰富的UI组件,大部分放在android.widget包及其子包android.view包及其子包 在Android当中View类是最基本的一个UI类,基本上所有的高级UI组件都是继承View类而实现的。如Button(按钮),list(列表),EditText(编辑框),RadioButton(多选按钮),Checkbox(选择框)等都是View类 在Android中,我们可以在Xml文件中使用UI组件也可以在java文件中创建UI组件官方建议采用xml方式,这样的话能够实现界面和代码分离 1.1TextView和EditText TextView是一种用于显示字符串的控件 EditText则是用来输入和编辑字符串的控件,EditText是一个具有编辑功能的TextView

TextView和EditText基本属性 ●android:id设置ID,通过编码可以找到这个组件 ●android:layout_width设置在屏幕上的宽度 ●android:layout_height设置在屏幕上的高度 fill_parent强制性地使构件扩展,以填充布局单元内尽可能多的空间 wrap_content强制性地使视图扩展以显示全部内容 ●android:text设置显示的文本信息 ●android:textColor设置文本颜色 ●android:textSize设置文本尺寸

Android OnTouchListener触屏事件接口

Android OnTouchListener触屏事件接口 OnTouchListener接口是用来处理手机屏幕事件的监听接口,当为View的范围内触摸按下、抬起或滑动等动作时都会触发该事件。该接口中的监听方法签名如下。 Java代码: public boolean onT ouch(View v, MotionEvent event) 参数v:参数v同样为事件源对象。 参数event:参数event为事件封装类的对象,其中封装了触发事件的详细信息,同样包括事件的类型、触发时间等信息。 节中介绍了一个在屏幕中拖动矩形移动的案例,本节将继续采用该案例的思路,通过监听接口的方式实现在屏幕上拖动按钮移动的案例。开发步骤如下。 创建一个名为Sample的Android项目。 准备字符串资源,打开strings.xml文件,用下列代码替换原有代码。 Java代码: Hello World, Sample Sample 位置 说明:与前面介绍的案例相同,对程序中用到的字符串资源进行定义。 开发布局文件。打开res/layout目录下的main.xml,用下列代码替换其原有代码。

Java代码: