学android已经数月了,最近跳槽了一家做手机的公司,一去就是改framework层里的代码,那个纠结啊。第一个项目就是改statusbar,添加控制面板开关。
因为项目公司里任何的资源都带不出来,就用我自己的手机截图示范,跟android原生statusbar对比,源码主要以wifi为示例。
我做的项目是添加WIFI,BLUETOOTH,GPS,SOUND,自动旋转,个人感觉自动旋转是最让人纠结的,因为无法监听到状态的广播,不过可以监听数据库里的变化。我主要以WIFI项目示范,其他的四个提下我的思路,欢迎大家探讨。
修改的图片文件位置:
platform\frameworks\base\packages\SystemUI\res\drawable-hdpi
添加switch_wifi_on.png,switch_wifi_off.png,switch_ind_on.png,switch_ind_off.png,swith_bg
修改的layout文件位置:
platform\frameworks\base\packages\SystemUI\res\layout\status_bar_expanded.xml
修改的string文件位置:
中文
platform\frameworks\base\packages\SystemUI\res\values-zh-rCN\strings.xml
英文
platform\frameworks\base\packages\SystemUI\res\values\strings.xml
代码文件位置:
platform\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\StatusBarService.java
在status_bar_expanded.xml里添加5个开关的排列,添加在最顶层的android:background="@drawable/title_bar_portrait"的这个LinearLayout的上面,那样控制面板就会显示在最顶层了,大家看下源码就知道了,下面添加的是wifi的,添加其他的开关继续在里面添加即可。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/switch_bg">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/switch_wifi"
>
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/switch_wifi_img"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/switch_wifi_text"/>
<ImageView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/switch_ind_state"/>
</LinearLayout>
</LinearLayout>
功能控制添加在StatusBarService.xml文件里:
定义变量:
private WifiManager mWifiManager;
private IntentFilter mWifiStateFilter;
LinearLayout mWifiState;
ImageView mWifiImg;
TextView mWifiText;
ImageView mWifiInd;
在private void makeStatusBarView(Context context)定义这些变量的值及要用的广播等。
mWifiState=(LinearLayout)expanded.findViewById(R.id.switch_wifi);
mWifiState.setOnClickListener(mWifiChangeListener);
mWifiImg=(ImageView)expanded.findViewById(R.id.switch_wifi_img);
mWifiText=(TextView)expanded.findViewById(R.id.switch_wifi_text);
mWifiInd=(ImageView)expanded.findViewById(R.id.switch_ind_state);
mWifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
mWifiStateFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
registerReceiver(mWifiStateReceiver, mWifiStateFilter);
然后在其他位置添加广播,定义的5个状态的样式及点击切换等。
private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
handleWifiStateChanged(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
mWifiManager.getWifiState()));
}
}
};
private void handleWifiStateChanged(int wifiState) {
switch(wifiState){
case WifiManager.WIFI_STATE_DISABLED:
mWifiImg.setBackgroundResource(R.drawable.switch_wifi_off);
mWifiText.setText(R.string.switch_wifi_text_off);
mWifiInd.setBackgroundResource(R.drawable.switch_ind_off);
break;
case WifiManager.WIFI_STATE_DISABLING:
mWifiImg.setBackgroundResource(R.drawable.switch_wifi_on);
mWifiText.setText(R.string.switch_wifi_text_closing);
mWifiInd.setBackgroundResource(R.drawable.switch_ind_on);
break;
case WifiManager.WIFI_STATE_ENABLED:
mWifiImg.setBackgroundResource(R.drawable.switch_wifi_on);
mWifiText.setText(R.string.switch_wifi_text_on);
mWifiInd.setBackgroundResource(R.drawable.switch_ind_on);
break;
case WifiManager.WIFI_STATE_ENABLING:
mWifiImg.setBackgroundResource(R.drawable.switch_wifi_off);
mWifiText.setText(R.string.switch_wifi_text_opening);
mWifiInd.setBackgroundResource(R.drawable.switch_ind_off);
break;
case WifiManager.WIFI_STATE_UNKNOWN:
mWifiImg.setBackgroundResource(R.drawable.switch_wifi_off);
mWifiText.setText(R.string.switch_wifi_text_off);
mWifiInd.setBackgroundResource(R.drawable.switch_wifi_off);
break;
}
}
private View.OnClickListener mWifiChangeListener = new View.OnClickListener() {
public void onClick(View v) {
if(mWifiManager.getWifiState()==WifiManager.WIFI_STATE_DISABLED){
mWifiManager.setWifiEnabled(true);
handleWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
}else{
mWifiManager.setWifiEnabled(false);
handleWifiStateChanged(WifiManager.WIFI_STATE_DISABLED);
}
}
};
在strings.xml文件里添加
<string name="switch_wifi_text_on">wifi</string>
<string name="switch_wifi_text_off">wifi</string>
<string name="switch_wifi_text_opening">opening</string>
<string name="switch_wifi_text_closing">closing</string>
就这样一个wifi的控制开关就完成了。因为是在家里写的,所以完整的代码无法打出来,就当给大家点提示吧。
wifi的源文件可以参考:
platform\packages\apps\Settings\src\com\android\settings\wifi
在widget里已经有一个范例了,大家可以参考里面的广播。
platform\packages\apps\Settings\src\com\android\settings\widget\SettingsAppWidgetProvider.java
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String action = intent.getAction();
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
sWifiState.onActualStateChange(context, intent);
} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
sBluetoothState.onActualStateChange(context, intent);
} else if (LocationManager.PROVIDERS_CHANGED_ACTION.equals(action)) {
sGpsState.onActualStateChange(context, intent);
} else if (SyncStorageEngine.SYNC_CONNECTION_SETTING_CHANGED_INTENT.getAction()
.equals(action)) {
sSyncState.onActualStateChange(context, intent);
} else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
Uri data = intent.getData();
int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
if (buttonId == BUTTON_WIFI) {
sWifiState.toggleState(context);
} else if (buttonId == BUTTON_BRIGHTNESS) {
toggleBrightness(context);
} else if (buttonId == BUTTON_SYNC) {
sSyncState.toggleState(context);
} else if (buttonId == BUTTON_GPS) {
sGpsState.toggleState(context);
} else if (buttonId == BUTTON_BLUETOOTH) {
sBluetoothState.toggleState(context);
}
} else {
// Don't fall-through to updating the widget. The Intent
// was something unrelated or that our super class took
// care of.
return;
}
// State changes fall through
updateWidget(context);
}
bluetooth也是一样的注册广播,监听变化。
BluetoothAdapter,主要是这个类的相关应用。
private void handleStateChanged(int state) {
switch (state) {
case BluetoothAdapter.STATE_TURNING_ON:
mCheckBox.setSummary(R.string.wifi_starting);
mCheckBox.setEnabled(false);
break;
case BluetoothAdapter.STATE_ON:
mCheckBox.setChecked(true);
mCheckBox.setSummary(null);
mCheckBox.setEnabled(true);
break;
case BluetoothAdapter.STATE_TURNING_OFF:
mCheckBox.setSummary(R.string.wifi_stopping);
mCheckBox.setEnabled(false);
break;
case BluetoothAdapter.STATE_OFF:
mCheckBox.setChecked(false);
mCheckBox.setSummary(mOriginalSummary);
mCheckBox.setEnabled(true);
break;
default:
mCheckBox.setChecked(false);
mCheckBox.setSummary(R.string.wifi_error);
mCheckBox.setEnabled(true);
}
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
handleBluetoothStateChanged(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, mBluetoothAdapter.getState()););
}
};
GPS则是监听数据库里的开关变化
参考写法:
platform\packages\apps\Settings\src\com\android\settings\widget\SettingsAppWidgetProvider.java
private static final class GpsStateTracker extends StateTracker {
public int getButtonId() { return R.id.img_gps; }
public int getIndicatorId() { return R.id.ind_gps; }
public int getButtonImageId(boolean on) {
return on ? R.drawable.ic_appwidget_settings_gps_on
: R.drawable.ic_appwidget_settings_gps_off;
}
@Override
public int getActualState(Context context) {
ContentResolver resolver = context.getContentResolver();
boolean on = Settings.Secure.isLocationProviderEnabled(
resolver, LocationManager.GPS_PROVIDER);
return on ? STATE_ENABLED : STATE_DISABLED;
}
@Override
public void onActualStateChange(Context context, Intent unused) {
// Note: the broadcast location providers changed intent
// doesn't include an extras bundles saying what the new value is.
setCurrentState(context, getActualState(context));
}
@Override
public void requestStateChange(final Context context, final boolean desiredState) {
final ContentResolver resolver = context.getContentResolver();
new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... args) {
Settings.Secure.setLocationProviderEnabled(
resolver,
LocationManager.GPS_PROVIDER,
desiredState);
return desiredState;
}
@Override
protected void onPostExecute(Boolean result) {
setCurrentState(
context,
result ? STATE_ENABLED : STATE_DISABLED);
updateWidget(context);
}
}.execute();
}
}
sound则是参考
platform\packages\apps\Settings\src\com\android\settings\SoundSettings.java
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
updateState(false);
}
}
};
IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
registerReceiver(mReceiver, filter);
自动选择则是参考:
platform\packages\apps\Settings\src\com\android\settings\DisplaySettings.java
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mAccelerometer) {
//设置数据库里自动选择的开关,1为开,0为关
Settings.System.putInt(getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION,
mAccelerometer.isChecked() ? 1 : 0);
}
return true;
}
写一个类继承于ContentResolver,用于监听数据库里的变化,然后即可。