正規(guī)網(wǎng)店代運(yùn)營公司seo每日
Harmony Ble 藍(lán)牙App (一)掃描
- 前言
- 正文
- 一、創(chuàng)建工程
- 二、工程配置
- ① 權(quán)限配置
- ② Debug配置
- ③ UI配置
- 三、掃描
- ① 掃描接口
- ② 掃描類
- 四、業(yè)務(wù)處理
- ① Slice的生命周期
- ② 藍(lán)牙開關(guān)和動態(tài)權(quán)限請求
- 五、掃描設(shè)備
- 六、顯示設(shè)備
- ① 自定義藍(lán)牙類
- ② 提供者
- ③ 顯示設(shè)備
- 七、源碼
前言
??關(guān)于Android的低功耗藍(lán)牙,我做了很多介紹了,那么對于Harmony來說這一塊我沒有做過介紹,而實(shí)際中我確實(shí)做過一個Harmony的BLE項(xiàng)目,所以這里分享一些內(nèi)容出來。
正文
??在Harmony中進(jìn)行Ble的藍(lán)牙開發(fā)實(shí)際上和Android中類似,但是又有一些不同,因?yàn)镠armony的SDK還在不斷的完善。而這里我們使用的是API 6進(jìn)行項(xiàng)目開發(fā),使用的語言是Java,至于為什么使用API 6而不是最新的API 9,因?yàn)槲屹I不起遙遙領(lǐng)先,所以只能用API 6的HUAWEI P30進(jìn)行真機(jī)測試。藍(lán)牙這種APP一定是要使用真機(jī)測試的,你用虛擬機(jī)是不行的,話不多說,我們開始吧。
一、創(chuàng)建工程
??下面開始創(chuàng)建工程。
選擇Empty Ability
,點(diǎn)擊Next。我們創(chuàng)建一個名為HarmonyBle
的項(xiàng)目,語言為Java。
點(diǎn)擊Finish
完成創(chuàng)建。
默認(rèn)的工程就是這個樣子的,是不是很像Android創(chuàng)建的工程呢?
二、工程配置
① 權(quán)限配置
??Harmony中同樣有權(quán)限這個概念,也需要配置靜態(tài)權(quán)限和動態(tài)權(quán)限,只不過配置靜態(tài)權(quán)限的地方不一樣。Harmony是在config.json
中,里面的代碼如下:
{"app": {"bundleName": "com.llw.ble","vendor": "example","version": {"code": 1000000,"name": "1.0.0"}},"deviceConfig": {},"module": {"package": "com.llw.ble","name": ".MyApplication","mainAbility": "com.llw.ble.MainAbility","deviceType": ["phone","tablet","tv","wearable","car"],"distro": {"deliveryWithInstall": true,"moduleName": "entry","moduleType": "entry","installationFree": false},"abilities": [{"skills": [{"entities": ["entity.system.home"],"actions": ["action.system.home"]}],"name": "com.llw.ble.MainAbility","description": "$string:mainability_description","icon": "$media:icon","label": "$string:entry_MainAbility","launchType": "standard","orientation": "unspecified","visible": true,"type": "page"}]}
}
??你閱讀一下,你就會發(fā)現(xiàn),這和Android的AndroidManifest.xml
配置文件好像差不多啊。只不過一個用的是json,一個用的是xml。
??所以我們配置權(quán)限也是在config.json
中,例如掃描藍(lán)牙時我們需要定位權(quán)限??梢栽诶锩嬖黾尤缦麓a:
"reqPermissions": [{"name": "ohos.permission.LOCATION"},{"name": "ohos.permission.USE_BLUETOOTH"},{"name": "ohos.permission.DISCOVER_BLUETOOTH"},{"name": "ohos.permission.MANAGE_BLUETOOTH"}]
如下圖所示,注意json中標(biāo)點(diǎn)符號。
② Debug配置
??然后我們就應(yīng)該要來寫代碼了,不過在此之前,我們先了解一下Ability和Slice的區(qū)別,Ability就像一個畫框,而Slice就像一個畫布。我們可以在一個畫框里面加載多個畫布,就好像多個頁面之前的跳轉(zhuǎn),我們可以用Slice來進(jìn)行,下面我們增加一個掃描的Slice,我們復(fù)制一下MainAbilitySlice,再粘貼一下,出現(xiàn)的彈窗中改名字
??為什么要通過這種方式來創(chuàng)建Java文件呢?因?yàn)镈evEco Studio我創(chuàng)建不了Java文件,可能是這個版本的DS沒有這個選項(xiàng)亦或是我沒有找到。
??下面我們需要創(chuàng)建對應(yīng)layout文件,再resources/base/layout下創(chuàng)建一個slice_scan.xml,代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:alignment="center"ohos:orientation="vertical"><ListContainerohos:id="$+id:lc_device"ohos:height="match_parent"ohos:width="match_parent"/></DirectionalLayout>
??然后我們再修改ScanSlice中的內(nèi)容,讓它加載我們剛寫好的slice_scan.xml。修改onStart()方法,代碼如下所示:
@Overridepublic void onStart(Intent intent) {super.onStart(intent);super.setUIContent(ResourceTable.Layout_slice_scan);}
現(xiàn)在App打開之后默認(rèn)會運(yùn)行MainAbility,我們看一下這個里面。
public class MainAbility extends Ability {@Overridepublic void onStart(Intent intent) {super.onStart(intent);super.setMainRoute(MainAbilitySlice.class.getName());}
}
??可以看到,在setMainRoute()方法中,默認(rèn)加載的是MainAbilitySlice
,將它改為我們剛寫好的ScanSlice
,代碼:super.setMainRoute(ScanSlice.class.getName());
然后我們先運(yùn)行一下看看,通過USB鏈接到鴻蒙手機(jī)上。
這里會提示報錯,瀏覽一下錯誤信息。
??這里是說我們需要配置Signing。點(diǎn)擊Run下面或者右側(cè)彈窗的Open signing configs
,會打開一個配置窗口,如下圖所示:
我們點(diǎn)擊Signing Configs
選項(xiàng),需要你進(jìn)行登錄,如下圖所示:
??這里就需要你登錄華為的帳號了,我們當(dāng)前在本地運(yùn)行所以是Debug模式,旁邊有一個Release表示發(fā)布版本,它里面配置的東西和Debug模式一致,區(qū)別在于Debug模式下的配置信息只要我們登錄之后,DevEco Studio會幫助我們自動生成,而Release中的信息則需要開發(fā)者去華為開發(fā)者官網(wǎng)上去創(chuàng)建應(yīng)用并申請配置文件和證書,比較麻煩,但是如果你要上架應(yīng)用則必須做這一步,在國內(nèi),華為應(yīng)用市場上架應(yīng)用是最嚴(yán)格的。華為的你搞得定,其他的都是小趴菜,不值一提。
??下面我們先登錄,會打開一個網(wǎng)頁,登錄成功之后,你會看到這樣的頁面。
??然后我們回到DS,就會自動配置Debug模式下的證書和配置文件,如下圖所示:
??點(diǎn)擊OK,會在DS中進(jìn)行一個配置,配置好之后你可以在工程目錄下的build.gradle
中看到debug的相關(guān)信息,如下圖所示。
然后我們再運(yùn)行一下看看,這一次毫無疑問是可以運(yùn)行成功的。如下圖所示:
③ UI配置
可以看到默認(rèn)的標(biāo)題欄就如同Android
默認(rèn)的ActionBar
,丑的很特別,我們?nèi)サ羲?#xff0c;在config.json中添加如下代碼:
"metaData": {"customizeData": [{"extra": "","name": "hwc-theme","value": "androidhwext:style/Theme.Emui.Light.NoTitleBar"}]},
添加位置如下所示:
下面我們再運(yùn)行一下看看。
是不是Unbelievable!
同樣為了標(biāo)題好看,我們在element下創(chuàng)建一個color.json
,里面的代碼如下所示:
{"color": [{"name": "white","value": "#FFF"},{"name": "black","value": "#000"},{"name": "blue","value": "#FFA7D3FF"},{"name": "bg_color","value": "#F8F8F8"},{"name": "gray","value": "#989898"}]
}
我們再修改一下scan_slice.xml中的代碼,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:alignment="center"ohos:background_element="$color:bg_color"ohos:orientation="vertical"><DirectionalLayoutohos:height="50vp"ohos:width="match_parent"ohos:alignment="vertical_center"ohos:background_element="$color:blue"ohos:orientation="horizontal"ohos:start_padding="12vp"><Textohos:id="$+id:title"ohos:height="match_content"ohos:width="match_content"ohos:text="選擇設(shè)備"ohos:text_color="#FFF"ohos:text_font="HwChinese-medium"ohos:text_size="18fp"ohos:weight="1"/><Textohos:id="$+id:tx_scan_status"ohos:height="match_content"ohos:width="match_content"ohos:end_margin="6vp"ohos:padding="8vp"ohos:text="搜索"ohos:text_color="#FFF"ohos:text_size="14fp"/></DirectionalLayout><ListContainerohos:id="$+id:lc_device"ohos:height="match_parent"ohos:width="match_parent"/></DirectionalLayout>
這個DirectionalLayout
布局就是線性布局,我們可以點(diǎn)擊右側(cè)導(dǎo)航欄的Previewer
進(jìn)行布局預(yù)覽,如下圖所示。
右上角的T圖標(biāo),點(diǎn)擊之后可以查看當(dāng)前布局的層級。
??這里說明一下,有時候在通過資源使用顏色值的時候會無法生效,所以就會直接使用#FFF
,在代碼里也是如此,這應(yīng)該屬于編譯器的Bug。
標(biāo)題欄就寫好了,還有狀態(tài)欄我們沒有改,狀態(tài)欄我們在MainAbility中進(jìn)行修改,代碼如下所示:
@Overridepublic void onStart(Intent intent) {Window window = WindowManager.getInstance().getTopWindow().get();window.setStatusBarColor(Color.getIntColor("#A7D3FF"));super.onStart(intent);super.setMainRoute(ScanSlice.class.getName());}
還是修改onStart()
方法,然后我們運(yùn)行一下看看。
好了,下面我們來寫掃描需要的內(nèi)容代碼。
三、掃描
??首先我們在com.llw.ble
包下新建一個core
包,core
包下創(chuàng)建一個BleCore
類,這里面就是控制Ble藍(lán)牙相關(guān)的一切,比如掃描,連接,讀寫數(shù)據(jù)等操作,我們先不寫代碼。下面在core
包下創(chuàng)建一個scan
包。
① 掃描接口
scan
包下新建一個ScanCallback
接口,代碼如下:
public interface ScanCallback {void onScanResult(BleScanResult result);default void onGroupScanResultsEvent(List<BleScanResult> results){}default void onScanFailed(String failed){}
}
② 掃描類
然后在scan
包下新建一個BleScan類,代碼如下所示:
public class BleScan {private final BleCentralManager centralManager;private boolean isScanning = false;private ScanCallback scanCallback;// 創(chuàng)建掃描過濾器然后開始掃描private List<BleScanFilter> filters;private static volatile BleScan mInstance;//初始化public static BleScan getInstance(Context context) {if (mInstance == null) {synchronized (BleScan.class) {if (mInstance == null) {mInstance = new BleScan(context);}}}return mInstance;}public BleScan(Context context) {BleScanCallback centralManagerCallback = new BleScanCallback();centralManager = new BleCentralManager(context, centralManagerCallback);}/*** 當(dāng)前是否正在掃描* @return true 掃描中,false 未掃描*/public boolean isScanning() {return isScanning;}/*** 設(shè)置過濾信息* @param filters 藍(lán)牙掃描過濾列表*/public void setFilters(List<BleScanFilter> filters) {this.filters = filters;}/*** 設(shè)置掃描回調(diào),頁面需要實(shí)現(xiàn)才能獲取掃描到的設(shè)備* @param scanCallback 掃描回調(diào)*/public void setScanCallback(ScanCallback scanCallback) {this.scanCallback = scanCallback;}/*** 開始掃描*/public void startScan() {if (centralManager == null) {localScanFailed("Bluetooth not turned on.");return;}centralManager.startScan(filters);isScanning = true;}/*** 停止掃描*/public void stopScan() {if (!isScanning) {localScanFailed("Not currently scanning, your stop has no effect.");return;}centralManager.stopScan();isScanning = false;}/*** 實(shí)現(xiàn)掃描回調(diào)*/public class BleScanCallback implements BleCentralManagerCallback {@Overridepublic void scanResultEvent(BleScanResult bleScanResult) {if (scanCallback != null) {scanCallback.onScanResult(bleScanResult);}}@Overridepublic void scanFailedEvent(int resultCode) {if (scanCallback != null) {scanCallback.onScanFailed(String.valueOf(resultCode));}}@Overridepublic void groupScanResultsEvent(final List<BleScanResult> scanResults) {// 對掃描結(jié)果進(jìn)行處理if (scanCallback != null) {scanCallback.onGroupScanResultsEvent(scanResults);}}}/*** 本地掃描失敗處理* @param failed 錯誤信息*/private void localScanFailed(String failed) {if (scanCallback != null) {scanCallback.onScanFailed(failed);}}
}
??這里面采用單例模式,在初始化之后直接調(diào)用,然后再實(shí)現(xiàn)掃描回調(diào)接口,返回掃描信息,有開始、停止掃描和是否正在掃描方法。這個類你可以直接用,也可以再封裝到BleCore中,這里我們封裝到BleCore中,修改BleCore中的代碼,如下所示:
public class BleCore {private static volatile BleCore mInstance;private final BleScan bleScan;public BleCore(Context context) {//藍(lán)牙掃描bleScan = BleScan.getInstance(context);}public static BleCore getInstance(Context context) {if (mInstance == null) {synchronized (BleCore.class) {if (mInstance == null) {mInstance = new BleCore(context);}}}return mInstance;}public void setPhyScanCallback(ScanCallback scanCallback) {bleScan.setScanCallback(scanCallback);}public boolean isScanning() {return bleScan.isScanning();}public void startScan() {bleScan.startScan();}public void stopScan() {bleScan.stopScan();}
}
四、業(yè)務(wù)處理
??這里的業(yè)務(wù)處理主要是兩個,第一個是藍(lán)牙開關(guān)監(jiān)聽,第二個動態(tài)權(quán)限申請。
再進(jìn)行業(yè)務(wù)處理之前,我們先修改一下MyApplication類的名字,修改為BleApp,修改后再改動里面的代碼,如下所示:
public class BleApp extends AbilityPackage {private static BleCore bleCore;@Overridepublic void onInitialize() {super.onInitialize();bleCore = BleCore.getInstance(getContext());}public static BleCore getBleCore() {return bleCore;}
}
① Slice的生命周期
??首先我們來看一下Slice的生命周期,這個就比較重要,下面我們首先在com.llw.ble
下創(chuàng)建一個utils
包,utils
包下創(chuàng)建一個LogUtils類,代碼如下所示:
public class LogUtils {static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "HarmonyBle");private static HiLogLabel logLabel;public static void setLogLabel(HiLogLabel logLabel) {LogUtils.logLabel = logLabel;}public static void Log(String content) {HiLog.info(LABEL, content);}public static void LogI(String TAG, String content) {HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x00201, TAG);HiLog.info(label, content);}public static void LogD(String TAG, String content) {HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x00201, TAG);HiLog.debug(label, content);}public static void LogE(String TAG, String content) {HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x00201, TAG);HiLog.error(label, content);}public static void LogW(String TAG, String content) {HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x00201, TAG);HiLog.warn(label, content);}}
??這是因?yàn)镠armony中打印日志比較麻煩,所以寫一個工具類,封裝一下,下面我們修改一下ScanSlice
類中的代碼,如下所示:
public class ScanSlice extends AbilitySlice {private final String TAG = ScanSlice.class.getSimpleName();@Overridepublic void onStart(Intent intent) {super.onStart(intent);super.setUIContent(ResourceTable.Layout_slice_scan);LogUtils.LogD(TAG, "onStart");}@Overridepublic void onActive() {LogUtils.LogD(TAG, "onActive");}@Overrideprotected void onInactive() {LogUtils.LogD(TAG, "onInactive");}@Overridepublic void onForeground(Intent intent) {LogUtils.LogD(TAG, "onForeground");}@Overrideprotected void onBackground() {LogUtils.LogD(TAG, "onBackground");}@Overrideprotected void onStop() {LogUtils.LogD(TAG, "onStop");}
}
然后我們運(yùn)行一下看看,檢查控制臺日志:
然后我們通過Home鍵回到桌面,看看日志:
然后我們點(diǎn)擊桌面上的圖標(biāo)回到應(yīng)用中,看看日志:
再回到桌面,然后我們通過后臺的運(yùn)行程序進(jìn)入應(yīng)用,看看日志:
這兩種回到應(yīng)用的方式日志一樣,然后我們按返回鍵回到桌面,看看日志:
那么現(xiàn)在你對于Slice的生命周期就比較了解了,下面我們進(jìn)行代碼的編寫。
② 藍(lán)牙開關(guān)和動態(tài)權(quán)限請求
??首先處理藍(lán)牙相關(guān)的,在BleCore
中添加如下代碼:
private final BluetoothHost mBluetoothHost;
在構(gòu)造方法中實(shí)例化
public BleCore(Context context) {...// 獲取藍(lán)牙本機(jī)管理對象mBluetoothHost = BluetoothHost.getDefaultHost(context);}
然后我們再寫兩個方法:
public boolean isEnableBt() {return mBluetoothHost.getBtState() == BluetoothHost.STATE_ON;}public void enableBt() {mBluetoothHost.enableBt();}
??用于判斷是否打開藍(lán)牙和打開藍(lán)牙,回到ScanSlice
中我們需要使用BleCore
來處理藍(lán)牙相關(guān)的工作,代碼如下所示:
public class ScanSlice extends AbilitySlice {private final String TAG = ScanSlice.class.getSimpleName();private BleCore bleCore;private Text txScanStatus;private ListContainer lcDevice;@Overridepublic void onStart(Intent intent) {super.onStart(intent);super.setUIContent(ResourceTable.Layout_slice_scan);bleCore = BleApp.getBleCore();txScanStatus = (Text) findComponentById(ResourceTable.Id_tx_scan_status);lcDevice = (ListContainer) findComponentById(ResourceTable.Id_lc_device);//點(diǎn)擊監(jiān)聽txScanStatus.setClickedListener(component -> {});}@Overridepublic void onActive() {// 判斷是否打開藍(lán)牙if (!bleCore.isEnableBt()) {//打開藍(lán)牙bleCore.enableBt();return;}}
}
??首先在onStart()
中對BleCore進(jìn)行實(shí)例化,findComponentById
就如同findViewById
,然后在onActive()
中調(diào)用剛才我們所寫的方法。
@Overridepublic void onActive() {// 判斷是否打開藍(lán)牙if (!bleCore.isEnableBt()) {//打開藍(lán)牙bleCore.enableBt();return;}}
然后是定位權(quán)限的處理,同樣在onActive()中,增加代碼如下所示:
@Overridepublic void onActive() {// 判斷是否打開藍(lán)牙...// 是否獲取定位權(quán)限String locationPermission = "ohos.permission.LOCATION";if (verifySelfPermission(locationPermission) != IBundleManager.PERMISSION_GRANTED) {requestPermissionsFromUser(new String[]{locationPermission}, 100);return;}}
這里首先我們定義一個權(quán)限,然后判斷是否授予,沒有授予則進(jìn)行請求,下面運(yùn)行一下看看:
??那么我們就完成了藍(lán)牙打開和定位權(quán)限動態(tài)申請,你可以在運(yùn)行一次,你會發(fā)現(xiàn),你還需要請求權(quán)限的,因?yàn)镈S默認(rèn)安裝時不會保留應(yīng)用的數(shù)據(jù),而藍(lán)牙打開了屬于系統(tǒng)層面的,所以你可以不用再打開藍(lán)牙,而需要重新請求定位權(quán)限,為了避免這一點(diǎn),我們點(diǎn)擊Run→ Edit Configurations...
在彈出的窗口上勾選Keep Application Data
點(diǎn)擊OK,再運(yùn)行即可。
五、掃描設(shè)備
??接下來我們進(jìn)行掃描的處理,在ScanSlice中增加如下方法代碼:
private void startScan() {bleCore.startScan();txScanStatus.setText("停止");}private void stopScan() {bleCore.stopScan();txScanStatus.setText("搜索");}
??這里就是掃描和停止方法,同時修改一下Text文本,在onStart()
中首先實(shí)現(xiàn)掃描回調(diào)監(jiān)聽,然后處理再處理txScanStatus
文本的點(diǎn)擊事件,代碼如下所示:
@Overridepublic void onStart(Intent intent) {...bleCore.setPhyScanCallback(this);//點(diǎn)擊監(jiān)聽txScanStatus.setClickedListener(component -> {if (bleCore.isScanning()) {stopScan();//掃描開關(guān)停止掃描} else {startScan();//開始掃描}});}
這里this
會報錯,鼠標(biāo)放在上面,Alt + Enter,出現(xiàn)彈窗。
選擇最后一個,就會給你實(shí)現(xiàn)ScanCallback
中的onScanResult()
方法,代碼如下所示:
@Overridepublic void onScanResult(BleScanResult result) {LogUtils.LogD(TAG, result.getPeripheralDevice().getDeviceAddr());}
我們在里面打印一下掃描到的設(shè)備Mac地址,最后我們在onActive()
中增加如下所示代碼:
@Overridepublic void onActive() {...// 是否在掃描中if (!bleCore.isScanning()) {startScan();}}
下面運(yùn)行一下,看看控制臺日志:
掃描出來了,只不過目前還看不到,所以我們要渲染一下,讓它可以看到。
六、顯示設(shè)備
要顯示設(shè)備,首先我們需要寫一個Bean。
① 自定義藍(lán)牙類
在core包下新建一個BleDevice類,里面的代碼如下所示:
public class BleDevice {private String realName = "Unknown device"; //藍(lán)牙設(shè)備真實(shí)名稱private String macAddress; //地址private int rssi; //信號強(qiáng)度private BlePeripheralDevice device; //設(shè)備public BleDevice(BleScanResult scanResult) {this.device = scanResult.getPeripheralDevice();this.macAddress = device.getDeviceAddr();String name = device.getDeviceName().get();if (name != null || !name.isEmpty()) {this.realName = name;}this.rssi = scanResult.getRssi();}public String getRealName() {return realName;}public void setRealName(String realName) {this.realName = realName;}public String getMacAddress() {return macAddress;}public void setMacAddress(String macAddress) {this.macAddress = macAddress;}public int getRssi() {return rssi;}public void setRssi(int rssi) {this.rssi = rssi;}public BlePeripheralDevice getDevice() {return device;}public void setDevice(BlePeripheralDevice device) {this.device = device;}
}
??這個Bean沒有什么好說的,下面要做的就是列表Item的渲染,在Android中我們使用的是適配器Adapter
,而在Harmony中使用的是提供者Provider
。
② 提供者
同樣我們先寫布局,在layout下新建一個item_scan_device.xml
,代碼如下所示:
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_content"ohos:width="match_parent"ohos:alignment="vertical_center"ohos:background_element="#FFF"ohos:bottom_padding="8vp"ohos:orientation="horizontal"ohos:right_padding="16vp"ohos:top_margin="1vp"ohos:top_padding="8vp"><Imageohos:height="match_content"ohos:width="match_content"ohos:end_margin="16vp"ohos:image_src="$graphic:ic_bluetooth"ohos:start_margin="16vp"/><DirectionalLayoutohos:height="match_content"ohos:width="match_content"ohos:orientation="vertical"ohos:weight="1"><Textohos:id="$+id:device_name"ohos:height="match_content"ohos:width="match_content"ohos:text="設(shè)備名稱"ohos:text_size="16fp"/><Textohos:id="$+id:device_address"ohos:height="match_content"ohos:width="match_content"ohos:text="設(shè)備地址"ohos:text_color="$color:gray"ohos:text_size="14fp"ohos:top_margin="4vp"/></DirectionalLayout><Textohos:id="$+id:rssi"ohos:height="match_content"ohos:width="match_content"ohos:align_parent_end="true"ohos:text_color="#000000"ohos:text_size="10fp"/></DirectionalLayout>
??幾個主要內(nèi)容,設(shè)備名稱、Mac地址、Rssi信號強(qiáng)度,然后這里有一個圖標(biāo),在graphic
下創(chuàng)建一個ic_bluetooth.xml
,代碼如下所示:
<?xml version="1.0" encoding="UTF-8"?><vectorxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="48vp"ohos:width="48vp"ohos:viewportHeight="1024"ohos:viewportWidth="1024"><pathohos:fillColor="#A7D3FF"ohos:pathData="M53.31,512a458.69,458.69 0,1 1,917.38 0A458.69,458.69 0,0 1,53.31 512zM584.96,301.82a356.16,356.16 0,0 0,-39.81 -26.69c-12.1,-6.34 -32,-13.89 -52.74,-3.01 -20.48,10.82 -25.86,31.23 -27.78,44.67 -1.92,13.18 -1.92,30.21 -1.92,48.45v77.18l-57.92,-49.6a32,32 0,0 0,-41.6 48.64L445.44,512 363.2,582.4a32,32 0,1 0,41.6 48.64l57.92,-49.6v77.18c0,18.24 0,35.33 1.92,48.51 1.92,13.44 7.23,33.86 27.78,44.61 20.74,10.88 40.64,3.33 52.74,-2.94a356.48,356.48 0,0 0,39.81 -26.69l39.42,-28.8c10.62,-7.74 21.31,-15.55 29.06,-23.1 8.64,-8.58 18.56,-21.57 18.56,-40.06 0,-18.56 -9.92,-31.55 -18.56,-40.06 -7.68,-7.55 -18.43,-15.36 -29.06,-23.17L548.99,512l75.39,-54.98c10.62,-7.74 21.31,-15.55 29.06,-23.17 8.64,-8.51 18.56,-21.5 18.56,-40 0,-18.56 -9.92,-31.55 -18.56,-40.06 -7.68,-7.62 -18.43,-15.36 -29.06,-23.17l-39.42,-28.8zM526.72,367.36v64.77c0,7.36 0,11.01 2.37,12.16 2.3,1.28 5.25,-0.9 11.2,-5.25l44.8,-32.7 8.32,-6.08c3.97,-2.94 5.95,-4.42 5.95,-6.53 0,-2.18 -1.98,-3.65 -5.95,-6.53l-8.32,-6.14 -36.1,-26.3a3344.06,3344.06 0,0 0,-9.34 -6.78c-5.44,-3.97 -8.19,-5.95 -10.5,-4.8 -2.37,1.15 -2.37,4.54 -2.37,11.33v12.86zM526.72,656.45L526.72,591.74c0,-7.36 0,-11.01 2.37,-12.16 2.3,-1.22 5.25,0.96 11.2,5.25l44.8,32.7 8.32,6.14c3.97,2.88 5.95,4.35 5.95,6.53 0,2.11 -1.98,3.58 -5.95,6.53l-8.32,6.08 -36.1,26.37 -9.34,6.78c-5.44,3.97 -8.19,5.95 -10.5,4.74 -2.37,-1.15 -2.37,-4.48 -2.37,-11.33v-12.8z"></path>
</vector>
??下面我們寫提供者,在com.llw.ble下創(chuàng)建一個provider包,包下創(chuàng)建一個ScanDeviceProvider類,代碼如下所示:
public class ScanDeviceProvider extends BaseItemProvider {private final List<BleDevice> deviceList;private final AbilitySlice slice;public ScanDeviceProvider(List<BleDevice> list, AbilitySlice slice) {this.deviceList = list;this.slice = slice;}@Overridepublic int getCount() {return deviceList == null ? 0 : deviceList.size();}@Overridepublic Object getItem(int position) {if (deviceList != null && position >= 0 && position < deviceList.size()) {return deviceList.get(position);}return null;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic Component getComponent(int position, Component component, ComponentContainer componentContainer) {final Component cpt;ScanDeviceHolder holder;BleDevice device = deviceList.get(position);if (component == null) {cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_scan_device, null, false);holder = new ScanDeviceHolder(cpt);//將獲取到的子組件信息綁定到列表項(xiàng)的實(shí)例中cpt.setTag(holder);} else {cpt = component;// 從緩存中獲取到列表項(xiàng)實(shí)例后,直接使用綁定的子組件信息進(jìn)行數(shù)據(jù)填充。holder = (ScanDeviceHolder) cpt.getTag();}holder.deviceName.setText(device.getRealName());holder.deviceAddress.setText(device.getMacAddress());holder.rssi.setText(String.format(Locale.getDefault(), "%d dBm", device.getRssi()));return cpt;}/*** 用于保存列表項(xiàng)的子組件信息*/public static class ScanDeviceHolder {Text deviceName;Text deviceAddress;Text rssi;public ScanDeviceHolder(Component component) {deviceName = (Text) component.findComponentById(ResourceTable.Id_device_name);deviceAddress = (Text) component.findComponentById(ResourceTable.Id_device_address);rssi = (Text) component.findComponentById(ResourceTable.Id_rssi);}}
}
??通過提供者的代碼,可以看到它和適配器的寫法差不多,不同的是你得注意getComponent()
方法中的處理,另外提供者默認(rèn)提供了Item的點(diǎn)擊方法,所以我們不用再自己去寫了。
③ 顯示設(shè)備
??我們回到ScanSlice中使用,首先我們創(chuàng)建幾個變量,代碼如下所示:
private final List<BleDevice> mList = new ArrayList<>();private ScanDeviceProvider provider;
然后在onStart()方法中進(jìn)行初始化:
@Overridepublic void onStart(Intent intent) {...provider = new ScanDeviceProvider(mList, this);lcDevice.setItemProvider(provider);//列表item點(diǎn)擊監(jiān)聽lcDevice.setItemClickedListener((listContainer, component, position, id) -> {});}
這里設(shè)置了列表提供者,然后添加item點(diǎn)擊監(jiān)聽,最后我們在掃描回調(diào)中渲染數(shù)據(jù),修改代碼如下所示:
private int findIndex(BleDevice bleDevice, List<BleDevice> deviceList) {int index = 0;for (final BleDevice devi : deviceList) {if (bleDevice.getMacAddress().equals(devi.getDevice().getDeviceAddr())) return index;index += 1;}return -1;}@Overridepublic void onScanResult(BleScanResult result) {BleDevice bleDevice = new BleDevice(result);int index = findIndex(bleDevice, mList);if (index == -1) {//添加新設(shè)備mList.add(bleDevice);} else {//更新已有設(shè)備的rssi和時間戳mList.get(index).setRssi(bleDevice.getRssi());}getUITaskDispatcher().syncDispatch(() -> provider.notifyDataChanged());}
這里添加一個findIndex()
方法,用于添加設(shè)備和更新設(shè)備,最終通過UI線程同步刷新提供者,再修改一個開始掃描和停止掃描的方法代碼:
private void startScan() {mList.clear();provider.notifyDataChanged();bleCore.startScan();txScanStatus.setText("停止");LogUtils.LogD(TAG,"開始掃描設(shè)備!");}private void stopScan() {bleCore.stopScan();txScanStatus.setText("搜索");LogUtils.LogD(TAG,"已經(jīng)停止掃描設(shè)備!");}
運(yùn)行一下看看:
七、源碼
如果對你有所幫助的話,不妨 Star 或 Fork,山高水長,后會有期~
源碼地址:HarmonyBle-Java