首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > 移动开发 >

DEFAULT_KEYS_SHORTCUT 效能的验证 及其 源码实现分析

2012-07-24 
DEFAULT_KEYS_SHORTCUT 功能的验证 及其 源码实现分析转自:http://blog.csdn.net/silenceburn/article/det

DEFAULT_KEYS_SHORTCUT 功能的验证 及其 源码实现分析

转自:http://blog.csdn.net/silenceburn/article/details/6069988

作者:silenceburn

Activity的setDefaultKeyMode (int mode) 方法用来设置一个Activity的默认的按键模式。

具体介绍可以参见我写的?setDefaultKeyMode 用法介绍???一文。

地址是:???http://blog.csdn.net/silenceburn/archive/2010/12/11/6069645.aspx

?

其中有一种模式是 DEFAULT_KEYS_SHORTCUT ,本文从API文档对此模式的介绍出发,

首先通过编写示例代码,介绍其功能用法,然后通过追踪源码,简单介绍此模式在android源码中是如何实现的。

?


1.??关于?DEFAULT_KEYS_SHORTCUT? 的?API文档介绍

Use with setDefaultKeyMode(int) to execute a menu shortcut in default key handling.

That is, the user does not need to hold down the menu key to execute menu shortcuts.

?

从字面上看,其含义是指,将默认的按键输入作为菜单快捷键进行处理。

也就是说,用户不需要按下menu按键,就可以处理菜单快捷键,听起来非常神奇,究竟是不是这样呢?

?

?


2.编写示例程序

我们编写一个程序验证一下其功能,首先新建一个工程,并设置默认按键模式为 DEFAULT_KEYS_SHORTCUT

?

[java]?view plaincopy
  1. package?com.silenceburn;??
  2. ??
  3. import?android.app.Activity;??
  4. import?android.os.Bundle;??
  5. ??
  6. public?class?MenuShortCutTester?extends?Activity?{??
  7. ????/**?Called?when?the?activity?is?first?created.?*/??
  8. ????@Override??
  9. ????public?void?onCreate(Bundle?savedInstanceState)?{??
  10. ????????super.onCreate(savedInstanceState);??
  11. ????????setContentView(R.layout.main);??
  12. ??????????
  13. ????????setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);??
  14. ????}??
  15. }??

?

?

?

为默认的main.xml中的TextView增加一个id属性,之后我们会用菜单选项控制这行字的颜色

?

[xhtml]?view plaincopy
  1. <?xml?version="1.0"?encoding="utf-8"?>??
  2. <LinearLayout?xmlns:android="http://schemas.android.com/apk/res/android"??
  3. ????android:orientation="vertical"??
  4. ????android:layout_width="fill_parent"??
  5. ????android:layout_height="fill_parent"??
  6. ????>??
  7. <TextView???
  8. ????android:id="@+id/myText"???
  9. ????android:layout_width="fill_parent"???
  10. ????android:layout_height="wrap_content"???
  11. ????android:text="@string/hello"??
  12. ????/>??
  13. </LinearLayout>??

?

?

?

使用findViewById获取上一步中定义了id的文本对象,将其引用保存在成员变量b中。

重写onPrepareOptionsMenu方法,增加我们自己的菜单项,并注册快捷键,同时增加菜单点击的响应事件。

?

[java]?view plaincopy
  1. package?com.silenceburn;??
  2. ??
  3. import?android.app.Activity;??
  4. import?android.os.Bundle;??
  5. import?android.view.Menu;??
  6. import?android.view.MenuItem;??
  7. import?android.view.MenuItem.OnMenuItemClickListener;??
  8. import?android.widget.TextView;??
  9. ??
  10. public?class?MenuShortCutTester?extends?Activity?{??
  11. ????/**?Called?when?the?activity?is?first?created.?*/??
  12. ????TextView?b;??
  13. ??????
  14. ????@Override??
  15. ????public?void?onCreate(Bundle?savedInstanceState)?{??
  16. ????????super.onCreate(savedInstanceState);??
  17. ????????setContentView(R.layout.main);??
  18. ??????????
  19. ????????b?=?(TextView)?this.findViewById(R.id.myText);??
  20. ??????????
  21. ????????setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);??
  22. ????}??
  23. ??????
  24. ????@Override??
  25. ????public?boolean?onPrepareOptionsMenu(Menu?menu)?{??
  26. ????????//?TODO?Auto-generated?method?stub??
  27. ????????super.onPrepareOptionsMenu(menu);??
  28. ??????????
  29. ????????menu.removeItem(0);??
  30. ????????menu.removeItem(1);??
  31. ????????menu.add(?0,?0,?0,?"One").setShortcut('0',?'0').setOnMenuItemClickListener(new?OnMenuItemClickListener(){??
  32. ??
  33. ????????????@Override??
  34. ????????????public?boolean?onMenuItemClick(MenuItem?item)?{??
  35. ????????????????//?TODO?Auto-generated?method?stub??
  36. ????????????????b.setBackgroundColor(android.graphics.Color.RED);??
  37. ????????????????return?true;??
  38. ????????????}});??
  39. ????????menu.add(?0,?1,?0,?"Two").setShortcut('1',?'1').setOnMenuItemClickListener(new?OnMenuItemClickListener(){??
  40. ??
  41. ????????????@Override??
  42. ????????????public?boolean?onMenuItemClick(MenuItem?item)?{??
  43. ????????????????//?TODO?Auto-generated?method?stub??
  44. ????????????????b.setBackgroundColor(android.graphics.Color.GREEN);??
  45. ????????????????return?true;??
  46. ????????????}});???
  47. ??????????
  48. ????????return?true;??
  49. ????}??
  50. }??

?

?

?

注意我们一共注册了两个菜单项,

一个叫“One”,点击时将文本对象 b 的背景颜色改为红色,同时定义其快捷键为0

一个叫“Two”,点击时将文本对象 b 的背景颜色改为绿色,同时定义其快捷键为1

?

至此示例程序完成。

?


3.验证使用示例程序

启动AVD,运行上述程序,程序启动后,我们应当看到是黑底灰字,点击menu按钮,可以看到One和Two两个菜单选项。

如下图所示:

DEFAULT_KEYS_SHORTCUT 效能的验证 及其 源码实现分析

?

目前Menu是打开状态,

点击One ,将把“helloworld...”字样的背景色变为红色,

点击Two ,将把“helloworld...”字样的背景色变为红绿色。

或者我们点设置好的快捷键 0 和 1,发现可以直接调用菜单选项控制颜色变化。?

?

到目前为止一切都很正常,不过,神奇的现在来了!?

?

我们首先关闭菜单,

然后直接点键盘键"0“,看看会发生什么。再直接点键盘键"1" ,看看会发生什么。

哈哈,在没有激活菜单的情况下,菜单项快捷键被直接调用了!根本不需要打开菜单,就可以用激活菜单快捷键!

?

什么?有位同学说快捷键就应该是这样子把,那好,请你把 onCreate 里面的

setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT); 改为 setDefaultKeyMode(DEFAULT_KEYS_DISABLE);

然后再运行试试,在不打开菜单的情况下,你就是把 0 和 1 按坏,系统也不会理你的呵呵

?


4.浅析实现原理

那么这神奇的功能是如何实现的呢?我们试着通过分析android源码找到答案。

?

首先顺藤摸瓜,我们找一找系统是如何处理 DEFAULT_KEYS_SHORTCUT 关键字的,

在Activity.java中可以找到如下代码片段:

?

?

[java]?view plaincopy
  1. if?(mDefaultKeyMode?==?DEFAULT_KEYS_DISABLE)?{??
  2. ????return?false;??
  3. }?else?if?(mDefaultKeyMode?==?DEFAULT_KEYS_SHORTCUT)?{??
  4. ????if?(getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,???
  5. ????????????keyCode,?event,?Menu.FLAG_ALWAYS_PERFORM_CLOSE))?{??
  6. ????????return?true;??
  7. ????}??
  8. ????return?false;??
  9. }??

?

?

由此可知,当系统检测到 DEFAULT_KEYS_SHORTCUT 关键字时,实际调用了

getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,

keyCode, event,Menu.FLAG_ALWAYS_PERFORM_CLOSE)

?

我们继续追寻,但是这里会遇到一个困难,就是查阅API文档你会发现,performPanelShortcut函数是个纯虚函数!

接下来该怎么办呢?既然功能顺利执行了,那么这个纯虚函数一定会有一个实现的。这个实现类必然是window类的子类。

所以我们在OnCreate里面加上一行代码 Window w =? this.getWindow();

然后通过Eclipse的调试器,利用RTTI查看其实现类,结果如下图:

?

DEFAULT_KEYS_SHORTCUT 效能的验证 及其 源码实现分析

?

可以看的很清楚,实现类是 PhoneWindow ,

这样我们就可以到 PhoneWindow 的源码中去查找performPanelShortcut的实现了。

?

在PhoneWindow.java中我们可以看到如下代码片段:

?

[java]?view plaincopy
  1. //?Only?try?to?perform?menu?shortcuts?if?preparePanel?returned?true?(possible?false??
  2. //?return?value?from?application?not?wanting?to?show?the?menu).??
  3. if?((st.isPrepared?||?preparePanel(st,?event))?&&?st.menu?!=?null)?{??
  4. ????//?The?menu?is?prepared?now,?perform?the?shortcut?on?it??
  5. ????handled?=?st.menu.performShortcut(keyCode,?event,?flags);??
  6. }??

?

?

终于看到menu字样了,这里我们可以看到 if 里面描述的调用条件,

首先当前panel必须已经准备好了(你可以用 onPreparePanel 截获到准备请求),

其次,当前panel必须是有Menu的!(st.menu != null),

从这里我们可以明白DEFAULT_KEYS_SHORTCUT对于没有menu的应用是没有任何效果的。

而且在另一处代码我们会看到还要进行 isShortCut 的判断,所以对于没有快捷键的菜单也是没有任何效果的。

?

那么我们再看看 preparePanel 里面是如何实现的,在其实现中可以找到如下代码片段:

?

[java]?view plaincopy
  1. //?Callback?and?return?if?the?callback?does?not?want?to?show?the?menu??
  2. if?(!cb.onPreparePanel(st.featureId,?st.createdPanelView,?st.menu))?{??
  3. ????return?false;??
  4. }??

?

?

至此,就完全明白了!代码在这里回调了 onPreparePanel ,而 onPreparePanel? 中会回调? onPrepareOptionsMenu ,

而onPrepareOptionsMenu ,就是我们自己写实现自定义菜单的地方了。

?

为了验证上述推导,我们在onPrepareOptionsMenu 中放入断点,然后在菜单关闭的情况下,输入快捷键,

运行到断点后查看调用堆栈,入下图所示:

?

?DEFAULT_KEYS_SHORTCUT 效能的验证 及其 源码实现分析

?

堆栈调用顺序可以很清楚的看出我们的推导过程是正确的。至此 DEFAULT_KEYS_SHORTCUT 的实现分析完毕。

?


?

总结:

?

我之所以非常喜爱和看好android平台,就是因为她是开源的,

当我们对任何一个问题有疑问时,都可以把她扒光了细看,呵呵。

而apple的IOS,尽管你很美,但是你的内心实在太难捉摸了。

?

从本文的分析过程可以看出,平台任何一个看似神奇的功能的实现,

背后都有安卓源码开发者们大巧不工重剑无锋的的实现。

?

本文对DEFAULT_KEYS_SHORTCUT的分析实际上是很浅显的,

如果细看源码,会发现更多的有意思的地方的。

?

学习安卓,

也许源码就是最好的老师。^_^

热点排行