展会信息港展会大全

Android TextView功能分析
来源:互联网   发布日期:2015-11-25 22:13:27   浏览:960次  

导读: 由于Android系统提供的控件比较多,因此我们只能挑一个比较有代表的控件进行分析。这个比较有代表性的控件便是TextView,其它的一些基础控件,例如Button、EditText和C...

由于Android系统提供的控件比较多,因此我们只能挑一个比较有代表的控件进行分析。这个比较有代表性的控件便是TextView,其它的一些基础控件,例如Button、EditText和CheckBox等,都是直接或者间接地以它为父类的。每一个控件的实现都是相当复杂的,不过基本上都是一些细节问题,而且不同的控件有不同的实现细节,因此,本文并不打算详细地分析TextView的具体实现,而是从所有控件为了实现自己的功能而需要的东西出发,去分析TextView的实现框架。

那么,控件为了实现自己的功能而需要的东西是什么呢?有两个材料是必不可少的。第一个材料是画布,第二个材料是用户输入。有画布才能绘制UI,而有用户输入才能与用户进行交互。因此,接下来我们主要分析TextView的绘制流程,以及它获得用户输入的过程。用户输入主要包括键盘输入以及触摸屏输入,本文主要关注的是键盘输入。触摸屏输入与键盘输入的获取过程是类似的,读者如果有兴趣的话,可以参照本文的内容来自己研究一下。

这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。

参数event描述的是窗口接收到的键盘事件,另外一个参数sendDone表示该键盘事件处理完成后,是否需要向系统的输入管理器发送一个通知。

ViewRoot类的成员变量mView描述的是窗口的顶层视图,即它指向的是一个DecorView对象,ViewRoot类的成员函数deliverKeyEvent首先是调用它的成员函数dispatchKeyEventPreIme来让它优先于输入法处理参数event所描述的键盘事件。如果这个DecorView对象的成员函数dispatchKeyEventPreIme的返回值handled等于true,那么就说明参数event所描述的键盘事件已经处理完毕,即ViewRoot类的成员函数deliverKeyEvent不用往下执行了。在这种情况下,如果参数sendDone的值等于true,那么ViewRoot类的成员函数deliverKeyEvent在返回之前,还会调用成员函数finishInputEvent来通知系统的输入管理器,当前激活的窗口已经处理完成刚刚发生的键盘事件了。在接下来的Step 2到Step 4中,我们再详细分析键盘事件优先于输入法分发给窗口处理的过程。

假设窗口不在输入法前面拦截参数event所描述的键盘事件,接下来ViewRoot类的成员函数deliverKeyEvent就会将该键盘事件分发给输入法处理,这个分发过程如下所示:

1. 调用InputMethodManager类的静态成员函数peekInstance获得一个类型为InputMethodManager输入法管理器imm;

2. 调用ViewRoot类的成员函数enqueuePendingEvent将参数event所描述的键盘事件缓存起来,等到输入法处理完成该键盘事件之后,再继续对它进行处理;

3. 调用第1步获得的输入法管理器imm的成员函数dispatchKeyEvent来将参数event所描述的键盘事件分发给输入法处理。

这里有两个地方是需要注意的。第一个地方是只有当前窗口正在显示输入法的情况下,ViewRoot类的成员函数deliverKeyEvent才会将参数event所描述的键盘事件分发给输入法处理,这是通过检查ViewRoot类的成员变量mLastWasImTarget的值是否等于true来确定的。第二个地方是在将参数event所描述的键盘事件分发给输入法处理时,ViewRoot类的成员函数deliverKeyEvent会同时传递一个类型为InputMethodCallback的回调接口给输入法,以便输入法处理完成参数event所描述的键盘事件之后,可以调用这个回调接口的成员函数finishedEvent来向窗口发送一个键盘事件处理完成通知。这个类型为InputMethodCallback的回调接口就保存在ViewRoot类的成员变量mInputMethodCallback中,当它的成员函数finishedEvent被调用的时候,它就会调用ViewRoot类的成员函数deliverKeyEventToViewHierarchy来继续将参数event所描述的键盘事件分发给窗口处理。

如果窗口当前不需要与输入法交互,即ViewRoot类的成员变量mLastWasImTarget的值等于false,那么ViewRoot类的成员函数deliverKeyEvent就会直接调用成员函数deliverKeyEventToViewHierarchy来将参数event所描述的键盘事件分发给窗口处理。

接下来,我们就先析窗口在输入法之前处理键盘输入的过程,接着再分析窗口在输入法之后处理键盘输入的过程。

从前面的分析可以知道,ViewRoot类的成员函数deliverKeyEvent是通过调用DecorView类的成员函数dispatchKeyEventPreIme来将获得的键盘输入优先于输入法分发给窗口处理的。DecorView类的成员函数dispatchKeyEventPreIme是从父类ViewGroup继承下来的,因此,接下来我们就继续分析ViewGroup类的成员函数dispatchKeyEventPreIme的实现。

Step 2. ViewGroup.dispatchKeyEventPreIme

[java]

public abstract class ViewGroup extends View implements ViewParent, ViewManager {

......

// The view contained within this ViewGroup that has or contains focus.

private View mFocused;

......

@Override

public boolean dispatchKeyEventPreIme(KeyEvent event) {

if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {

return super.dispatchKeyEventPreIme(event);

} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {

return mFocused.dispatchKeyEventPreIme(event);

}

return false;

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/ViewGroup.java中。

ViewGroup类的成员函数dispatchKeyEventPreIme首先是检查当前正在处理的视图容器是否能够获得焦点。如果能够获得焦点的话,那么ViewGroup类的成员变量mPrivateFlags的FOCUSED位就会等于1。在当前正在处理的视图容器能够获得焦点的情况下,还要检查正在处理的视图容器是否已经计算过大小了,即检查ViewGroup类的成员变量mPrivateFlags的HAS_BOUNDS位是否等于1。只有在已经计算过大小并且能够获得焦点的情况下,那么正在处理的视图容器才有资格处理参数event所描述的键盘事件。注意,正在处理的视图容器是通过调用其父类View的成员函数dispatchKeyEventPreIme来处理参数event所描述的键盘事件的。

如果当前正在处理的视图容器没有资格处理参数event所描述的键盘事件,但是它有一个能够获得焦点的子视图,并且这个子视图的大小也是已经计算好了的,那么ViewGroup类的成员函数dispatchKeyEventPreIme就会将参数event所描述的键盘事件分发给该子视图处理。当前正在处理的视图容器能够获得焦点的子视图是通过ViewGroup类的成员变量mFocused来描述的,通过调用这个成员变量所描述的一个View对象的成员函数dispatchKeyEventPreIme即可将参数event所描述的键盘事件分发给够获得焦点的子视图处理。

一个视图容器是如何知道它的焦点子视图的呢?我们知道,当我们在屏幕上触摸一个窗口时,就会发生一个Pointer事件。这个Pointer事件关联有一个触摸点,通过检查这个触摸点当前是包含在窗口顶层视图的哪一个子视图里面,就可以知道哪一个子视图是焦点子视图了。

从上面的分析可以知道,无论是当前正在处理的视图容器获得焦点,还是它的子视图获得焦点,最终都是通过调用View类的成员函数dispatchKeyEventPreIme来在输入法之前处理参数event所描述的键盘事件,因此,接下来我们就继续分析View类的成员函数dispatchKeyEventPreIme的实现。

Step 3. View.dispatchKeyEventPreIme

[java]

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {

......

public boolean dispatchKeyEventPreIme(KeyEvent event) {

return onKeyPreIme(event.getKeyCode(), event);

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

View类的成员函数dispatchKeyEventPreIme的实现很简单,它只是通过调用另外一个成员函数onKeyPreIme来在输入法之前处理参数event所描述的键盘事件。

Step 4. View.onKeyPreIme

[java]

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {

......

public boolean onKeyPreIme(int keyCode, KeyEvent event) {

return false;

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

View类的成员函数onKeyPreIme默认是不会在输入法之前处理参数event所描述的键盘事件的,因此,我们在实现自己的控件的时候,如果需要在输入法之前处理键盘输入,那么就必须重写父类View的成员函数onKeyPreIme。在重写父类View的成员函数onKeyPreIme来处理一个键盘事件的时候,如果不希望这个键盘事件分发给输入法处理,那么就返回一个true值,www.superkp111.com, 否则的话,就返回一个false值。

我们假设当前获得焦点的是图1所示的TextView控件,但是由于TextView类没有重写其父类View的成员函数onKeyPreIme,因此,参数event所描述的键盘事件接下来就会继续分发给输入法或者当前激活的窗口处理。

这一步执行完成之后,回到前面的Step 1中,即ViewRoot类的成员函数deliverKeyEvent中www.superkp123.com,无论接下来是否需要先将一个键盘事件分发给输入法处理,最终都会调用到ViewRoot类的成员函数deliverKeyEventToViewHierarchy来继续将该键盘事件分发给当前激活的窗口处理。

Step 5. ViewRoot.deliverKeyEventToViewHierarchy

[java]

public final class ViewRoot extends Handler implements ViewParent,

View.AttachInfo.Callbacks {

......

private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) {

try {

if (mView != null && mAdded) {

final int action = event.getAction();

boolean isDown = (action == KeyEvent.ACTION_DOWN);

......

boolean keyHandled = mView.dispatchKeyEvent(event);

if (!keyHandled && isDown) {

int direction = 0;

switch (event.getKeyCode()) {

case KeyEvent.KEYCODE_DPAD_LEFT:

direction = View.FOCUS_LEFT;

break;

case KeyEvent.KEYCODE_DPAD_RIGHT:

direction = View.FOCUS_RIGHT;

break;

case KeyEvent.KEYCODE_DPAD_UP:

direction = View.FOCUS_UP;

break;

case KeyEvent.KEYCODE_DPAD_DOWN:

direction = View.FOCUS_DOWN;

break;

}

if (direction != 0) {

View focused = mView != null ? mView.findFocus() : null;

if (focused != null) {

View v = focused.focusSearch(direction);

......

if (v != null && v != focused) {

......

focusPassed = v.requestFocus(direction, mTempRect);

}

......

}

}

}

}

} finally {

if (sendDone) {

finishInputEvent();

}

......

}

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。

ViewRoot类的成员函数deliverKeyEventToViewHierarchy首先将参数event所描述的键盘事件交给当前激活的窗口的顶层视图来处理,这是通过调用ViewRoot类的成员变量mView所描述的一个DecorView对象的成员函数dispatchKeyEvent来实现的。

如果当前激活的窗口的顶层视图在处理完成参数event所描述的键盘事件之后,希望该键盘事件还能继续被ViewRoot类的成员函数deliverKeyEventToViewHierarchy处理,那么前面调用DecorView类的成员函数dispatchKeyEvent得到的返回值keyHandled的值就会等于false。在这种情况下,如果参数event描述的是一个按下的键盘事件,即变量isDown的值等于true,那么ViewRoot类的成员函数deliverKeyEventToViewHierarchy就会继续检查参数event描述的是否是一个DPAD事件。如果是的话,那么就可能需要改变窗口当前的焦点子视图。

如果参数event描述的是一个DPAD事件,那么最终得到的变量direction的值就不会等于0,并且它描述的是当前按下的是哪一个方向的DPAD键。假设这时候窗口已经有一个焦点子视图,即调用ViewRoot类的成员变量mView所描述的一个DecorView对象的成员函数findFocus的返回值focused不等于null,那么接下来就要根据变量direction的值来决定下一个焦点子视图是谁。例如,假设变量direction的值等于View.FOCUS_LEFT,www.fpjinan.com那么就表示在当前的焦点子视图focused的左边查找一个最靠近的子视图作为下一个焦点子视图,这是通过调用当前焦点子视图focused的成员函数focusSearch来实现的。

一旦找到了下一个焦点子视图v,并且该子视图不是当前的焦点子视图focused,那么ViewRoot类的成员函数deliverKeyEventToViewHierarchy就需要将子视图v设置为焦点子视图,这是通过调用变量v所描述的一个View对象的成员函数requestFocus来实现的。

通过前面的操作,参数event所描述的键盘事件就处理完成了。如果这时候参数sendDone的值等于true,那么就表示需要通知系统的输入管理器,参数event所描述的键盘事件已经处理完成了,这是通过调用ViewRoot类的成员函数finishInputEvent来实现的。

接下来,我们就继续分析DecorView类的成员函数dispatchKeyEvent的实现,以便可以了解窗口的顶层视图分发键盘事件的过程。

Step 6. DecorView.dispatchKeyEvent

[java]

public class PhoneWindow extends Window implements MenuBuilder.Callback {

......

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

......

@Override

public boolean dispatchKeyEvent(KeyEvent event) {

final int keyCode = event.getKeyCode();

final boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;

......

final Callback cb = getCallback();

final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)

: super.dispatchKeyEvent(event);

if (handled) {

return true;

}

return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)

: PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);

}

......

}

......

}

这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。

PhoneWindow类的成员函数getCallback是从父类Window继承下来的,它返回的是一个Window.Callback接口。每一个Activity组件都会实现一个Window.Callback接口,并且将这个Window.Callback接口设置到与它所关联的一个PhoneWindow对象的内部去,这样当该PhoneWindow对象接收到键盘事件的时候,就可以该键盘事件分发给与它所关联的Activity组件处理。

DecorView类的成员变量mFeatureId用来描述当前正在处理的DecorView对象的特征,当它的值小于0的时候,就表示当前正在处理的一个DecorView对象是用来描述一个Activity组件窗口的顶层视图的。

因此,当当前正在处理的DecorView对象描述的是一个Activity组件窗口的顶层视图,并且这个Activity组件实现有一个Window.Callback接口时,DecorView类的成员函数dispatchKeyEvent就会调用该Window.Callback接口的成员函数dispatchKeyEvent来通知对应的Activity组件,它接收到一个键盘事件了。否则的话,参数event所描述的键盘事件就会被分发给当前正在处理的DecorView对象的父对象来处理,这是通过调用DecorView类的父类View的成员函数dispatchKeyEvent来实现的。

我们假设当前正在处理的DecorView对象描述的是一个Activity组件窗口的顶层视图,并且这个Activity组件实现有一个Window.Callback接口,那么参数event所描述的键盘事件接下来就会分给该Activity组件处理。如果该Activity组件在处理完成这个键盘事件之后,希望该键盘事件还能继续分发下去给其它对象处理,那么它所实现的Window.Callback接口的成员函数dispatchKeyEvent的返回值handled就会等于false,这时候DecorView类的成员函数dispatchKeyEvent就会将该键盘事件分发给与当前正在处理的DecorView对象所关联的一个PhoneWindow www.fpnanchang.com对象的成员函数onKeyDown或者onKeyUp来处理,取决于变量isDown的值是true还是false,即当前发生的键盘事件是与按键按下有关,还是与按键松开有关。

PhoneWindow类的成员函数onKeyDown和onKeyUp主要是有来监控一些特殊按键事件,例如电话键和音量键,以便可以执行一些对应的逻辑。例如,当按下电话键时,就打开拨号程序;又如,当按下音量键时,就调节音量的大校

接下来,我们就继续分析Activity类所实现的Window.Callback www.fpzhuhai.com接口的成员函数dispatchKeyEvent的实现,以便可以了解键盘事件在Activity组件窗口的分发过程。

Step 7. Activity.dispatchKeyEvent

[java]

public class Activity extends ContextThemeWrapper

implements LayoutInflater.Factory,

Window.Callback, KeyEvent.Callback,

OnCreateContextMenuListener, ComponentCallbacks {

......

public boolean dispatchKeyEvent(KeyEvent event) {

......

Window win = getWindow();

if (win.superDispatchKeyEvent(event)) {

return true;

}

View decor = mDecor;

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

return event.dispatch(this, decor != null

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

}

......

}

这个函数定义在文件frameworks/base/core/java/android/app/Activity.java中。

Activity类的成员函数getWindow返回的是与当前正处理的Activity组件所关联的一个PhoneWindow对象,Activity类的成员函数dispatchKeyEvent获得了这个PhoneWindow对象之后,就会调用它的成员函数superDispatchKeyEvent,以便可以将参数event所描述的键盘事件分发给它处理。

这个PhoneWindow对象在处理完成参数event所描述的键盘事件之后,如果希望该键盘事件能继续往下分发,那么Activity类的成员函数dispatchKeyEvent就会将该键盘事件分发给当前正在处理的Activity组件处理,这是通过调用参数event所描述的一个KeyEvent对象的成员函数dispatch来实现的。

注意,在调用event所描述的一个KeyEvent对象的成员函数dispatch的时候,第一个参数指定为当前正在处理的Activity组件所实现的一个KeyEvent.Callback接口。参数event所指向的一个KeyEvent对象的成员函数dispatch的执行的过程中,就会相应地调用这个KeyEvent.Callback接口的成员函数onKeyDown、onKeyUp或者onKeyMultiple来处理它所描述的键盘事件,实际上就是调用Activity类的成员函数onKeyDown、onKeyUp或者onKeyMultiple来处理参数event所描述的键盘事件。因此,我们在自定义一个Activity组件时,如果需要处理分发给该Activity组件的键盘事件,那么就需要重写父类Activity的成员函数onKeyDown、onKeyUp或者onKeyMultiple。

接下来,我们就继续分析PhoneWindow类的成员函数superDispatchKeyEvent的实现,以便可以了解键盘事件在Activity组件窗口的分发过程。

Step 8. PhoneWindow.superDispatchKeyEvent

[java]

public class PhoneWindow extends Window implements MenuBuilder.Callback {

......

// This is the top-level view of the window, containing the window decor.

private DecorView mDecor;

......

@Override

public boolean superDispatchKeyEvent(KeyEvent event) {

return mDecor.superDispatchKeyEvent(event);

}

......

}

这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。

PhoneWindow类的成员变量mDecor描述的是当前正在处理的Activity组件窗口的顶层视图,PhoneWindow类的成员函数superDispatchKeyEvent通过调用它所指向的一个DecorView对象的成员函数superDispatchKeyEvent来处理参数event所描述的键盘事件。

Step 9. DecorView.superDispatchKeyEvent

[java]

public class PhoneWindow extends Window implements MenuBuilder.Callback {

......

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

......

public boolean superDispatchKeyEvent(KeyEvent event) {

return super.dispatchKeyEvent(event);

}

......

}

......

}

这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中。

DecorView类的成员函数superDispatchKeyEvent的实现很简单,它只是调用父类ViewGroup的成员函数dispatchKeyEvent来处理参数event所描述的键盘事件。

Step 10. ViewGroup.dispatchKeyEvent

[java]

public abstract class ViewGroup extends View implements ViewParent, ViewManager {

......

@Override

public boolean dispatchKeyEvent(KeyEvent event) {

if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {

return super.dispatchKeyEvent(event);

} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {

return mFocused.dispatchKeyEvent(event);

}

return false;

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/ViewGroup.java中。

ViewGroup类的成员函数dispatchKeyEvent的实现与在前面的Step 3中所介绍的ViewGroup类的成员函数dispatchKeyEventPreIme的实现是类似的,即如果当前正在处理的视图容器能够获得焦点并且该视图容器的大小已经计算好了,那么就会将参数event所描述的键盘事件分发给它的父类View的成员函数dispatchKeyEvent来处理,否则的话,如果当前正在处理的视图容器有一个焦点子视图,并且这个焦点子视图的大小已经计算好了,那么就将参数event所描述的键盘事件分发给该焦点子视图的父类View的成员函数dispatchKeyEvent来处理。

从前面的调用过程可以知道,当前正在处理的视图容器即为Activity组件窗口的顶层视图。我们假设在该顶层视图中,获得焦点的是一个TextView控件,并且这个TextView控件的大小已经计算好了,那么接下来就会调用这个TextView控件的父类View的成员函数dispatchKeyEvent来处理参数event所描述的键盘事件。

Step 11. View.dispatchKeyEvent

[java]

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {

......

private OnKeyListener mOnKeyListener;

......

public boolean dispatchKeyEvent(KeyEvent event) {

// If any attached key listener a first crack at the event.

//noinspection SimplifiableIfStatement

......

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

&& mOnKeyListener.onKey(this, event.getKeyCode(), event)) {

return true;

}

return event.dispatch(this, mAttachInfo != null

? mAttachInfo.mKeyDispatchState : null, this);

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

当View类的成员变量mOnKeyListener的值不等于null时,它所指向的一个OnKeyListener对象描述的是注册到当前正在处理的视图的一个键盘事件监听器。在这种情况下,如果当前正在处理的视图是处于启用状态的,即它的成员变量mViewFlags的ENABLED位等于1,那么参数event所描述的键盘事件就先分给该键盘事件监听器处理,这是通过调用View类的成员变量mOnKeyListener所指向的一个OnKeyListener对象的成员函数onKey来实现的。

注册到当前正在处理的视图的键盘事件监听器在处理完成参数event所描述的键盘事件之后,如果希望该键盘事件还能继续往下处理,那么View类的成员函数dispatchKeyEvent就会继续调用参数event所指向的一个KeyEvent对象的成员函数dispatch来处理该键盘事件。

接下来,我们就继续分析KeyEvent类的成员函数dispatch的实现,以便可以了解键盘事件在Activity组件窗口的分发过程。

Step 12. KeyEvent.dispatch

[java]

public class KeyEvent extends InputEvent implements Parcelable {

......

public final boolean dispatch(Callback receiver, DispatcherState state,

Object target) {

switch (mAction) {

case ACTION_DOWN: {

......

boolean res = receiver.onKeyDown(mKeyCode, this);

......

return res;

}

case ACTION_UP:

......

return receiver.onKeyUp(mKeyCode, this);

case ACTION_MULTIPLE:

final int count = mRepeatCount;

final int code = mKeyCode;

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

return true;

}

......

return false;

}

return false;

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/KeyEvent.java中。

从前面的调用过程可以知道,参数receiver指向的是一个View对象所实现的一个KeyEvent.Callback接口,这个KeyEvent.Callback接口是用来接收当前正在处理的键盘事件。

KeyEvent类的成员变量mAction描述的的是当前正在处理的键盘事件的类型,当它的值等于ACTION_DOWN、ACTION_UP和ACTION_MULTIPLE的时候,KeyEvent类的成员函数dispatch就会分别调用参数receiver所指向的一个View对象的成员函数onKeyDown、onKeyUp和onKeyMultiple来接收当前正在处理的键盘事件。

假设当前正在处理的键盘事件是与按键按下相关的,即KeyEvent类的成员变量mAction的值等于ACTION_DOWN,那么接下来就会调用参数receiver所指向的一个View对象的成员函数onKeyDown来接收当前正在处理的键盘事件。

由于前面我们已经假设了当前获得焦点的是一个TextView控件,因此,参数receiver指向的实际上是一个TextView对象。TextView类重写了父类View的成员函数onKeyDown,因此,接下来KeyEvent类的成员函数dispatch就会调用TextView类的成员函数onKeyDown来接收当前正在处理的键盘事件。

Step 13. TextView.onKeyDown

[java]

public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {

......

@Override

public boolean onKeyDown(int keyCode, KeyEvent event) {

int which = doKeyDown(keyCode, event, null);

if (which == 0) {

// Go through default dispatching.

return super.onKeyDown(keyCode, event);

}

return true;

}

......

}

这个函数定义在文件frameworks/base/core/java/android/widget/TextView.java中。

TextView类的成员函数onKeyDown调用另外一个成员函数doKeyDown来处理参数event所描述的键盘事件,以便可以相应地改变当前获得焦点的TextView控件的UI。当TextView类的成员函数doKeyDown的返回值which等于0的时候,就表示当前获得焦点的TextView控件希望参数event所描述的键盘事件可以继续分发给它的父类View处理,这是通过调用父类View的成员函数onKeyDown来实现的。www.2cto.com

至此,我们就分析完成TextView控件获得键盘事件的过程了,整个TextView控件的实现框架也分析完成了。

在Android系统中,其它的Android控件与TextView控件的实现框架都是类似的,区别就在于实现细节和所表现的UI不一样,而且它们一般都有一个共同的特点,那就是都在宿主窗口的绘图表面上进行UI绘制。在接下来的一篇文章中,我们就分析另外一个种以SurfaceView为代表的特殊控件,它们的UI是绘制在一个专用的绘图表面上面的,即它们不与宿主窗口共享同一个绘图表面。敬请关注!

赞助本站

人工智能实验室

相关热词: android开发 教程

AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港