`
java-admin
  • 浏览: 1362389 次
  • 性别: Icon_minigender_1
  • 来自: 陕西.西安
社区版块
存档分类
最新评论

在android的状态栏(statusbar)中增加menu,home和back快捷键的方法

阅读更多

需要说明的是:刚入手android没几天,对系统还不算很熟悉,这篇文章是基于前一篇转帖做的。只是觉得他的方法有些麻烦,而且改出来的效果也不 是我想要的。

由于完全改了 status bar ,建议先做几张 png 图片,加到

Frameworks/base/core/res/res/drawable

下。最好做一张背景图,替换 statusbar_background.png

另外我又加了几张 icon ,分别是 home menu back 的正常和按下状态。

这些图片为:

stat_home.png

stat_home_pressed.png

stat_back.png

stat_back_pressed.png

stat_menu.png

stat_menu_pressed.png

修改步骤为:

一.    修改 xml 界面

 

1. 增加图标

 

当然,更改整个 status bar 避免不要要对源码大刀修一下。我的该法是:

  修改 status bar layerout 文件:

Frameworks/base/core/res/res/layout/status_bar.xml

在原来的 linearlayout 中新增三个 image view

 

  1. < LinearLayout   android:id = "@+id/icons"   
  2.     android:layout_width = "fill_parent"   
  3.     android:layout_height = "fill_parent"   
  4.     android:orientation = "horizontal" >   
  5.   
  6.           < ImageView   android:id = "@+id/status_home"   
  7.                  android:layout_width = "wrap_content"   
  8.                  android:layout_height = "wrap_content"   
  9.                  android:layout_gravity = "top"   
  10.                  android:paddingTop = "6dip"   
  11.                  android:paddingRight = "10dip"   
  12.                  android:paddingLeft = "10dip"   
  13.                  android:src = "@drawable/stat_home"   />   
  14.   
  15.     < com.android.server.status.IconMerger   android:id = "@+id/notificationIcons"   
  16.         android:layout_width = "0dip"   
  17.         android:layout_weight = "1"   
  18.         android:layout_height = "fill_parent"   
  19.         android:layout_alignParentLeft = "true"   
  20.         android:paddingLeft = "6dip"   
  21.         android:gravity = "center_vertical"   
  22.         android:orientation = "horizontal" />     
  23.           
  24.     < LinearLayout   android:id = "@+id/statusIcons"   
  25.         android:layout_width = "wrap_content"   
  26.         android:layout_height = "fill_parent"   
  27.         android:layout_alignParentRight = "true"   
  28.         android:paddingRight = "6dip"   
  29.         android:gravity = "center_vertical"   
  30.         android:orientation = "horizontal" />       
  31.   
  32.           < ImageView   android:id = "@+id/status_menu"   
  33.                  android:layout_width = "wrap_content"   
  34.                  android:layout_height = "wrap_content"   
  35.                  android:layout_gravity = "top"   
  36.                  android:paddingTop = "6dip"   
  37.                  android:paddingLeft = "10dip"   
  38.                  android:paddingRight = "10dip"   
  39.                  android:src = "@drawable/stat_menu"   />   
  40.   
  41.           < ImageView   android:id = "@+id/status_back"   
  42.                  android:layout_width = "wrap_content"   
  43.                  android:layout_height = "wrap_content"   
  44.                  android:layout_gravity = "top"   
  45.                  android:paddingTop = "6dip"   
  46.                  android:paddingRight = "10dip"   
  47.                  android:paddingLeft = "10dip"   
  48.                  android:src = "@drawable/stat_back"   />   
  49.   
  50. /LinearLayout>   

  

这样做的好处就是简单。同时保证 最右端是 home 按钮,最左端是 back 按钮,不受它本来的约束。这样 status bar 上即可看到这些按钮了。

       图标的位置,可通过修改 paddingRight paddingLeft paddingTop 的值达到最佳视觉效果。

 

2. 修改 status bar 的高度。

 

既然要在 status bar 上增加那么几个按钮,当然是想要使用触摸操作的, android 自带的 status bar 高度太小,不适用。对于 7 寸屏的话, 50pixel 的高度应该是差不多了。

修改高度很简单,如我转的 shinning mm 的博文。

修改 frameworks/base/core/res/res/values/dimens.xml status_bar_height 属性

    <!-- Height of the status bar -->

    <dimen name="status_bar_height">50dip</dimen>

当然,如果相改 title 的高度,可以修改 Frameworks /base/core/res/res/values/themes.xml 中的 Window attributes windowTitleSize 值,不过我觉得没必要,改了反倒不好看了 :)

 

编译运行一下:

 

  1. ~/donut$ source ./env.sh  
  2. ~/donut$ make –j8  
  3. ~/donut$ emulator –skin WVGA800  

看状态栏是不是改变了?

 

 

为按钮添加动态效果

 

添加动态效果,就是触摸按下 hilight ,松开或者移出后恢复的动作。这一块,我是通过修改 frameworks/base/services/java/com/android/server/status/StatusBarView.java 实现的。

 

1. 获取 statusbar 中新增加的 icon handler

 

在类中新增加三个成员(这需要 import android.widget.ImageView; ):

 

  1. ImageView mHomeIcon;  
  2. ImageView mBackIcon;  
  3. ImageView mMenuIcon;  
      

同时增加三个常量,表示这些 icon 对应的键值 ( 这需要 import android.view.KeyEvent;)

 

  1. public   static   final   int  RESV_KEY_HOME = KeyEvent.KEYCODE_HOME;  
  2. public   static   final   int  RESV_KEY_BACK = KeyEvent.KEYCODE_BACK;  
  3. public   static   final   int  RESV_KEY_MENU = KeyEvent.KEYCODE_MENU;;  
   

onFinishInflate() 中,获得实际的对象:

  1. mHomeIcon = (ImageView)findViewById(R.id.status_home);  
  2. mBackIcon = (ImageView)findViewById(R.id.status_back);  
  3. mMenuIcon = (ImageView)findViewById(R.id.status_menu);  

 

这三个对象就是我们在 status_bar.xml 中添加的。

 

2. 添加触摸处理。

首先,应该判断是那个图标被按下,这个我们在 StatusBarView.Java onTouchEvent 中来判断。

这里,我做了一个小的按键状态,已方便处理按下、弹起和移出的动作。

首先增加两个状态成员:

  1. int  mResvKeyState = -1;   //记住的上次按键状态, -1为无状态。   
  2. int  mResvKeyCode  = -1;   //记住的上次按键值,-1为无状态。   
             

 

这样我的 onTouchEvent 就变成这样了:

  1. @Override   
  2.    public   boolean  onTouchEvent(MotionEvent event) {  
  3.     if (mService.mExpanded== true  || mService.mTracking== true ){  
  4.        if  (event.getAction() != MotionEvent.ACTION_DOWN) {  
  5.            mService.interceptTouchEvent(event);  
  6.        }  
  7.         return   true ;  
  8.     }  
  9.   
  10.     if (mResvKeyState == - 1 // remembered key state, no reserve   
  11.     {  
  12.         switch (getResvKeyArea(event)){  
  13.             case  RESV_KEY_HOME:  
  14.             case  RESV_KEY_BACK:  
  15.             case  RESV_KEY_MENU:  
  16.             {  
  17.                 mResvKeyState = event.getAction();  
  18.                 mResvKeyCode  = getResvKeyArea(event);  
  19.   
  20.                 updateResvKeyIcon(mResvKeyState, mResvKeyCode);  
  21.             }  
  22.             break ;  
  23.               
  24.             default :  
  25.             if  (event.getAction() != MotionEvent.ACTION_DOWN) {  
  26.                 mService.interceptTouchEvent(event);  
  27.             }  
  28.         }  
  29.     }else {  
  30.         mResvKeyState = event.getAction(); // new state   
  31.           
  32.         if (mResvKeyState == MotionEvent.ACTION_MOVE){  
  33.             if (mResvKeyCode != getResvKeyArea(event)){  
  34.                 /* out of bound, resume the icon */   
  35.                 updateResvKeyIcon(MotionEvent.ACTION_UP, mResvKeyCode);  
  36.                   
  37.                 mResvKeyCode  = -1 ;  
  38.                 mResvKeyState = -1 ;  
  39.             }  
  40.         }else   if (mResvKeyState == MotionEvent.ACTION_UP){  
  41.             updateResvKeyIcon(mResvKeyState, mResvKeyCode);  
  42.             mResvKeyCode  = -1 ;  
  43.             mResvKeyState = -1 ;  
  44.         }else {  
  45.             Log.d(TAG, "state machine error! Never be here!" );  
  46.         }  
  47.     }  
  48.       
  49.        return   true ;  
  50.    }  

里面用到的两个private方法简单实现如下:

  1. private   int  getResvKeyArea(MotionEvent  event )  
  2. {  
  3.     if (  ( event .getX() <= mHomeIcon.getRight())  
  4.       && (event .getY() <=  this .getHeight()) ){  
  5.         return  RESV_KEY_HOME;  
  6.     }  
  7.     else   if (  ( event .getX() >= mBackIcon.getLeft())  
  8.       && (event .getY() <=  this .getHeight()) ){  
  9.         return  RESV_KEY_BACK;  
  10.     }  
  11.     else   if (  ( event .getX() >= mMenuIcon.getLeft())  
  12.       && (event .getY() <=  this .getHeight()) ){  
  13.         return  RESV_KEY_MENU;  
  14.     }else   
  15.         return  -1;  
  16. }  
  17.   
  18. private   int  updateResvKeyIcon( int  state,  int  key)  
  19. {  
  20.     if (key == RESV_KEY_BACK){  
  21.         if (state == MotionEvent.ACTION_UP){  
  22.             mBackIcon.setImageResource(com.android.internal .R.drawable.stat_back);  
  23.         }else   if (state == MotionEvent.ACTION_DOWN){  
  24.             mBackIcon.setImageResource(com.android.internal .R.drawable.stat_back_pressed);  
  25.         }  
  26.     }else   if (key == RESV_KEY_HOME){  
  27.         if (state == MotionEvent.ACTION_UP){  
  28.             mHomeIcon.setImageResource(com.android.internal .R.drawable.stat_home);  
  29.         }else   if (state == MotionEvent.ACTION_DOWN){  
  30.             mHomeIcon.setImageResource(com.android.internal .R.drawable.stat_home_pressed);  
  31.         }  
  32.     }else   if (key == RESV_KEY_MENU){  
  33.         if (state == MotionEvent.ACTION_UP){  
  34.             mMenuIcon.setImageResource(com.android.internal .R.drawable.stat_menu);  
  35.         }else   if (state == MotionEvent.ACTION_DOWN){  
  36.             mMenuIcon.setImageResource(com.android.internal .R.drawable.stat_menu_pressed);  
  37.         }  
  38.     }  
  39.       
  40.     return  0;  
  41. }  

 

同时,我不想再在按下这些icon的时候,触发下拉动作,我也改了 onInterceptTouchEvent函数:

  1.   @Override   
  2.  public   boolean  onInterceptTouchEvent(MotionEvent event) {  
  3.     if (  (event.getX() > mHomeIcon.getRight())  
  4.  &&  (event.getX() < mMenuIcon.getLeft())){  
  5.         return  mService.interceptTouchEvent(event)  
  6.              ? true  :  super .onInterceptTouchEvent(event);  
  7.     }  
  8.   
  9.     return   false ;  
  10. }  

 

 

 

再编译一下,看一下结果 : 是不是能动了?

 

三,添加相应事件

 

1. 添加新的intent

首先是新增一条 intent framework/base/core/java/android/content/intent.java 中增加

 

 

 

  1. @SdkConstant (SdkConstantType.BROADCAST_INTENT_ACTION)  
  2. public   static   final  String ACTION_ICONKEY_CHANGED =  "android.intent.action.ICONKEY_CHANGED" ;  

  

 

2. 发送intent 

  在StatusBarView.java的OnKeyEvent中,松开按键的分支else if(mResvKeyState == MotionEvent.ACTION_UP)操作中加入发送intent的动作:

  1. Intent intent =  new  Intent(Intent.ACTION_ICONKEY_CHANGED);  
  2. intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);  
  3. intent.putExtra("keycode" ,   mResvKeyCode);  
  4. mService.sendIntent(intent);  

这 个intent是只有注册的接收者才能接收。

这里,我们是通过StatusBarService来发送这个intent的。

在StatusBarService.java中新增一个方法:

  1. void  sendIntent(Intent intent)  
  2. {  
  3.     mContext.sendBroadcast(intent);  
  4. }  

3.接收并处理intent

这个就要修改StatusBarPolicy.java了

首先,在构造函数中加入Intent的filter,注册号这个intent的receiver。

  filter.addAction(Intent.ACTION_ICONKEY_CHANGED);

然后再private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() 加入Intent的receiver动作;

  1. else   if  (action.equals(Intent.ACTION_ICONKEY_CHANGED)) {  
  2. G, "Received ACTION_ICONKEY_CHANGED" );  
  3.     updateIconKeyAction(intent);  
  4. }  

方法updateIconKeyAction的定义如下:

 

  1. private   final   void  updateIconKeyAction(Intent intent){  
  2.     int      keycode = intent.getIntExtra( "keycode" , - 1 );  
  3.   
  4.     if (keycode != - 1 ){  
  5.         long  now = SystemClock.uptimeMillis();  
  6.   
  7.         try  {  
  8.             KeyEvent down = new  KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode,  0 );  
  9.             KeyEvent up = new  KeyEvent(now, now, KeyEvent.ACTION_UP, keycode,  0 );  
  10.             (IWindowManager.Stub  
  11.                 .asInterface(ServiceManager.getService("window" )))  
  12.                 .injectKeyEvent(down, false );  
  13.             (IWindowManager.Stub  
  14.                 .asInterface(ServiceManager.getService("window" )))  
  15.                 .injectKeyEvent(up, false );  
  16.         } catch  (RemoteException e) {  
  17.             Log.i("Input" "DeadOjbectException" );  
  18.         }  
  19.               
  20.     }  
  21. }  

这样,基本上就完成了。

编译一下, 由于新增了一个intent,因此要先make update-api,

  1. ~/donut$ source ./env.sh  
  2. ~/donut$ make update-api  
  3. ~/donut$ make –j8  
  4. ~/donut$ emulator –skin WVGA800  

另外,如果不是做phone,也可以在StatusBarPolicy.java中将所有phone相关的处理都删掉。

 

 

http://blog.csdn.net/freshui/archive/2010/07/15/5738115.aspx

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics