布局图如下:
1.ActionBar:Switch
在Android4.0中,在主界面Settings中定义了很多内部子空类,例如:
public static class BluetoothSettingsActivity extends Settings
在Mainifest.xml中,配置了一些对应的fragment。例如:
<activityandroid:name="Settings$BluetoothSettingsActivity" android:label="@string/bluetooth_settings_title" android:clearTaskOnLaunch="true"> <intent-filter> <actionandroid:name="android.intent.action.MAIN" /> <action android:name="android.settings.BLUETOOTH_SETTINGS"/> <categoryandroid:name="android.intent.category.VOICE_LAUNCH" /> <category android:name="com.android.settings.SHORTCUT"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> <meta-dataandroid:name="com.android.settings.FRAGMENT_CLASS" android:value="com.android.settings.bluetooth.BluetoothSettings"/> <meta-dataandroid:name="com.android.settings.TOP_LEVEL_HEADER_ID" android:resource="@id/bluetooth_settings" /> </activity>
可以看到他对应的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<CachedBluetoothDevice> 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界面大概就是这样。