二级菜单UI:
SettingsActivity类:
private SwitchBar mSwitchBar;
protected void onCreate(Bundle savedState) {
...
setContentView(R.layout.settings_main_prefs);//加载布局
...
mSwitchBar = findViewById(R.id.switch_bar);//开关 switchbar
...
}
settings_main_prefs内容如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="match_parent" android:layout_width="match_parent"> <!--开关--> <com.android.settings.widget.SwitchBar android:id="@+id/switch_bar" android:layout_height="?android:attr/actionBarSize" android:layout_width="match_parent" android:theme="?attr/switchBarTheme"/> <FrameLayout android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <RelativeLayout android:id="@+id/button_bar" android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" android:visibility="gone"> <Button android:id="@+id/back_button" android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" android:layout_alignParentStart="true" android:text="@*android:string/back_button_label"/> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true"> <Button android:id="@+id/skip_button" android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" android:text="@*android:string/skip_button_label" android:visibility="gone"/> <Button android:id="@+id/next_button" android:layout_width="150dip" android:layout_height="wrap_content" android:layout_margin="5dip" android:text="@*android:string/next_button_label"/> </LinearLayout> </RelativeLayout> </LinearLayout>
SwitchBar类:
定义了一个接口
public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener {
public interface OnSwitchChangeListener {
void onSwitchChanged(Switch switchView, boolean isChecked);
}
蓝牙的入口是放在《已连接设备》菜单里的:
connected_devices_advanced.xml:
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="connected_devices_advanced_screen"
android:title="@string/connected_device_connections_title">
<Preference
android:fragment="com.android.settings.connecteddevice.BluetoothDashboardFragment"
android:key="bluetooth_settings"
android:title="@string/bluetooth_settings_title"
android:icon="@*android:drawable/ic_settings_bluetooth"
android:order="-9"
settings:searchable="false"/>
<Preference
android:fragment="com.mediatek.nfc.NfcSettings"
android:key="toggle_mtk_nfc"
android:title="@string/nfc_quick_toggle_title"
android:icon="@drawable/ic_nfc"
android:order="-6"/>
<SwitchPreference
android:key="toggle_nfc"
android:title="@string/nfc_quick_toggle_title"
android:icon="@drawable/ic_nfc"
android:summary="@string/nfc_quick_toggle_summary"
settings:controller="com.android.settings.nfc.NfcPreferenceController"
android:order="-7"/>
<com.android.settingslib.RestrictedPreference
android:fragment="com.android.settings.nfc.AndroidBeam"
android:key="android_beam_settings"
android:title="@string/android_beam_settings_title"
settings:controller="com.android.settings.nfc.AndroidBeamPreferenceController"
android:icon="@drawable/ic_android"
android:order="-6"/>
<SwitchPreference
android:key="nfc_secure_settings"
android:title="@string/nfc_secure_settings_title"
settings:controller="com.android.settings.nfc.SecureNfcPreferenceController"
android:icon="@drawable/ic_nfc"
android:summary="@string/nfc_secure_toggle_summary"
android:order="-7"/>
<com.android.settingslib.RestrictedPreference
android:key="connected_device_printing"
android:title="@string/print_settings"
android:summary="@string/summary_placeholder"
android:icon="@*android:drawable/ic_settings_print"
android:fragment="com.android.settings.print.PrintSettingsFragment"
android:order="-3"
settings:searchable="false"/>
<Preference
android:key="bt_received_files"
android:icon="@drawable/ic_folder_vd_theme_24"
android:title="@string/bluetooth_show_files_received_via_bluetooth"/>
<PreferenceCategory
android:key="dashboard_tile_placeholder"
android:order="-8"/>
</PreferenceScreen>
对应的fragment是BluetoothDashboardFragment。
BluetoothDashboardFragment类:
public class BluetoothDashboardFragment extends DashboardFragment { ... private SwitchBar mSwitchBar; private BluetoothSwitchPreferenceController mController;
...
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
SettingsActivity activity = (SettingsActivity) getActivity();
mSwitchBar = activity.getSwitchBar();
mController = new BluetoothSwitchPreferenceController(activity,
new SwitchBarController(mSwitchBar), mFooterPreference);
...
}
...
BluetoothDashboardFragment有一个成员变量是BluetoothSwitchPreferenceController,这个BluetoothSwitchPreferenceController的构造函数有一个SwitchBarController,SwitchBarController的构造函数又需要一个mSwitchBar,这里的mSwitchBar可以看出来,就是SettingsActivity的SwitchBar。
BluetoothSwitchPreferenceController类:
public class BluetoothSwitchPreferenceController
implements LifecycleObserver, OnStart, OnStop,
SwitchWidgetController.OnSwitchChangeListener, View.OnClickListener {
BluetoothSwitchPreferenceController实现了SwitchWidgetController.OnSwitchChangeListener的接口
public abstract class SwitchWidgetController { protected OnSwitchChangeListener mListener; ... public interface OnSwitchChangeListener { ... boolean onSwitchToggled(boolean isChecked); }
...
@VisibleForTesting
public BluetoothSwitchPreferenceController(Context context, RestrictionUtils restrictionUtils,
SwitchWidgetController switchController, FooterPreference footerPreference) {
mRestrictionUtils = restrictionUtils;
mSwitch = switchController;
mContext = context;
mFooterPreference = footerPreference;
mSwitch.setupView();
updateText(mSwitch.isChecked());
mBluetoothEnabler = new BluetoothEnabler(context,
switchController,
FeatureFactory.getFactory(context).getMetricsFeatureProvider(),
SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE,
mRestrictionUtils);
mBluetoothEnabler.setToggleCallback(this);
}
而SwitchWidgetController类是一个抽象类,而且它还定义了一个接口OnSwitchChangeListener,接口只有一个onSwitchToggled的方法。而我们的BluetoothSwitchPreferenceController实现了这个接口方法。这个onSwitchToggled在什么时候调用?
再看BluetoothSwitchPreferenceController的构造函数,它创建了一个BluetoothEnabler的对象,把从BluetoothDashboardFragment传过来的switchController做为参数。并将自己通过mBluetoothEnabler.setToggleCallback(this);设置进去。
BluetoothEnabler类:
public final class BluetoothEnabler implements SwitchWidgetController.OnSwitchChangeListener {
...
SwitchWidgetController mSwitchController;
public BluetoothEnabler(Context context, SwitchWidgetController switchController,
MetricsFeatureProvider metricsFeatureProvider, int metricsEvent,
RestrictionUtils restrictionUtils) {
mContext = context;
mMetricsFeatureProvider = metricsFeatureProvider;
mSwitchController = switchController;
mSwitchController.setListener(this);
...
public void setToggleCallback(SwitchWidgetController.OnSwitchChangeListener listener) {
mCallback = listener;
}
...
BluetoothEnabler也实现了SwitchWidgetController.OnSwitchChangeListener接口方法onSwitchToggled,它又把自己设置成了
mSwitchController的listener。
SwitchBarController类:
public class SwitchBarController extends SwitchWidgetController implements SwitchBar.OnSwitchChangeListener { private final SwitchBar mSwitchBar;
...
@Override public void onSwitchChanged(Switch switchView, boolean isChecked) { if (mListener != null) { mListener.onSwitchToggled(isChecked); } }
...
当switch的状态发生变化时,会调用监听者的onSwitchToggled函数,也就是BluetoothEnabler自己。
BluetoothEnabler的onSwitchToggled函数如下:
@Override public boolean onSwitchToggled(boolean isChecked) { if (maybeEnforceRestrictions()) { triggerParentPreferenceCallback(isChecked); return true; } Log.d(TAG, "onSwitchChanged to " + isChecked); // Show toast message if Bluetooth is not allowed in airplane mode if (isChecked && !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) { Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show(); // Reset switch to off mSwitchController.setChecked(false); triggerParentPreferenceCallback(false); return false; } mMetricsFeatureProvider.action(mContext, mMetricsEvent, isChecked); if (mBluetoothAdapter != null) { boolean status = setBluetoothEnabled(isChecked); // If we cannot toggle it ON then reset the UI assets: // a) The switch should be OFF but it should still be togglable (enabled = True) // b) The switch bar should have OFF text. if (isChecked && !status) { mSwitchController.setChecked(false); mSwitchController.setEnabled(true); mSwitchController.updateTitle(false); triggerParentPreferenceCallback(false); return false; } } mSwitchController.setEnabled(false); triggerParentPreferenceCallback(isChecked); return true; }
通过mSwitchController去更新一些状态和显示,而mSwitchController又通过自己的成员变量
mSwitchBar去更新,这个mSwitchBar就是从BluetoothDashboardFragment传过来的,也就是SettingsActivity的SwitchBar。
BluetoothEnabler还调用triggerParentPreferenceCallback去调用mCallback的onSwitchToggled:
private void triggerParentPreferenceCallback(boolean isChecked) {
if (mCallback != null) { mCallback.onSwitchToggled(isChecked); }
}
而这个mCallback就是通过 mBluetoothEnabler.setToggleCallback(this);设置的BluetoothSwitchPreferenceController,它的onSwitchToggled函数是这样的:
@Override public boolean onSwitchToggled(boolean isChecked) { updateText(isChecked); return true; }
只是去更新mFooterPreference的文字而已。