由于完全改了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

    ~/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;):

     

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

    ImageView mHomeIcon; ImageView mBackIcon; 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;;

    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()中,获得实际的对象:

    1. mHomeIcon = (ImageView)findViewById(R.id.status_home);  
    2. mBackIcon = (ImageView)findViewById(R.id.status_back);  
    3. 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中来判断。

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

    首先增加两个状态成员:

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

    int mResvKeyState = -1; //记住的上次按键状态, -1为无状态。 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. // 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. 1;  
    38. 1;  
    39.             }  
    40. else if(mResvKeyState == MotionEvent.ACTION_UP){  
    41.             updateResvKeyIcon(mResvKeyState, mResvKeyCode);  
    42. 1;  
    43. 1;  
    44. else{  
    45. "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. internal.R.drawable.stat_back);  
    23. else if(state == MotionEvent.ACTION_DOWN){  
    24. internal.R.drawable.stat_back_pressed);  
    25.         }  
    26. else if(key == RESV_KEY_HOME){  
    27. if(state == MotionEvent.ACTION_UP){  
    28. internal.R.drawable.stat_home);  
    29. else if(state == MotionEvent.ACTION_DOWN){  
    30. internal.R.drawable.stat_home_pressed);  
    31.         }  
    32. else if(key == RESV_KEY_MENU){  
    33. if(state == MotionEvent.ACTION_UP){  
    34. internal.R.drawable.stat_menu);  
    35. else if(state == MotionEvent.ACTION_DOWN){  
    36. internal.R.drawable.stat_menu_pressed);  
    37.         }  
    38.     }  
    39.       
    40. return 0;  
    41. }

     

     

     

    R.drawable.stat_menu_pressed来索引就可以了。

    同时,我不想再在按下这些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中增加

     

     

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

    添加hide 标志,表示是系统内部intent,不向外公布  不需要make -api

     如果不想进行make -api

    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);


    在窗帘下拉时, ANR。针对测试结果,此时将back键转为home键来操作。


                   

    Intent intent = new Intent(Intent.ACTION_ICONKEY_CHANGED);
                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);            
                    if(mService.mExpanded == true){
                        switch(mResvKeyCode){
                        case KeyEvent.KEYCODE_BACK:
                            mResvKeyCode = KeyEvent.KEYCODE_HOME;
                            intent.putExtra("keycode",   mResvKeyCode);        
                            mService.sendIntent(intent);
                            break;
                        case KeyEvent.KEYCODE_MENU:
                            break;
                        case KeyEvent.KEYCODE_HOME:
                            intent.putExtra("keycode",   mResvKeyCode);        
                            mService.sendIntent(intent);
                        default:
                            break;
                        }    
                    } else {
                        intent.putExtra("keycode",   mResvKeyCode);
                        mService.sendIntent(intent);
                    }

                    

     

     

     

     

     

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

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

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

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

    void sendIntent(Intent intent) { mContext.sendBroadcast(intent); }

    注:
    2.3中有所更改:
      

    public void sendIntent(Intent intent) {
            // TODO Auto-generated method stub
            StatusBarService.this.sendBroadcast(intent);
        }

    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. new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0);  
    9. new KeyEvent(now, now, KeyEvent.ACTION_UP, keycode, 0);  
    10.             (IWindowManager.Stub  
    11. "window")))  
    12. false);  
    13.             (IWindowManager.Stub  
    14. "window")))  
    15. false);  
    16. catch (RemoteException e) {  
    17. "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

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

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