Android systemUI移植

SystemUI

Android4.3或以前版本,SystemUI是分为TabletPhone两种不同代码文件的,在Android4.4已经整合了phonetablet,统一为phone,只区分phonetv对于Phone来说SystemUI指的是:StatusBar(状态栏)NavigationBar(导航栏)

启动后Phone界面上的信号,蓝牙标志,Wifi标志等等这些状态显示标志都会在StatusBar上显示。当我们的设备开机后,首先需要给用户呈现的就是各种界面同时也包括了我们的SystemUI,因此对于整个Android系统来说,SystemUI都有举足轻重的作用。

StatusBarNavigationBar都是在

/SourceCode/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.javastart()方法中,完成初始化并显示到界面上的。

 

1.分辨率

适配不同分辨率需要修改的文件主要有:navigation_bar.xml、以及values-swXXXdp下的dimens.xml

1.1. navigation_bar.xml(导航栏)

只要修改几个虚拟按键的大小和边距,sw600dp上面的布局:android:layout_width="128dp" android:paddingLeft="25dp" android:paddingRight="25dp",因为默认的导航栏是水平放置,所以使用的边距是paddingLeft,如果是竖直放置就用paddingTop

现在有几种分辨率就有几个navigation_bar.xml,在修改增加分辨率的时候会很麻烦,实际上可以用dimen来给这些大小和边距布局,例如:android:layout_width="@dimen/virtualButtonWidth" android:paddingLeft="@dimen/virtualButtonpaddingLeft" android:paddingRight="@dimen/virtualButtonpaddingRight"

然后在value-swXXXdp目录下的dimen.xml文件下定义对应的值,例如<dimen name="virtualButtonWidth">128dp</dimen>等。以后在增加修改分辨率的时候,就不需要在navigation_bar.xml里面一个一个地修改了。实际上除了navigation_bar.xml的布局,其他布局文件都使用了dimen来定义大小。

Android系统默认在600dp以上分辨率才会显示导航栏,可以在PhoneWindowManager.java中修改mNavigationBarCanMove = shortSizeDp < 600mNavigationBarCanMove = shortSizeDp < 480,那么就能在480dp上显示导航栏。

1.2. values-swXXXdp

目前dimens.xml中主要定义了通知栏和快捷设置的(notification_panel)的布局大小:

1.notification_panel_width:定义了通知栏宽度和快捷设置的宽度,快捷设置里面的每一个小块会根据总宽度平均分成三小块;

2.quick_settings_cell_height:定义每个快捷设置的高度,原生代码中,这个dimen放在value文件下,在移植4.3的时候因为有sw480dp需求所以在value-swXXXdp目录的dimens.xml也增加了这个dimen,而4.4是没有添加的,为了方便适配不同分辨率,建议在value-swXXXdp增加。

2.增加虚拟按键

这些按键其实是button,是android原生自带的,我们可以通过config控制其开和关。对应的config项是:"config_showNavigationBar".隐藏就设成false

我们在原生的基础上增加了音量-/+和截屏按键。增加虚拟按键时,在对应分辨率的navigation_bar.xml中<LinearLayout android:id="@+id/nav_buttons"> </LinearLayout>布局里面按照按键的位置增加对应的

<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/nav_button"控件。<LinearLayout/>外面还有一层<FrameLayout/>布局,分两种情况:<FrameLayout android:id="@+id/rot0"和<FrameLayout android:id="@+id/rot90",rot90也就是旋转90°的布局。每个分辨率还有一个对应竖屏时的layout-XXXdp-port,这里面也有一个navigation_bar.xml,这里的navigation_bar.xml跟横屏刚好在<FrameLayout android:id="@+id/rot0"和<FrameLayout android:id="@+id/rot90"是反过来的。如果需要在横屏和竖屏之间显示不同的虚拟按键,可以在对应方向的navigation_bar.xml中把控件的android:layout_width="128dp" android:paddingLeft="25dp" android:paddingRight="25dp"都改为0。目前在600dp的分辨率下面,竖屏是不显示音量-/+两个虚拟按钮的,所以在layout-swXXXdp-port目录下id/volume_downid/volume_up的大小和边距都是0

 

在编写虚拟按钮的时候,如果按键是实现系统按钮功能(例如,volume_downvolume_up),可以直接增加systemui:keyCode属性,那么该虚拟按钮就会默认实现keyCode的功能,不需要在Java代码增加按钮点击事件。例如:

返回键:systemui:keyCode="4"

Home键:systemui:keyCode="3"

音量-键:systemui:keyCode="25"

音量+键:systemui:keyCode="24" 

 

增加虚拟按键布局之后,可以在NavigationBarView.java添加判断时候显示的语句,以及在PhoneStatusBar.java代码中添加初始化语句和对应按键事件逻辑。

 

对于截屏的方法,我们使用的是原生的实现方法takeScreenshot(),位于PhoneWindowManager.java代码中,该功能是通过音量+键 + 电源键实现的。

 

3.导航栏位置

修改导航栏位置是之前的一个需求提出的,将导航栏放置在屏幕右侧,方便右手操作。

1.修改navigation_bar.xml

导航栏实现竖直放置,首先要把navigation_bar.xml中的<LinearLayout       android:id="@+id/nav_buttons">android:orientation属性改为"vertical"。根据屏幕大小设置导航栏里面每个虚拟按键的大小和边距,因为竖直放置,所以虚拟按键的大小要改为宽度是自适应最大android:layout_width="match_parent",高度根据分辨率设置合适的值 android:layout_height="XXdp";没有水平方向的边距,只有竖直方向上的android:paddingTop="XXdp" android:paddingBottom="XXdp"

2.修改导航栏宽度

因为竖直放置所以导航栏的高度没有用了,只需要考虑宽度,同样这个宽度也是根据分辨率和UI布局考虑的,可以修改dimens.xml<dimen name="navigation_bar_width">XXdp</dimen>

3.修改代码显示在右侧

PhoneWindowManager.java中有把导航栏显示在右侧的代码,不过原生只支持手机显示右侧导航栏。代码段:

 mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);

if (mNavigationBarOnBottom) {

}else{

// Landscape screen; nav bar goes to the right.

    ......

          ......

}

if里面代码是把导航栏放在底部,else里面的就是把导航栏放置在右侧。只要保证只执行else里面的代码,就能够把导航栏显示在右侧。

3.1.可选的导航栏位置

目前导航栏都是放置在底部,如果把它放在右侧用户可能会不习惯,是否考虑增加设置选,选择导航栏的位置。目前的大概思路:

1. 增加不同navigation_bar.xml布局文件,navigation_bar_bottom.xmlnavigation_bar_left.xml

2. 增加Settings选项;

3. PhoneWindowManager.java提供把导航栏放置不同位置的接口;

4. PhoneStatusBarView.java增加设置选项监听,加载不同布局,调用PhoneWindowManager提供的接口重新放置导航栏。

           

4.增加新状态

首先在status_bar.xml中增加新状态的布局,如果图标就添加<ImageView/>,并在drawable目录下放入对应的图标资源,如果是文字就添加<TextView/>

然后,如果是系统自带的服务通知,只需要在PhoneStatusBarPolicy.javafilter.addAction()添加对应的Intent参数,并在private BroadcastReceiver mIntentReceiver添加显示图标的逻辑,在获取服务通知之后就能够更新状态了。例如,增加耳机插拔状态:

1.增加广播接收事件:

public PhoneStatusBarPolicy(Context context) {

......

   filter.addAction(Intent.ACTION_HEADSET_PLUG);

   ......

}

2.广播接收处理

 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {

   ......

   else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {

updateHeadset(intent);

}

}

3.更新状态

private final void updateHeadset(Intent intent) {

    int state = intent.getIntExtra("state" , 0);

    mService.setIconVisibility("headset", (mHeadsetState!=0) ? true : false );

}

但是,如果是非系统自带的服务通知,那么还需要增加自定义服务文件XXXService注册到SystemServer中广播服务状态,以及定义Intentaction,接收对应广播事件。

\statusbar\policy\目录下有一些系统自带的状态控制代码,如BatteryBTWifi等,如果是修改这些状态就可以直接修改这部分代码。

 

5.添加电量百分比显示

分别在各个精度分辨率目录下放有两类图片,一类是充电时显示的,命名为stat_sys_battery_charge_animxx.png;一类是普通情况下显示的,命名为stat_sys_battery_xx.png

一共分了0152843577185100等级的图片,其实是按照最大电量级别来分的,最大电量分别为4153549607590100,比如电量为17,则电量图标显示的是stat_sys_battery_charge_anim28/stat_sys_battery_28图片,图片旁边的数值按照实时电量以百分比形式显示。电量数值的位置和显示形式都是可以根据需要进行修改。

电量数据的接收,在src/com/android/systemui/statusbar/policy/BatteryController.java里,Intent.ACTION_BATTERY_CHANGED  是电量变化的广播。

Status 是电池的状态:

BATTERY_STATUS_UNKNOWN 未知、BATTERY_STATUS_CHARGING 表示充电、BATTERY_STATUS_FULL 满电

 

final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)   //获取当前电量

由于图片分为充电时和不充电时两类不同的情况,则需要通过plugged的truefalse来判断是否在充电来选择要显示的图片:

final int icon = plugged ? R.drawable.stat_sys_battery_charge  //充电的

               : R.drawable.stat_sys_battery;  //不充电的

电量数值的格式可以通过此处修改:

 <string name="status_bar_settings_battery_meter_format" translatable="false">

        <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g>

</string>

 

控制显示在:

/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java

加上

mBatteryController.addLabelView((TextView)mStatusBarView.findViewById(R.id.battery_text))

6.增加QuickSetting

首先,添加新设置的状态图标和文字说明,例如自动旋屏和锁定旋屏的图标和文字。

然后,修改QuickSettingsModel.java的代码,这里大致分两种情况:第一种新的快捷设置直接更新状态的(例如屏幕自动旋转),第二种点击之后跳转到新界面进行设置的。

1.快捷设置直接更新状态

需要在QuickSettingsModel类中实现新快捷设置的状态回调接口,然后添加一个继承State的状态类XXXState用于更新QuickSettings界面的变化。最后实现onXXXStateChanged回调方法,其中完成XXXState状态更新和UI更新。

接着,在QuickSettings.java中把新的快捷设置增加到QuickSettings菜单中。

例如自动旋转屏幕:

1) 实现RotationLockControllerCallback接口:

class QuickSettingsModel implements RotationLockControllerCallback

2) 定义状态类:

public static class RotationLockState extends State

3) 实现onRotationLockStateChanged方法:

@Override

    public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {

        mRotationLockState.visible = affordanceVisible;

        mRotationLockState.enabled = rotationLocked;

        mRotationLockState.iconId = rotationLocked

                ? R.drawable.ic_qs_rotation_locked

                : R.drawable.ic_qs_auto_rotate;

        mRotationLockState.label = rotationLocked

                ? mContext.getString(R.string.quick_settings_rotation_locked_label)

                : mContext.getString(R.string.quick_settings_rotation_unlocked_label);

        mRotationLockCallback.refreshView(mRotationLockTile, mRotationLockState);

    }

4) 添加到快捷菜单:

mModel.addRotationLockTile(rotationLockTile, mRotationLockController,

new QuickSettingsModel.RefreshCallback() {

@Override

        public void refreshView(QuickSettingsTileView view, State state) {

        }

    });

parent.addView(rotationLockTile);

 

2.点击之后跳转到新界面

QuickSettingsModel类添加addXXXTile方法回去回调RefreshCallback,在QuickSettings.java中把新的快捷设置增加到QuickSettings菜单中,并实现onclick()方法。在onclick()中可以实现跳转Activity以及弹出设置对话框,详见QuickSettings中的SETTINGSBRIGHTNESS

你可能感兴趣的:(UI,android,System,移植,SystemUI)