布局图如下:
1.ActionBar:Switch
在Android4.0中,在主界面Settings中定义了很多内部子空类,例如:
public static class BluetoothSettingsActivity extends Settings
在Mainifest.xml中,配置了一些对应的fragment。例如:
可以看到他对应的fragment是
com.android.settings.bluetooth.BluetoothSettings
所以说我们从一级界面Settings点击相应的header跳转到BT的设置的界面的时候,实际上是启动相应的Activity加上fragment。
这 里当然是BluetoothSettingsActivity加上BluetoothSettings,对于ActionBar这部分,我们可以看到是由两部分主城,其中一部分是由icon和文字来表示当前的界面,其中icon始终没有变,而文字是要相应的 变化的,而这个文字的设置就在 Mainfest中配置的: android:label="@string/bluetooth_settings_title"。 另一部分是一个Switcher来控制蓝牙的关闭。这个switch就是一个widget,他的基本关系:public class Switch extendsCompoundButton。 Switch是在BluetoothSettings代码中添加的:
activity.getActionBar().setCustomView(actionBarSwitch,new ActionBar.LayoutParams(
ActionBar.LayoutParams.WRAP_CONTENT,
ActionBar.LayoutParams.WRAP_CONTENT,
Gravity.CENTER_VERTICAL | Gravity.RIGHT));
后面又将actionBarSwitch传入到BluetoothEnabler,通过BluetoothEnabler来控制开关的逻辑。
2.PreferenceScreen
这个就是ActionBar下面的这部分,可以从图中看到这部分主要包含了本机名字Hike706, 已配对的设备,可用设备,和一个搜索设备的action按钮。
这部分内容是要打开BT设备后,才会显示出来。
(1).Preference, 本机名
相关代码:
if (mMyDevicePreference == null) {
mMyDevicePreference = new Preference(getActivity());
}
mMyDevicePreference.setTitle(mLocalAdapter.getName());
preferenceScreen.addPreference(mMyDevicePreference);
得到本机的名字调用关系:LocalBluetoothAdapter.getName()------->BluetoothAdapter.getName()(处于Framework了,在framework会分析这部分代码)。
还有前面的那个电话图标是怎么产生的。
if(getResources().getBoolean(com.android.internal.R.bool.config_voice_capable)) {
mMyDevicePreference.setIcon(R.drawable.ic_bt_cellphone); // for phones
}else {
mMyDevicePreference.setIcon(R.drawable.ic_bt_laptop); // for tablets, etc.
}
可以看到就两个图标,一个是手机,一个是平板。
具体的是由 com.android.internal.R.bool.config_voice_capable这个确定的,存在于framework,后面分析。
(2).PreferenceCategory (已配对的设备)
首先是界面部分:
相关代码:
if(mPairedDevicesCategory == null) {
mPairedDevicesCategory = new PreferenceCategory(getActivity());
}else {
mPairedDevicesCategory.removeAll();
}
addDeviceCategory(mPairedDevicesCategory,
R.string.bluetooth_preference_paired_devices,
BluetoothDeviceFilter.BONDED_DEVICE_FILTER);
其中addDeviceCategory会将所有已配对的设备都添加进这一组:
相关代码:
preferenceGroup.setTitle(titleId);
getPreferenceScreen().addPreference(preferenceGroup);
setFilter(filter);
setDeviceListGroup(preferenceGroup);
addCachedDevices();
preferenceGroup.setEnabled(true);
可以看到除了将preferenceGroup添加到PreferenceScreen(),后面几句就是向preferenceGroup添加设备:
DeviceListPreferenceFragment:addCachedDevices();
void addCachedDevices() {
Collection cachedDevices =
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
for(CachedBluetoothDevice cachedDevice : cachedDevices) {
onDeviceAdded(cachedDevice);
}
}
但是在这个函数中
if (mDevicePreferenceMap.get(cachedDevice) != null) {
return;
}
// Preventupdates while the list shows one of the state messages
if(mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return;
if(mFilter.matches(cachedDevice.getDevice())) {
createDevicePreference(cachedDevice);
}
可以看到会根据Filter尽心过滤。在这里我们的filter是BluetoothDeviceFilter.BONDED_DEVICE_FILTER。所以只会create配对的设备。
void createDevicePreference(CachedBluetoothDevice cachedDevice) {
BluetoothDevicePreference preference = new BluetoothDevicePreference(
getActivity(),cachedDevice);
initDevicePreference(preference);
mDeviceListGroup.addPreference(preference);
mDevicePreferenceMap.put(cachedDevice, preference);
}
这个函数就是最终生成我们看到一个个已配对设备的列表。
这个函数就是遍历出cachedDevices中的元素依次添加进preferenceGroup。
cachedDevices:LocalBluetoothManager---->CachedBluetoothDeviceManager:addDevice
从这条路径来看,cachedDevices最终是通过CachedBluetoothDeviceManager添加进来的,
那addDevice什么时候调用呢?
是BluetoothEventManager接收到相应的广播后调用CachedBluetoothDeviceManager:addDevice。
相关代码:
CachedBluetoothDevice cachedDevice =mDeviceManager.findDevice(device);
if(cachedDevice == null) {
cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager,device);
Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice:"
+ cachedDevice);
//callback to UI to create Preference for new device
dispatchDeviceAdded(cachedDevice);
}
不仅将cachedDevice添加给 mDeviceManager,而且还会通知界面更新。
至于
mDeviceManager.findDevice(device);中的device是从何而来,是由接收BluetoothDevice.ACTION_BOND_STATE_CHANGED这个广播所带的信息而来。这个广播是从framework发送而来。后面framework会分析。
广播接收代码:
private finalBroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context,Intent intent) {
Log.v(TAG, "Received " + intent.getAction());
Stringaction = intent.getAction();
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Handlerhandler = mHandlerMap.get(action);
if(handler != null) {
handler.onReceive(context, intent, device);
}
}
};
可以看到device的信息:
BluetoothDevicedevice = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
(3).ProgressCategory-->ProgressCategoryBase----->PreferenceCategory.(已发现的设备)
可以看到最终也是一个PreferenceCategory, 只不过是增加了了一个“触摸可配对”的元件(在没有扫描的时候)。在扫描的时候,会将其替换掉“正在搜索”和一个旋转的ProgressBar,其他的就完全一样。
相关代码:
if (mAvailableDevicesCategory == null) {
mAvailableDevicesCategory = new ProgressCategory(getActivity(), null);
}else {
mAvailableDevicesCategory.removeAll();
}
addDeviceCategory(mAvailableDevicesCategory,
R.string.bluetooth_preference_found_devices,
BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER);
其他的流程基本和(2)的流程一样,只不过是filter换成了BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER。所以就只会添加cachedDevices中没有配对的设备到这一组来。但是当开始扫描后会不断更新这一组的列表。
对于已配对的设备和可用的设备之间界面的区别:
对于这两个界面的构造都是在BluetoothDevicePreference创建中实现的。
相关代码:
if(cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
setWidgetLayoutResource(R.layout.preference_bluetooth);
}
当处于BluetoothDevice.BOND_BONDED状态的时候会在处于没有配对状态的后面添加一个R.layout.preference_bluetooth, 这个包含了详细信息按钮。
当开始扫描后:回到BluetoothEventManager, 接收到BluetoothDevice.ACTION_FOUND的广播:
CachedBluetoothDevice cachedDevice =mDeviceManager.findDevice(device);
if(cachedDevice == null) {
cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager,device);
Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice:"
+ cachedDevice);
//callback to UI to create Preference for new device
dispatchDeviceAdded(cachedDevice);
}
synchronized (mCallbacks) {
for(BluetoothCallback callback : mCallbacks) {
callback.onDeviceAdded(cachedDevice);
}
因为在DeviceListPreferenceFragment中有注册callback,所以会调用DeviceListPreferenceFragment中的onDeviceAdded。
mLocalManager.getEventManager().registerCallback(this);。
接后面的步骤参考(2)。
(4).menus:
menu.add(Menu.NONE, MENU_ID_SCAN, 0, textId).setEnabled(bluetoothIsEnabled && !isDiscovering).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
所以他会一直作为ActionBar显示在底部。而其他的会隐藏在menu中
MENU_ID_SCAN
MENU_ID_RENAME_DEVICE
MENU_ID_VISIBILITY_TIMEOUT
MENU_ID_SHOW_RECEIVED
MENU_ID_ADVANCED_SETTING
蓝牙settings界面大概就是这样。