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

来源:互联网 发布:圣诞老人 知乎 编辑:程序博客网 时间:2024/05/01 14:27

需要说明的是:刚入手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

 

view plaincopy to clipboardprint?
01.<LinearLayout android:id="@+id/icons" 
02.    android:layout_width="fill_parent" 
03.    android:layout_height="fill_parent" 
04.    android:orientation="horizontal"> 
05. 
06.          <ImageView android:id="@+id/status_home" 
07.                 android:layout_width="wrap_content" 
08.                 android:layout_height="wrap_content" 
09.                 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> 
    <LinearLayout android:id="@+id/icons"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal">

              <ImageView android:id="@+id/status_home"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_gravity="top"
                     android:paddingTop="6dip"
                     android:paddingRight="10dip"
                     android:paddingLeft="10dip"
                     android:src="@drawable/stat_home" />

        <com.android.server.status.IconMerger android:id="@+id/notificationIcons"
            android:layout_width="0dip"
            android:layout_weight="1"
            android:layout_height="fill_parent"
            android:layout_alignParentLeft="true"
            android:paddingLeft="6dip"
            android:gravity="center_vertical"
            android:orientation="horizontal"/> 
           
        <LinearLayout android:id="@+id/statusIcons"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:layout_alignParentRight="true"
            android:paddingRight="6dip"
            android:gravity="center_vertical"
            android:orientation="horizontal"/>   

              <ImageView android:id="@+id/status_menu"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_gravity="top"
                     android:paddingTop="6dip"
                     android:paddingLeft="10dip"
                     android:paddingRight="10dip"
                     android:src="@drawable/stat_menu" />

              <ImageView android:id="@+id/status_back"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_gravity="top"
                     android:paddingTop="6dip"
                     android:paddingRight="10dip"
                     android:paddingLeft="10dip"
                     android:src="@drawable/stat_back" />

</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值,不过我觉得没必要,改了反倒不好看了 :)

 

编译运行一下:

 

view plaincopy to clipboardprint?
01.~/donut$ source ./env.sh  
02.~/donut$ make –j8  
03.~/donut$ emulator –skin WVGA800 
~/donut$ source ./env.sh
~/donut$ make –j8
~/donut$ emulator –skin WVGA800
 

, 看状态栏是不是改变了?

 

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

 

1. 获取statusbar中新增加的icon的handler。
 
在类中新增加三个成员(这需要import android.widget.ImageView;):

 view plaincopy to clipboardprint?
01.ImageView mHomeIcon;  
02.ImageView mBackIcon;  
03.ImageView mMenuIcon; 
 ImageView mHomeIcon;
 ImageView mBackIcon;
 ImageView mMenuIcon;
      

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

 
view plaincopy to clipboardprint?
01.public static final int RESV_KEY_HOME = KeyEvent.KEYCODE_HOME;  
02.public static final int RESV_KEY_BACK = KeyEvent.KEYCODE_BACK;  
03.public static final int RESV_KEY_MENU = KeyEvent.KEYCODE_MENU;; 
 public static final int RESV_KEY_HOME = KeyEvent.KEYCODE_HOME;
 public static final int RESV_KEY_BACK = KeyEvent.KEYCODE_BACK;
 public static final int RESV_KEY_MENU = KeyEvent.KEYCODE_MENU;;
   

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

view plaincopy to clipboardprint?
01.mHomeIcon = (ImageView)findViewById(R.id.status_home);  
02.mBackIcon = (ImageView)findViewById(R.id.status_back);  
03.mMenuIcon = (ImageView)findViewById(R.id.status_menu); 
  mHomeIcon = (ImageView)findViewById(R.id.status_home);
  mBackIcon = (ImageView)findViewById(R.id.status_back);
  mMenuIcon = (ImageView)findViewById(R.id.status_menu);
 

 

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

 

2. 添加触摸处理。

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

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

首先增加两个状态成员:

view plaincopy to clipboardprint?
01.int mResvKeyState = -1;  //记住的上次按键状态, -1为无状态。  
02.int mResvKeyCode  = -1;  //记住的上次按键值,-1为无状态。 
 int mResvKeyState = -1;  //记住的上次按键状态, -1为无状态。
 int mResvKeyCode  = -1;  //记住的上次按键值,-1为无状态。
            

 

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

view plaincopy to clipboardprint?
01.@Override 
02.   public boolean onTouchEvent(MotionEvent event) {  
03.    if(mService.mExpanded==true || mService.mTracking==true){  
04.       if (event.getAction() != MotionEvent.ACTION_DOWN) {  
05.           mService.interceptTouchEvent(event);  
06.       }  
07.        return true;  
08.    }  
09. 
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.   } 
 @Override
    public boolean onTouchEvent(MotionEvent event) {
  if(mService.mExpanded==true || mService.mTracking==true){
        if (event.getAction() != MotionEvent.ACTION_DOWN) {
            mService.interceptTouchEvent(event);
        }
         return true;
  }

  if(mResvKeyState == -1) // remembered key state, no reserve
  {
   switch(getResvKeyArea(event)){
    case RESV_KEY_HOME:
    case RESV_KEY_BACK:
    case RESV_KEY_MENU:
    {
     mResvKeyState = event.getAction();
     mResvKeyCode  = getResvKeyArea(event);

     updateResvKeyIcon(mResvKeyState, mResvKeyCode);
    }
    break;
    
    default:
          if (event.getAction() != MotionEvent.ACTION_DOWN) {
              mService.interceptTouchEvent(event);
          }
   }
  }else{
   mResvKeyState = event.getAction(); // new state
   
   if(mResvKeyState == MotionEvent.ACTION_MOVE){
    if(mResvKeyCode != getResvKeyArea(event)){
     /* out of bound, resume the icon */
     updateResvKeyIcon(MotionEvent.ACTION_UP, mResvKeyCode);
     
     mResvKeyCode  = -1;
     mResvKeyState = -1;
    }
   }else if(mResvKeyState == MotionEvent.ACTION_UP){
    updateResvKeyIcon(mResvKeyState, mResvKeyCode);
    mResvKeyCode  = -1;
    mResvKeyState = -1;
   }else{
    Log.d(TAG, "state machine error! Never be here!");
   }
  }
  
        return true;
    }

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

view plaincopy to clipboardprint?
01.private int getResvKeyArea(MotionEvent event)  
02.{  
03.    if(  (event.getX() <= mHomeIcon.getRight())  
04.      && (event.getY() <= this.getHeight()) ){  
05.        return RESV_KEY_HOME;  
06.    }  
07.    else if(  (event.getX() >= mBackIcon.getLeft())  
08.      && (event.getY() <= this.getHeight()) ){  
09.        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.} 
 private int getResvKeyArea(MotionEvent event)
 {
  if(  (event.getX() <= mHomeIcon.getRight())
    && (event.getY() <= this.getHeight()) ){
   return RESV_KEY_HOME;
  }
  else if(  (event.getX() >= mBackIcon.getLeft())
    && (event.getY() <= this.getHeight()) ){
   return RESV_KEY_BACK;
  }
  else if(  (event.getX() >= mMenuIcon.getLeft())
    && (event.getY() <= this.getHeight()) ){
   return RESV_KEY_MENU;
  }else
   return -1;
 }

 private int updateResvKeyIcon(int state, int key)
 {
  if(key == RESV_KEY_BACK){
   if(state == MotionEvent.ACTION_UP){
    mBackIcon.setImageResource(com.android.internal.R.drawable.stat_back);
   }else if(state == MotionEvent.ACTION_DOWN){
    mBackIcon.setImageResource(com.android.internal.R.drawable.stat_back_pressed);
   }
  }else if(key == RESV_KEY_HOME){
   if(state == MotionEvent.ACTION_UP){
    mHomeIcon.setImageResource(com.android.internal.R.drawable.stat_home);
   }else if(state == MotionEvent.ACTION_DOWN){
    mHomeIcon.setImageResource(com.android.internal.R.drawable.stat_home_pressed);
   }
  }else if(key == RESV_KEY_MENU){
   if(state == MotionEvent.ACTION_UP){
    mMenuIcon.setImageResource(com.android.internal.R.drawable.stat_menu);
   }else if(state == MotionEvent.ACTION_DOWN){
    mMenuIcon.setImageResource(com.android.internal.R.drawable.stat_menu_pressed);
   }
  }
  
  return 0;
 }

 

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

view plaincopy to clipboardprint?
01. @Override 
02. public boolean onInterceptTouchEvent(MotionEvent event) {  
03.    if(  (event.getX() > mHomeIcon.getRight())  
04. &&  (event.getX() < mMenuIcon.getLeft())){  
05.        return mService.interceptTouchEvent(event)  
06.             ? true : super.onInterceptTouchEvent(event);  
07.    }  
08. 
09.    return false;  
10.} 
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
     if(  (event.getX() > mHomeIcon.getRight())
    &&  (event.getX() < mMenuIcon.getLeft())){
         return mService.interceptTouchEvent(event)
                ? true : super.onInterceptTouchEvent(event);
      }

      return false;
   }
 

 

 

 

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

 

 

三,添加相应事件
 

1. 添加新的intent
首先是新增一条intent, 在framework/base/core/java/android/content/intent.java中增加

 

 

 

view plaincopy to clipboardprint?
01.@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)  
02.public static final String ACTION_ICONKEY_CHANGED = "android.intent.action.ICONKEY_CHANGED";  
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_ICONKEY_CHANGED = "android.intent.action.ICONKEY_CHANGED";

 
 

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

view plaincopy to clipboardprint?
01.Intent intent = new Intent(Intent.ACTION_ICONKEY_CHANGED);  
02.intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);  
03.intent.putExtra("keycode",   mResvKeyCode);  
04.mService.sendIntent(intent); 
Intent intent = new Intent(Intent.ACTION_ICONKEY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra("keycode",   mResvKeyCode);
mService.sendIntent(intent);
 

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

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

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

view plaincopy to clipboardprint?
01.void sendIntent(Intent intent)  
02.{  
03.    mContext.sendBroadcast(intent);  
04.} 
    void sendIntent(Intent intent)
    {
     mContext.sendBroadcast(intent);
    }
 

3.接收并处理intent
这个就要修改StatusBarPolicy.java了

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

  filter.addAction(Intent.ACTION_ICONKEY_CHANGED);
 

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

view plaincopy to clipboardprint?
01.else if (action.equals(Intent.ACTION_ICONKEY_CHANGED)) {  
02.G, "Received ACTION_ICONKEY_CHANGED");  
03.    updateIconKeyAction(intent);  
04.} 
            else if (action.equals(Intent.ACTION_ICONKEY_CHANGED)) {
    Log.d(TAG, "Received ACTION_ICONKEY_CHANGED");
                updateIconKeyAction(intent);
            }


方法updateIconKeyAction的定义如下:

 
view plaincopy to clipboardprint?
01.private final void updateIconKeyAction(Intent intent){  
02.    int     keycode = intent.getIntExtra("keycode", -1);  
03. 
04.    if(keycode != -1){  
05.        long now = SystemClock.uptimeMillis();  
06. 
07.        try {  
08.            KeyEvent down = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0);  
09.            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.} 
 private final void updateIconKeyAction(Intent intent){
  int     keycode = intent.getIntExtra("keycode", -1);

  if(keycode != -1){
         long now = SystemClock.uptimeMillis();

         try {
             KeyEvent down = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0);
             KeyEvent up = new KeyEvent(now, now, KeyEvent.ACTION_UP, keycode, 0);
             (IWindowManager.Stub
                 .asInterface(ServiceManager.getService("window")))
                 .injectKeyEvent(down, false);
             (IWindowManager.Stub
                 .asInterface(ServiceManager.getService("window")))
                 .injectKeyEvent(up, false);
         } catch (RemoteException e) {
             Log.i("Input", "DeadOjbectException");
         }
    
  }
 }

 

这样,基本上就完成了。

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

view plaincopy to clipboardprint?
01.~/donut$ source ./env.sh  
02.~/donut$ make update-api  
03.~/donut$ make –j8  
04.~/donut$ emulator –skin WVGA800 
~/donut$ source ./env.sh
~/donut$ make update-api
~/donut$ make –j8
~/donut$ emulator –skin WVGA800
 

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

 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/freshui/archive/2010/07/15/5738115.aspx

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/freshui/archive/2010/07/15/5738115.aspx