菜单实现Android中的常量 DEFAULT_KEYS_SHORTCUT
文章结束给大家来个程序员笑话:[M]
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
- package com.silenceburn;
- import android.app.Activity;
- import android.os.Bundle;
- public class MenuShortCutTester extends Activity {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
- }
- }
为默许的main.xml中的TextView加增一个id属性,之后我们会用菜单选项控制这行字的颜色
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:id="@+id/myText"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/hello"
- />
- </LinearLayout>
用使findViewById取获上一步中义定了id的本文对象,将其用引保存在成员变量b中。
写重onPrepareOptionsMenu方法,加增我们自己的菜单项,并册注快捷键,同时加增菜单点击的响应事件。
- package com.silenceburn;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.Menu;
- import android.view.MenuItem;
- import android.view.MenuItem.OnMenuItemClickListener;
- import android.widget.TextView;
- public class MenuShortCutTester extends Activity {
- /** Called when the activity is first created. */
- TextView b;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- b = (TextView) this.findViewById(R.id.myText);
- setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
- }
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- // TODO Auto-generated method stub
- super.onPrepareOptionsMenu(menu);
- menu.removeItem(0);
- menu.removeItem(1);
- menu.add( 0, 0, 0, "One").setShortcut('0', '0').setOnMenuItemClickListener(new OnMenuItemClickListener(){
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- // TODO Auto-generated method stub
- b.setBackgroundColor(android.graphics.Color.RED);
- return true;
- }});
- menu.add( 0, 1, 0, "Two").setShortcut('1', '1').setOnMenuItemClickListener(new OnMenuItemClickListener(){
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- // TODO Auto-generated method stub
- b.setBackgroundColor(android.graphics.Color.GREEN);
- return true;
- }});
- return true;
- }
- }
意注我们一共册注了两个菜单项,
一个叫“One”,点击时将本文对象 b 的背景颜色为改红色,同时义定其快捷键为0
一个叫“Two”,点击时将本文对象 b 的背景颜色为改绿色,同时义定其快捷键为1
至此示例程序成完。
3.验证用使示例程序
启动AVD,运行上述程序,程序启动后,我们当应看到是黑底灰字,点击menu按钮,可以看到One和Two两个菜单选项。
如下图所示:
现在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中可以找到如下码代片段:
- if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {
- return false;
- } else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {
- if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
- keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
- return true;
- }
- return false;
- }
由此可知,当统系检测到 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查看其实现类,结果如下图:
可以看的很楚清,实现类是 PhoneWindow ,
这样我们以可就到 PhoneWindow 的码源中去查找performPanelShortcut的实现了。
在PhoneWindow.java中我们可以看到如下码代片段:
- // Only try to perform menu shortcuts if preparePanel returned true (possible false
- // return value from application not wanting to show the menu).
- if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) {
- // The menu is prepared now, perform the shortcut on it
- handled = st.menu.performShortcut(keyCode, event, flags);
- }
于终看到menu字样了,这里我们可以看到 if 里头述描的调用条件,
首先以后panel必须已预备好了(你可以用 onPreparePanel 截获到预备求请),
其次,以后panel必须是有Menu的!(st.menu != null),
从这里我们可以白明DEFAULT_KEYS_SHORTCUT对于没有menu的应用是没有任何效果的。
而且在另外一处码代我们会看到还要停止 isShortCut 的判断,所以对于没有快捷键的菜单也是没有任何效果的。
那么我们再看看 preparePanel 里头是如何实现的,在其实现中可以找到如下码代片段:
- // Callback and return if the callback does not want to show the menu
- if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) {
- return false;
- }
至此,就完整白明了!码代在这里回调了 onPreparePanel ,而 onPreparePanel 中会回调 onPrepareOptionsMenu ,
而onPrepareOptionsMenu ,就是我们自己写实现自义定菜单的地方了。
为了验证上述推导,我们在onPrepareOptionsMenu 中放入断点,然后在菜单闭关的情况下,输入快捷键,
运行到断点后查看调用堆栈,入下图所示:
堆栈调用次序可以很楚清的看出我们的推导进程是准确的。至此 DEFAULT_KEYS_SHORTCUT 的实现析分毕完。
文章结束给大家分享下程序员的一些笑话语录:
神灯新篇
一个程序员在海滩上发现了一盏神灯。他在灯上擦了几下,一个妖怪就从灯里跳出来说:“我是世界上法术最强的妖怪。我可以实现你的任何梦想,但现在,我只能满足你一个愿望。”程序员摊开了一幅中东地图说:“我想让中东得到永久的和平。”妖怪答道:“哦,我没办法。自打创世纪以来,那里的战火就没有停息过。这世上几乎没有我办不到的事,但这件事除外。”程序员于是说:“好吧,我是一个程序员,为许多用户编写过程序。你能让他们把需求表述得更清楚些,并且让我们的软件项目有那么一两次按进度按成本完成吗?”妖怪说:“唔,我们还是来看中东地图吧。”