最近要修改Settings相关的东西,就顺便研究下Setings的代码结构,特做个记录
由于修改的是Android4.4的平台,所以以下都是在Android4.4基础上分析的。
1、入口
从packages/apps/settings/AndroidManifestxml找到Settings模块的入口:
<activity android:name="Settings"
android:label="@string/settings_label_launcher"
android:taskAffinity="com.android.settings"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.settings.SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
说明进入settings的第一个Activity为Settings.java
2、Settings extends PreferenceActivity
settings继承与preverenceActivity,所以我们要主要两个重载函数onBuildHeaders和setListAdapter
1)onBuildHeaders
onbuildHeaders负责加载xml布局文件,这里的xml文件settings_headers使用的是Preference Headers,没有使用传统的PreferenceScreen。
每个Header对应一个子界面,一般为Fragment
关于这点可以参考http://www.xuebuyuan.com/549370.html
@Override
public void onBuildHeaders(List<Header> headers) {
if (!onIsHidingHeaders()) {
loadHeadersFromResource(R.xml.settings_headers, headers);
updateHeaderList(headers);
}
}
2)setListAdapter
PreferenceActivity继承于ListActivity,所以这里可以使用需要ListView创建Adapter,这里的自定义Adapter对不同子菜单的分别处理有关键性作用
@Override public void setListAdapter(ListAdapter adapter) { if (adapter == null) { super.setListAdapter(null); } else { DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm)); } }
自定义Adapter:
private static class HeaderAdapter extends ArrayAdapter<Header> {
static final int HEADER_TYPE_CATEGORY = 0;
static final int HEADER_TYPE_NORMAL = 1;
static final int HEADER_TYPE_SWITCH = 2;
static final int HEADER_TYPE_BUTTON = 3;
private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
private final WifiEnabler mWifiEnabler;
private final BluetoothEnabler mBluetoothEnabler;
private AuthenticatorHelper mAuthHelper;
private DevicePolicyManager mDevicePolicyManager;
private static class HeaderViewHolder {
ImageView icon;
TextView title;
TextView summary;
Switch switch_;
ImageButton button_;
View divider_;
}
private LayoutInflater mInflater;
static int getHeaderType(Header header) {
if (header.fragment == null && header.intent == null) {
return HEADER_TYPE_CATEGORY;
} else if (header.id == R.id.security_settings) {
return HEADER_TYPE_BUTTON;
} else {
return HEADER_TYPE_NORMAL;
}
}
@Override
public int getItemViewType(int position) {
Header header = getItem(position);
return getHeaderType(header);
}
@Override
public boolean areAllItemsEnabled() {
return false; // because of categories
}
@Override
public boolean isEnabled(int position) {
return getItemViewType(position) != HEADER_TYPE_CATEGORY;
}
@Override
public int getViewTypeCount() {
return HEADER_TYPE_COUNT;
}
@Override
public boolean hasStableIds() {
return true;
}
public HeaderAdapter(Context context, List<Header> objects,
AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
super(context, 0, objects);
mAuthHelper = authenticatorHelper;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// Temp Switches provided as placeholder until the adapter replaces these with actual
// Switches inflated from their layouts. Must be done before adapter is set in super
mWifiEnabler = new WifiEnabler(context, new Switch(context));
mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
mDevicePolicyManager = dpm;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
HeaderViewHolder holder;
Header header = getItem(position);
int headerType = getHeaderType(header);
View view = null;
if (convertView == null) {
holder = new HeaderViewHolder();
switch (headerType) {
case HEADER_TYPE_CATEGORY:
view = new TextView(getContext(), null,
android.R.attr.listSeparatorTextViewStyle);
holder.title = (TextView) view;
break;
case HEADER_TYPE_SWITCH:
view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
false);
holder.icon = (ImageView) view.findViewById(R.id.icon);
holder.title = (TextView)
view.findViewById(com.android.internal.R.id.title);
holder.summary = (TextView)
view.findViewById(com.android.internal.R.id.summary);
holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
break;
case HEADER_TYPE_BUTTON:
view = mInflater.inflate(R.layout.preference_header_button_item, parent,
false);
holder.icon = (ImageView) view.findViewById(R.id.icon);
holder.title = (TextView)
view.findViewById(com.android.internal.R.id.title);
holder.summary = (TextView)
view.findViewById(com.android.internal.R.id.summary);
holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
holder.divider_ = view.findViewById(R.id.divider);
break;
case HEADER_TYPE_NORMAL:
view = mInflater.inflate(
R.layout.preference_header_item, parent,
false);
holder.icon = (ImageView) view.findViewById(R.id.icon);
holder.title = (TextView)
view.findViewById(com.android.internal.R.id.title);
holder.summary = (TextView)
view.findViewById(com.android.internal.R.id.summary);
break;
}
view.setTag(holder);
} else {
view = convertView;
holder = (HeaderViewHolder) view.getTag();
}
// All view fields must be updated every time, because the view may be recycled
switch (headerType) {
case HEADER_TYPE_CATEGORY:
holder.title.setText(header.getTitle(getContext().getResources()));
break;
case HEADER_TYPE_SWITCH:
// Would need a different treatment if the main menu had more switches
if (header.id == R.id.wifi_settings) {
mWifiEnabler.setSwitch(holder.switch_);
} else {
mBluetoothEnabler.setSwitch(holder.switch_);
}
updateCommonHeaderView(header, holder);
break;
case HEADER_TYPE_BUTTON:
if (header.id == R.id.security_settings) {
boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
if (hasCert) {
holder.button_.setVisibility(View.VISIBLE);
holder.divider_.setVisibility(View.VISIBLE);
boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
if (isManaged) {
holder.button_.setImageResource(R.drawable.ic_settings_about);
} else {
holder.button_.setImageResource(
android.R.drawable.stat_notify_error);
}
holder.button_.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(
android.provider.Settings.ACTION_MONITORING_CERT_INFO);
getContext().startActivity(intent);
}
});
} else {
holder.button_.setVisibility(View.GONE);
holder.divider_.setVisibility(View.GONE);
}
}
updateCommonHeaderView(header, holder);
break;
case HEADER_TYPE_NORMAL:
updateCommonHeaderView(header, holder);
break;
}
return view;
}
3、settings设置数据的保存
虽然preference是可以记录状态,并保存在sharedpreference文件中,但是有很多状态值是全局的,比如wifi,蓝牙等。所以这里涉及到全局数据的保存
以蓝牙为例,蓝牙打开关闭的操作在BluetoothEnabler.java里面
当按下蓝牙开关时,调用mLocalAdapter.setBluetoothEnabled(isChecked);–>BluetoothAdapter.enable()–>BluetoothAdapter:mManagerService.enable();–>BluetoothManagerService:enable–>BluetoothManagerService:persistBluetoothSetting
private void persistBluetoothSetting(int value) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.BLUETOOTH_ON,
value);
}
最终改的是Settings.Global.BLUETOOTH_ON的值,而这个默认值则在DatabaseHelper.java中设置
loadBooleanSetting(stmt, Settings.Global.BLUETOOTH_ON,
R.bool.def_bluetooth_on);
其他的值,比如wifi开关,默认语言等,都类似于这个过程