由于完全改了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相关的处理都删掉。