当前位置: 首页 > news >正文

国税局网站里打印设置如何做浏览器大全列表下载

国税局网站里打印设置如何做,浏览器大全列表下载,wordpress个人站无法升级,吃什么补肾吗Ble蓝牙App#xff08;一#xff09;扫描 前言正文一、基本配置二、扫描准备三、扫描页面① 增加UI布局② 点击监听③ 扫描处理④ 广播处理 四、权限处理五、扫描结果① 列表适配器② 扫描结果处理③ 接收结果 六、源码 前言 关于低功耗的蓝牙介绍我已经做过很多了#xff0… Ble蓝牙App一扫描 前言正文一、基本配置二、扫描准备三、扫描页面① 增加UI布局② 点击监听③ 扫描处理④ 广播处理 四、权限处理五、扫描结果① 列表适配器② 扫描结果处理③ 接收结果 六、源码 前言 关于低功耗的蓝牙介绍我已经做过很多了只不过很多人不是奔着学习的目的去的拿着源码就去运行后面又发现连接设备后马上断开然后不会自己看问题这个现象就是快餐式的你不了解里面的知识内容自然就不知道是怎么回事重复的问题我回答了好多次了。而我也是觉得写的有问题本意上来说我是希望读者可以参考来写能看一看文章内容而结果绝大多数看个标题看个运行效果下载源码就运行运行有问题就问你没有什么思考。   针对这个情况我决定做了系列性的Ble蓝牙App尽可能的避免在你运行的时候出现bug所以这是一个低功耗蓝牙工具App可以让你了解到一些东西。注意是低功耗不是经典蓝牙如果你不知道两者之间的区别建议你先了解一下。本文的效果 正文 本文将会重新创建一个项目功能一个一个的做尽量的做好每一个功能的优化下面我们创建一个名为GoodBle的项目语言为Kotlin。 至于为什么使用Kotlin稳固一下不然太久不用就会生疏文本我们讲述的是扫描你可能回想一个扫描有什么好写不就是开始、结束、显示设备嘛至于单独作为一个功能来写一篇文章嘛那么我们带着问题来看这篇文章看看扫描到底有没有必要这样来做。 一、基本配置 当前我们创建项目有一个MainActivity然后我们需要打开viewBinding的开关在app的build.gradle中的android{}闭包中添加如下代码 buildFeatures {viewBinding true}然后Sync Now同步一下开启成功。随后我们就可以在Activity中使用ViewBinding了常规的使用方式是这样的 class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBinding;override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding ActivityMainBinding.inflate(layoutInflater);setContentView(binding.root)} }在Java中封装通常采用反射的方式在Kotlin中如果要对ViewBinding进行封装的话同时利用上Kotlin的一些特性的话可以这样做原文地址如下Viewbinding使用和委托封装感觉写得蛮好的太久没用Kotlin了还是看了一会才看懂感兴趣的可以看看。 那么我们在com.llw.goodble下面创建一个base包base包下创建BaseViewBinding.kt文件里面的代码如下所示 package com.llw.goodble.baseimport android.app.Activity import android.view.LayoutInflater import androidx.viewbinding.ViewBinding import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KPropertyfun VB : ViewBinding viewBinding(viewInflater: (LayoutInflater) - VB):ReadOnlyPropertyActivity, VB ActivityViewBindingProperty(viewInflater)class ActivityViewBindingPropertyVB : ViewBinding(private val viewInflater: (LayoutInflater) - VB ) : ReadOnlyPropertyActivity, VB {private var binding: VB? nulloverride fun getValue(thisRef: Activity, property: KProperty*): VB {return binding ?: viewInflater(thisRef.layoutInflater).also {thisRef.setContentView(it.root)binding it}} }通过委托的方式进行封装下面来看在MainActivity中怎么使用它 class MainActivity : AppCompatActivity() {private val binding by viewBinding(ActivityMainBinding::inflate)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)} }使用起来很简单后面就采用这种方式你可以运行一下看看有没有问题然后我们可以再创建一个ScanActivity类用于扫描页面修改一下activity_main.xml中的代码如下所示 ?xml version1.0 encodingutf-8? androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsandroid:layout_widthmatch_parentandroid:layout_heightmatch_parenttools:context.MainActivitycom.google.android.material.appbar.MaterialToolbarandroid:idid/toolbarandroid:layout_widthmatch_parentandroid:layout_height?attr/actionBarSizeandroid:backgroundcolor/orangeapp:layout_constraintEnd_toEndOfparentapp:layout_constraintStart_toStartOfparentapp:layout_constraintTop_toTopOfparentapp:navigationIcondrawable/ic_scan_bleapp:titleGoodBleapp:titleCenteredtrueapp:titleTextColorcolor/white //androidx.constraintlayout.widget.ConstraintLayout这里用到了图标代码如下所示 vector xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:width32dpandroid:height32dpandroid:viewportWidth1024android:viewportHeight1024pathandroid:fillColor#ffffffandroid:pathDataM761.5,141.1c-14.3,-9.6 -33.6,-5.9 -43.2,8.4 -9.6,14.2 -5.9,33.6 8.4,43.2 106,71.6 169.3,190.7 169.3,318.4 0,211.7 -172.2,384 -384,384S128,722.9 128,511.1c0,-127.8 63.3,-246.8 169.3,-318.4 14.2,-9.6 18,-29 8.4,-43.2s-29,-18 -43.2,-8.4C139.3,224.4 65.7,362.7 65.7,511.1c0,246.1 200.2,446.2 446.2,446.2S958.2,757.2 958.2,511.1C958.2,362.7 884.6,224.4 761.5,141.1z /pathandroid:fillColor#ffffffandroid:pathDataM402.1,157.6c17.2,0 31.1,-13.9 31.1,-31.1L433.2,96c0,-17.2 -13.9,-31.1 -31.1,-31.1s-31.1,13.9 -31.1,31.1l0,30.4C371,143.6 384.9,157.6 402.1,157.6z /pathandroid:fillColor#ffffffandroid:pathDataM624.3,157.6c17.2,0 31.1,-13.9 31.1,-31.1L655.5,96c0,-17.2 -13.9,-31.1 -31.1,-31.1s-31.1,13.9 -31.1,31.1l0,30.4C593.2,143.6 607.1,157.6 624.3,157.6z /pathandroid:fillColor#ffffffandroid:pathDataM428.3,227.4c11.2,18 41.8,48.4 85.9,48.4 43.8,0 74.9,-30.2 86.3,-48.1 9.3,-14.5 5.1,-33.7 -9.4,-43 -14.5,-9.3 -33.7,-5 -43,9.4 -0.1,0.2 -13.3,19.4 -33.9,19.4 -19.9,0 -32.3,-18 -33.2,-19.3 -9.1,-14.4 -28.2,-18.7 -42.7,-9.7C423.7,193.6 419.2,212.8 428.3,227.4z /pathandroid:fillColor#ffffffandroid:pathDataM306,440.9c-9.2,14.5 -4.8,33.8 9.7,42.9l142.7,90.1L314.1,665.1c-14.5,9.2 -18.9,28.4 -9.7,42.9 5.9,9.4 16,14.5 26.3,14.5 5.7,0 11.4,-1.6 16.6,-4.8l135.7,-85.7 0,148c0,10.6 4,20.2 10.3,27.8 0.4,0.5 0.8,1 1.2,1.4 8.4,9.3 20.5,15.3 34.1,15.3 2.4,0 4.8,-0.3 7,-0.9 5.8,-0.9 11.4,-2.8 16.5,-5.8 0.8,-0.5 1.6,-1 2.3,-1.5l134,-96.2c12.7,-8.2 20.5,-22.2 20.6,-37.2 0,-15.5 -8.4,-30.1 -21.2,-37.7l-113,-71.4 110.6,-69.9c13.6,-8.1 22,-22.8 21.9,-38.3 -0.1,-15 -8,-29 -20.7,-37.1l-132.4,-94.4c-0.8,-0.6 -1.6,-1.1 -2.5,-1.6 -21,-12.1 -47.9,-6.1 -61.4,13.7 -2.5,3.7 -4.1,7.8 -4.8,11.9 -1.7,3.9 -2.7,8.1 -2.7,12.7l0,144.9 -134.1,-84.7C334.4,422 315.2,426.4 306,440.9zM545.3,746.4 L545.3,628.9l87.1,55L545.3,746.4zM630.7,465.1l-85.4,53.9L545.3,404.3 630.7,465.1z / /vector颜色值在colors.xml中增加 color nameorange#FF5722/colorcolor namewarm_yellow#FFC107/colorcolor namedark_orange#FF9800/colorcolor namelight_orange#FFF3E0/colorcolor namegray_white#F8F8F8/colorcolor namegray#989898/color这里给toolbar设置导航图标点击这个导航到扫描页面不过再次之前我们可以在base包下再创建一个BaseActivity这里面可以写一些常用的函数代码如下所示 open class BaseActivity : AppCompatActivity() {private var context: Context? nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)context this}protected fun jumpActivity(clazz: Class*?, finish: Boolean false) {startActivity(Intent(context, clazz))if (finish) finish()}protected fun back(toolbar: Toolbar, finish: Boolean false) toolbar.setNavigationOnClickListener { if (finish) finish() else onBackPressed() }protected fun showMsg(msg: CharSequence) Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()protected open fun isAndroid12() Build.VERSION.SDK_INT Build.VERSION_CODES.Sprotected open fun hasAccessFineLocation() hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)protected open fun hasCoarseLocation() hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION)RequiresApi(Build.VERSION_CODES.S)protected open fun hasBluetoothConnect() hasPermission(Manifest.permission.BLUETOOTH_CONNECT)RequiresApi(Build.VERSION_CODES.S)protected open fun hasBluetoothScan() hasPermission(Manifest.permission.BLUETOOTH_SCAN)/*** 检查是有拥有某权限** param permission 权限名称* return true 有 false 没有*/protected open fun hasPermission(permission: String) checkSelfPermission(permission) PackageManager.PERMISSION_GRANTED/*** 蓝牙是否打开** return true or false*/protected open fun isOpenBluetooth(): Boolean {(getSystemService(BLUETOOTH_SERVICE) as BluetoothManager).also {it.adapter ?: return falsereturn it.adapter.isEnabled}}/*** 位置是否打开*/protected open fun isOpenLocation(): Boolean {val locationManager getSystemService(LOCATION_SERVICE) as LocationManagerval gps locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)val network locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)val locationEnabled isLocationEnabled()Log.d(TAG, gps: $gpsnetwork$networklocationEnabled$locationEnabled)return gps || network || locationEnabled}open fun isLocationEnabled(): Boolean {val locationMode try {Settings.Secure.getInt(contentResolver, Settings.Secure.LOCATION_MODE)} catch (e: SettingNotFoundException) {e.printStackTrace()return false}return locationMode ! Settings.Secure.LOCATION_MODE_OFF} }这里面就是一些比较基础的方法在后面扫描页面会用到的然后再修改一下MainActivity中的代码继承BaseActivity点击中跳转扫描页面 class MainActivity : BaseActivity() {private val binding by viewBinding(ActivityMainBinding::inflate)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)binding.toolbar.setNavigationOnClickListener { jumpActivity(ScanActivity::class.java) }} }为了保持一样的UI效果下面更改一下themes.xml中的代码如下所示 resources xmlns:toolshttp://schemas.android.com/tools!-- Base application theme. --style nameTheme.GoodBle parentTheme.MaterialComponents.DayNight.NoActionBar!-- Primary brand color. --item namecolorPrimarycolor/orange/itemitem namecolorPrimaryVariantcolor/orange/itemitem namecolorOnPrimarycolor/white/item!-- Secondary brand color. --item namecolorSecondarycolor/light_orange/itemitem namecolorSecondaryVariantcolor/dark_orange/itemitem namecolorOnSecondarycolor/white/item!-- Status bar color. --item nameandroid:statusBarColor?attr/colorPrimaryVariant/item!-- Customize your theme here. --item nameandroid:windowBackgroundcolor/gray_white/item/stylestyle nameBottomSheetDialogStyle parentTheme.Design.BottomSheetDialogitem nameandroid:windowFramenull/itemitem nameandroid:windowIsFloatingtrue/itemitem nameandroid:windowIsTranslucenttrue/itemitem nameandroid:backgroundandroid:color/transparent/itemitem nameandroid:backgroundDimEnabledtrue/itemitem nameandroid:colorBackgroundandroid:color/transparent/item/style /resources主要就是修改状态栏颜色窗口默认背景颜色现在前置的条件都准备的差不多了运行一下看看MainActivity的页面效果。 二、扫描准备 下面在com.llw.goodble包下新建一个ble包里面我们需要创建一些类来处理扫描的相关事务首先在ble包下创建一个BleCore类里面先不写内容然后我们在ble包下新建一个scan包。在scan包下新建一个BleScanCallback接口这是一个扫描回调接口代码如下所示 interface BleScanCallback {/*** 扫描结果*/fun onScanResult(result: ScanResult)/*** 批量扫描结果*/fun onBatchScanResults(results: ListScanResult) {}/*** 扫描错误*/fun onScanFailed(failed: String) {} }同时在扫描页面需要监听一下蓝牙和定位是否打开在scan包下添加一个广播接收器的ReceiverCallback 接口代码如下所示 interface ReceiverCallback {/*** 蓝牙关闭*/fun bluetoothClose()/*** 位置关闭*/fun locationClose() }下面在scan创建广播接收器ScanReceiver代码如下所示 class ScanReceiver : BroadcastReceiver() {private var callback: ReceiverCallback? nullfun setCallback(callback: ReceiverCallback?) {this.callback callback}private var isSend 0override fun onReceive(context: Context, intent: Intent) {val action intent.actionif (action BluetoothAdapter.ACTION_STATE_CHANGED) {when (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {BluetoothAdapter.STATE_OFF - Log.d(TAG, STATE_OFF Phone bluetooth off)BluetoothAdapter.STATE_TURNING_OFF - {callback!!.bluetoothClose()Log.d(TAG, STATE_TURNING_OFF Phone bluetooth is turning off)}BluetoothAdapter.STATE_ON - Log.d(TAG, STATE_ON Phone bluetooth turned on)BluetoothAdapter.STATE_TURNING_ON - Log.d(TAG, STATE_TURNING_ON Phone bluetooth is on)}} else if (action LocationManager.PROVIDERS_CHANGED_ACTION) {if (!isGPSOpen(context)) {isSendif (isSend 1) {Log.d(TAG, Positioning off)callback!!.locationClose()} else if (isSend 4) {isSend 0}}}}companion object {val TAG: String ScanReceiver::class.java.simpleNamefun isGPSOpen(context: Context): Boolean {val locationMode try {Settings.Secure.getInt(context.contentResolver, Settings.Secure.LOCATION_MODE)} catch (e: SettingNotFoundException) {e.printStackTrace()return false}return locationMode ! Settings.Secure.LOCATION_MODE_OFF}} }这里的代码相对简单就是广播接收器接收相关的动作信息再进行回调然后我们写一个用于扫描类在scan包下新建一个BleScan类代码如下所示 /*** 低功耗扫描类*/ SuppressLint(MissingPermission, InlinedApi) class BleScan private constructor(private val context: Context) {private var mScanFilters: ListScanFilterprivate var mScanSettings: ScanSettingsprivate var bleScanCallback: BleScanCallback? nullvar mIsScanning falseinit {mScanFilters ArrayList()mScanSettings ScanSettings.Builder().build()}companion object {SuppressLint(StaticFieldLeak)Volatileprivate var instance: BleScan? nullprivate var mBluetoothAdapter: BluetoothAdapter? nullprivate var mScanner: BluetoothLeScanner? nullfun getInstance(context: Context) instance ?: synchronized(this) {instance ?: BleScan(context).also {instance itval manager context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManagermBluetoothAdapter manager.adapterif (mBluetoothAdapter ! null) {mScanner mBluetoothAdapter?.bluetoothLeScanner}}}}/*** 设置扫描过滤*/fun setScanFilters(scanFilters: ListScanFilter) {mScanFilters scanFilters}/*** 设置扫描设置选项*/fun setScanSettings(scanSettings: ScanSettings) {mScanSettings scanSettings}/*** 设置扫描回调*/fun setPhyScanCallback(bleScanCallback: BleScanCallback?) {this.bleScanCallback bleScanCallback}fun isScanning() mIsScanning/*** 扫描回调*/private val scanCallback: ScanCallback object : ScanCallback() {override fun onScanResult(callbackType: Int, result: ScanResult) {bleScanCallback?.onScanResult(result)}override fun onBatchScanResults(results: ListScanResult) {bleScanCallback?.onBatchScanResults(results)}override fun onScanFailed(errorCode: Int) {localScanFailed(when (errorCode) {SCAN_FAILED_ALREADY_STARTED - Fails to start scan as BLE scan with the same settings is already started by the app.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED - Fails to start scan as app cannot be registered.SCAN_FAILED_INTERNAL_ERROR - Fails to start scan due an internal errorSCAN_FAILED_FEATURE_UNSUPPORTED - Fails to start power optimized scan as this feature is not supported.else - UNKNOWN_ERROR})}}/*** 显示本地扫描错误*/private fun localScanFailed(failed: String) bleScanCallback?.onScanFailed(failed)/*** 开始扫描*/SuppressLint(MissingPermission)fun startScan() {if (!isOpenBluetooth()) {localScanFailed(Bluetooth is not turned on.)return}if (isAndroid12()) {if (!hasBluetoothScan()) {localScanFailed(Android 12 needs to dynamically request bluetooth scan permission.)return}} else {if (!hasAccessFineLocation()) {localScanFailed(Android 6 to 12 requires dynamic request location permission.)return}}if (mIsScanning) {localScanFailed(Currently scanning, please close the current scan and scan again.)return}if (mScanner null) mScanner mBluetoothAdapter?.bluetoothLeScannerif (!mBluetoothAdapter!!.isEnabled) {localScanFailed(Bluetooth not turned on.)return}mScanner?.startScan(mScanFilters, mScanSettings, scanCallback)mIsScanning true}/*** 停止扫描*/fun stopScan() {if (!mIsScanning) {localScanFailed(Not currently scanning, your stop has no effect.)return}if (mScanner null) {localScanFailed(BluetoothLeScanner is Null.)return}if (!mBluetoothAdapter!!.isEnabled) {localScanFailed(Bluetooth not turned on.)return}mIsScanning falsemScanner?.stopScan(scanCallback)}/*** 是否打开蓝牙*/private fun isOpenBluetooth() if (mBluetoothAdapter null) {localScanFailed(BluetoothAdapter is Null.); false} else mBluetoothAdapter!!.isEnabledprivate fun isAndroid12() Build.VERSION.SDK_INT Build.VERSION_CODES.Sprivate fun hasAccessFineLocation() hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)private fun hasBluetoothConnect() hasPermission(Manifest.permission.BLUETOOTH_CONNECT)private fun hasBluetoothScan() hasPermission(Manifest.permission.BLUETOOTH_SCAN)private fun hasPermission(permission: String) context.checkSelfPermission(permission) PackageManager.PERMISSION_GRANTED }这里首先是创建一个单例在里面对当前类和一些变量进行初始化核心就是一个扫描回调开始和停止扫描的方法处理。因为后面还需要写Ble相关的数据处理因此在ble包下创建一个BleCore类代码如下所示 class BleCore private constructor(private val context: Context) {SuppressLint(StaticFieldLeak)companion object {SuppressLint(StaticFieldLeak)Volatileprivate var instance: BleCore? nullSuppressLint(StaticFieldLeak)private lateinit var bleScan: BleScanfun getInstance(context: Context) instance ?: synchronized(this) {instance ?: BleCore(context).also {instance it//蓝牙扫描bleScan BleScan.getInstance(context)}}}fun setPhyScanCallback(bleScanCallback: BleScanCallback) {bleScan.setPhyScanCallback(bleScanCallback)}fun isScanning() bleScan.isScanning()fun startScan() bleScan.startScan()fun stopScan() bleScan.stopScan() }同样是一个单例在里面初始化BleScan然后增加几个函数去调用BleScan中的函数最后我们在com.llw.goodble包下创建一个BleApp类代码如下所示 class BleApp : Application() {SuppressLint(StaticFieldLeak)private lateinit var context: ContextSuppressLint(StaticFieldLeak)private lateinit var bleCore: BleCoreoverride fun onCreate() {super.onCreate()context applicationContext//初始化Ble核心库bleCore BleCore.getInstance(this)}fun getBleCore() bleCore }这里继承Application通过自定义的方式在App启动的时候加载这个类然后在onCreate()函数中完成对于Ble核心类的初始化顺便完成对于Ble扫描类的初始化。最后在AndroidManifest.xml中的application标签中配置这个BleApp如下所示 applicationandroid:name.BleApp...三、扫描页面 在Android12及以上版本使用蓝牙时需要请求扫描、连接权限、如果还需要使用手机作为从机的话就请求广播权限后面会提到的同时在低版本Android中我们扫描蓝牙请求定位权限那么首先我们就把权限的部分先做了。 首先声明静态权限在AndroidManifest.xml中增加如下代码 uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION /uses-permission android:nameandroid.permission.ACCESS_COARSE_LOCATION /uses-permission android:nameandroid.permission.BLUETOOTH /uses-permission android:nameandroid.permission.BLUETOOTH_ADMIN /uses-permission android:nameandroid.permission.BLUETOOTH_CONNECT /uses-permission android:nameandroid.permission.BLUETOOTH_SCAN /uses-feature android:nameandroid.hardware.bluetooth_le/① 增加UI布局 动态权限请求有两种方式一种是进入这个页面一下子请求多个权限另一种是一个一个来请求让你知道为什么会请求这个权限这里我们选择第二种因此需要增加一些布局xml如下图所示的布局XML。 下面我们依次创建lay_android12_should_connect.xml ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoandroid:idid/request_location_layandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:backgroundcolor/gray_whiteandroid:gravitycenterandroid:orientationverticalandroid:paddingStart16dpandroid:paddingEnd16dpImageViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravitycenter_horizontalandroid:padding16dpandroid:srcdrawable/ic_bluetooth_connected /TextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:text需要蓝牙连接权限android:textColorcolor/dark_orangeandroid:textSize16sp /TextViewandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:layout_marginTop16dpandroid:layout_marginBottom16dpandroid:text从Android12.0开始打开蓝牙之前需要请求此权限使用蓝牙连接权限app:titleTextColorcolor/black /com.google.android.material.button.MaterialButtonandroid:idid/btn_request_connect_permissionandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravitycenter_horizontalandroid:text授予权限android:textColorcolor/white / /LinearLayout用到一个图标ic_bluetooth_connected.xml ?xml version1.0 encodingUTF-8 standaloneno? vector xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:width48dpandroid:height48dpandroid:tintcolor/orangeandroid:viewportWidth24.0android:viewportHeight24.0pathandroid:fillColorandroid:color/whiteandroid:pathDataM7,12l-2,-2 -2,2 2,2 2,-2zM17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88zM19,10l-2,2 2,2 2,-2 -2,-2z / /vector布局lay_android12_should_scan.xml ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoandroid:idid/request_location_layandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:backgroundcolor/gray_whiteandroid:gravitycenterandroid:orientationverticalandroid:paddingStart16dpandroid:paddingEnd16dpImageViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravitycenter_horizontalandroid:padding16dpandroid:srcdrawable/ic_bluetooth_scan /TextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:text需要扫描权限android:textColorcolor/dark_orangeandroid:textSize16sp /TextViewandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:layout_marginTop16dpandroid:layout_marginBottom16dpandroid:text从Android12.0开始扫描设备不再需要请求定位权限使用此权限app:titleTextColorcolor/black /com.google.android.material.button.MaterialButtonandroid:idid/btn_request_scan_permissionandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravitycenter_horizontalandroid:text授予权限android:textColorcolor/white / /LinearLayout图标ic_bluetooth_scan.xml ?xml version1.0 encodingUTF-8 standaloneno? vector xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:width48dpandroid:height48dpandroid:autoMirroredtrueandroid:tintcolor/orangeandroid:viewportWidth24.0android:viewportHeight24.0pathandroid:fillColorandroid:color/whiteandroid:pathDataM14.24,12.01l2.32,2.32c0.28,-0.72 0.44,-1.51 0.44,-2.33 0,-0.82 -0.16,-1.59 -0.43,-2.31l-2.33,2.32zM19.53,6.71l-1.26,1.26c0.63,1.21 0.98,2.57 0.98,4.02s-0.36,2.82 -0.98,4.02l1.2,1.2c0.97,-1.54 1.54,-3.36 1.54,-5.31 -0.01,-1.89 -0.55,-3.67 -1.48,-5.19zM15.71,7.71L10,2L9,2v7.59L4.41,5 3,6.41 8.59,12 3,17.59 4.41,19 9,14.41L9,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM11,5.83l1.88,1.88L11,9.59L11,5.83zM12.88,16.29L11,18.17v-3.76l1.88,1.88z / /vector布局lay_empty.xml ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:gravitycenterandroid:orientationverticalImageViewandroid:idid/imageViewandroid:layout_width100dpandroid:layout_height100dpapp:srcCompatmipmap/ic_scanning /TextViewandroid:idid/textView2android:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_marginTop24dpandroid:text努力扫描中android:textColorcolor/grayandroid:textSize18spandroid:textStylebold / /LinearLayout图标不是XML图片去源码中获取这是在扫描不到设备的时候显示的布局布局lay_should_enable_bluetooth.xml。 ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:backgroundcolor/gray_whiteandroid:gravitycenterandroid:orientationverticalImageViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:padding16dpapp:srcCompatdrawable/ic_bluetooth_disabled /TextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:gravitycenter_horizontalandroid:text蓝牙已禁用android:textColorcolor/dark_orangeandroid:textSize16sp /TextViewandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:layout_marginTop16dpandroid:gravitycenter_horizontalandroid:text蓝牙适配器已关闭单击下面的按钮以启用它。android:textColorcolor/black /com.google.android.material.button.MaterialButtonandroid:idid/btn_enable_bluetoothandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravitycenter_horizontalandroid:layout_marginTop16dpandroid:text启用android:textColorcolor/white / /LinearLayout图标ic_bluetooth_disabled.xml vector xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:width48dpandroid:height48dpandroid:viewportWidth24.0android:viewportHeight24.0pathandroid:fillColorcolor/orangeandroid:pathDataM13,5.83l1.88,1.88 -1.6,1.6 1.41,1.41 3.02,-3.02L12,2h-1v5.03l2,2v-3.2zM5.41,4L4,5.41 10.59,12 5,17.59 6.41,19 11,14.41V22h1l4.29,-4.29 2.3,2.29L20,18.59 5.41,4zM13,18.17v-3.76l1.88,1.88L13,18.17z / /vector布局lay_should_enable_location.xml ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:backgroundcolor/gray_whiteandroid:gravitycenterandroid:orientationverticalImageViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravitycenter_horizontalandroid:padding16dpapp:srcCompatdrawable/ic_location_disabled /TextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:text位置已禁用android:textColorcolor/dark_orangeandroid:textSize16sp /TextViewandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:layout_marginTop16dpandroid:gravitycenter_horizontalandroid:text位置已关闭单击下面的按钮以启用它。android:textColorcolor/black /com.google.android.material.button.MaterialButtonandroid:idid/btn_enable_locationandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravitycenter_horizontalandroid:layout_marginTop16dpandroid:text启用android:textColorcolor/white / /LinearLayout图标ic_location_disabled.xml ?xml version1.0 encodingUTF-8 standaloneno? vector xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:width48dpandroid:height48dpandroid:tintcolor/orangeandroid:viewportWidth24.0android:viewportHeight24.0pathandroid:fillColorandroid:color/whiteandroid:pathDataM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06c-1.13,0.12 -2.19,0.46 -3.16,0.97l1.5,1.5C10.16,5.19 11.06,5 12,5c3.87,0 7,3.13 7,7 0,0.94 -0.19,1.84 -0.52,2.65l1.5,1.5c0.5,-0.96 0.84,-2.02 0.97,-3.15L23,13v-2h-2.06zM3,4.27l2.04,2.04C3.97,7.62 3.25,9.23 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c1.77,-0.2 3.38,-0.91 4.69,-1.98L19.73,21 21,19.73 4.27,3 3,4.27zM16.27,17.54C15.09,18.45 13.61,19 12,19c-3.87,0 -7,-3.13 -7,-7 0,-1.61 0.55,-3.09 1.46,-4.27l9.81,9.81z / /vector布局lay_should_location_lay.xml ?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoandroid:idid/request_location_layandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:backgroundcolor/gray_whiteandroid:gravitycenterandroid:orientationverticalandroid:paddingStart16dpandroid:paddingEnd16dpImageViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravitycenter_horizontalandroid:padding16dpandroid:srcdrawable/ic_location_off /TextViewandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:text需要位置许可android:textColorcolor/dark_orangeandroid:textSize16sp /TextViewandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:layout_marginTop16dpandroid:layout_marginBottom16dpandroid:text从 Android 6.0 Marshmallow 开始应用程序需要位置权限才能扫描低功耗蓝牙设备。 /com.google.android.material.button.MaterialButtonandroid:idid/btn_request_location_permissionandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravitycenter_horizontalandroid:text授予权限android:textColorcolor/white / /LinearLayout图标ic_location_off.xml vector xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:width48dpandroid:height48dpandroid:viewportWidth24.0android:viewportHeight24.0pathandroid:fillColorcolor/orangeandroid:pathDataM12,6.5c1.38,0 2.5,1.12 2.5,2.5 0,0.74 -0.33,1.39 -0.83,1.85l3.63,3.63c0.98,-1.86 1.7,-3.8 1.7,-5.48 0,-3.87 -3.13,-7 -7,-7 -1.98,0 -3.76,0.83 -5.04,2.15l3.19,3.19c0.46,-0.52 1.11,-0.84 1.85,-0.84zM16.37,16.1l-4.63,-4.63 -0.11,-0.11L3.27,3 2,4.27l3.18,3.18C5.07,7.95 5,8.47 5,9c0,5.25 7,13 7,13s1.67,-1.85 3.38,-4.35L18.73,21 20,19.73l-3.63,-3.63z / /vector好了在我们的努力下这些布局总算是创建完成了下面我们将它们放置到activity_scan.xml中代码如下所示 ?xml version1.0 encodingutf-8? RelativeLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsandroid:layout_widthmatch_parentandroid:layout_heightmatch_parenttools:context.ScanActivitycom.google.android.material.appbar.MaterialToolbarandroid:idid/toolbarandroid:layout_widthmatch_parentandroid:layout_height?attr/actionBarSizeandroid:backgroundcolor/orangeapp:title选择蓝牙设备app:titleTextColorcolor/whiteTextViewandroid:idid/tv_scan_statusandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_gravityendandroid:layout_marginEnd6dpandroid:padding10dpandroid:text搜索android:textColorcolor/whiteandroid:textSize14spandroid:visibilitygone //com.google.android.material.appbar.MaterialToolbarProgressBarandroid:idid/pb_scan_loadingstyle?android:attr/progressBarStyleHorizontalandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:layout_alignBottomid/toolbarandroid:layout_marginBottom-10dpandroid:indeterminatetrueandroid:indeterminateTintcolor/orangeandroid:visibilityinvisible /!--设备列表--androidx.recyclerview.widget.RecyclerViewandroid:idid/rv_deviceandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:layout_belowid/toolbarandroid:layout_marginTop4dpandroid:overScrollModenever /!--未扫描到设备时显示--includeandroid:idid/empty_laylayoutlayout/lay_emptyandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:layout_belowid/toolbar /!-- Android 12蓝牙扫描权限为许可时显示 Shell_Unresponsive--includeandroid:idid/request_bluetooth_scan_laylayoutlayout/lay_android12_should_scanandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:layout_belowid/toolbarandroid:visibilitygone /!--Android 6 至 11 没有打开位置开关 无法扫描蓝牙--includeandroid:idid/enable_location_laylayoutlayout/lay_should_enable_locationandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:layout_belowid/toolbarandroid:visibilitygone /!-- 位置权限未许可时显示 --includeandroid:idid/request_location_laylayoutlayout/lay_should_location_layandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:layout_belowid/toolbarandroid:visibilitygone /!-- 手机蓝牙未开启时显示 --includeandroid:idid/enable_bluetooth_laylayoutlayout/lay_should_enable_bluetoothandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:layout_belowid/toolbarandroid:visibilitygone /!-- Android12 开启蓝牙需要先请求蓝牙连接权限 --includeandroid:idid/request_bluetooth_connect_laylayoutlayout/lay_android12_should_connectandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:layout_belowid/toolbarandroid:visibilitygone //RelativeLayout下面我们可以写代码了在ScanActivity中 ② 点击监听 首先是ScanActivity的一些基本配置如下所示 class ScanActivity : BaseActivity() {private val TAG ScanActivity::class.java.simpleNameprivate val binding by viewBinding(ActivityScanBinding::inflate)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_scan)} }然后增加布局中按钮的点击监听创建一个initView()函数在onCreate()中调用它代码如下所示 override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_scan)initView()}private fun initView() {binding.requestBluetoothConnectLay.btnRequestConnectPermission.setOnClickListener(this)binding.enableBluetoothLay.btnEnableBluetooth.setOnClickListener(this)binding.requestLocationLay.btnRequestLocationPermission.setOnClickListener(this)binding.enableLocationLay.btnEnableLocation.setOnClickListener(this)binding.requestBluetoothScanLay.btnRequestScanPermission.setOnClickListener(this)binding.toolbar.setOnClickListener(this)binding.tvScanStatus.setOnClickListener(this)}然后实现点击监听 class ScanActivity : BaseActivity(), View.OnClickListener重写onClick()函数代码如下所示 override fun onClick(v: View) {when (v.id) {//请求蓝牙连接权限R.id.btn_request_connect_permission - {}//打开蓝牙开关R.id.btn_enable_bluetooth - {}//请求定位权限R.id.btn_request_location_permission - {}//打开位置开关R.id.btn_enable_location - {}//请求蓝牙扫描权限R.id.btn_request_scan_permission - {}//扫描或停止扫描R.id.tv_scan_status - {}else - {}}}在这里我们先不写内容后面再完善然后我们可以先处理权限再重写Activity的onResume()函数代码如下所示 override fun onResume() {super.onResume()if (isAndroid12()) {//蓝牙连接binding.requestBluetoothConnectLay.root.visibility if (hasBluetoothConnect()) View.GONE else View.VISIBLEif (!hasBluetoothConnect()) {Log.d(TAG, onResume: 未获取蓝牙连接权限)return}//打开蓝牙开关binding.enableBluetoothLay.root.visibility if (isOpenBluetooth()) View.GONE else View.VISIBLEif (!isOpenBluetooth()) {Log.d(TAG, onResume: 未打开蓝牙)return}//蓝牙扫描binding.requestBluetoothScanLay.root.visibility if (hasBluetoothScan()) View.GONE else View.VISIBLEif (!hasBluetoothScan()) {Log.d(TAG, onResume: 未获取蓝牙扫描权限)return}}//打开蓝牙binding.enableBluetoothLay.root.visibility if (isOpenBluetooth()) View.GONE else View.VISIBLEif (!isOpenBluetooth()) {Log.d(TAG, onResume: 未打开蓝牙)return}//打开定位binding.enableLocationLay.root.visibility if (isOpenLocation()) View.GONE else View.VISIBLEif (!isOpenLocation()) {Log.d(TAG, onResume: 未打开位置)return}//请求定位binding.requestLocationLay.root.visibility if (hasCoarseLocation() hasAccessFineLocation()) View.GONE else View.VISIBLEif (!hasAccessFineLocation()) {Log.d(TAG, onResume: 未获取定位权限)return}binding.tvScanStatus.visibility View.VISIBLE//开始扫描}③ 扫描处理 在这个函数中对activity_scan.xml中引入的布局判断是否显示在请求权限或者是打开开关之后都会触发这个函数然后进行检查当所有检查都通过之后说明你可以开始扫描了。那么如果要扫描我们需要得到BleCore的对象先声明然后在onCreate中进行实例化。 private lateinit var bleCore: BleCoreoverride fun onCreate(savedInstanceState: Bundle?) {...bleCore (application as BleApp).getBleCore()} 下面我们可以写扫描相关的方法代码如下所示 private fun startScan() {bleCore?.startScan()binding.tvScanStatus.text 停止binding.pbScanLoading.visibility View.VISIBLE}private fun stopScan() {bleCore?.stopScan()binding.tvScanStatus.text 搜索binding.pbScanLoading.visibility View.INVISIBLE}这里就是开始和停止扫描别忘了还有扫描回调这个回调应该写在哪里首先是在onCreate()函数中代码如下 override fun onCreate(savedInstanceState: Bundle?) {...//设置扫描回调if (isOpenBluetooth()) bleCore!!.setPhyScanCallback(thisScanActivity)}这里还判断了一下是否开启蓝牙扫描的结果需要实现BleScanCallback接口如下所示 class ScanActivity : BaseActivity(), View.OnClickListener, BleScanCallback 重写onScanResult()函数如下所示 /*** 扫描回调*/override fun onScanResult(result: ScanResult) {}④ 广播处理 然后别忘记了我们还有一个广播处理在onCreate()函数中进行广播注册代码如下所示 override fun onCreate(savedInstanceState: Bundle?) {...//注册广播registerReceiver(ScanReceiver().apply { setCallback(thisScanActivity) },IntentFilter().apply {addAction(BluetoothAdapter.ACTION_STATE_CHANGED)addAction(LocationManager.PROVIDERS_CHANGED_ACTION)})}实现接口ReceiverCallback代码如下所示 class ScanActivity : BaseActivity(), View.OnClickListener, BleScanCallback, ReceiverCallback重写里面的函数代码如下所示 /*** 蓝牙关闭*/override fun bluetoothClose() {}/*** 位置关闭*/override fun locationClose() {}四、权限处理 下面我们进行权限和开关的请求处理在ScanActivity中新增如下代码 //蓝牙连接权限private val requestConnect registerForActivityResult(ActivityResultContracts.RequestPermission()) {showMsg(if (it) 可以打开蓝牙 else Android12 中不授予此权限无法打开蓝牙)}//启用蓝牙private val enableBluetooth registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -if (result.resultCode Activity.RESULT_OK) {showMsg(蓝牙已打开)Log.d(TAG, : 蓝牙已打开)bleCore.setPhyScanCallback(thisScanActivity)}}//请求定位private val requestLocation registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -val coarseLocation result[Manifest.permission.ACCESS_COARSE_LOCATION]val fineLocation result[Manifest.permission.ACCESS_FINE_LOCATION]if (coarseLocation true fineLocation true) {//开始扫描设备showMsg(定位权限已获取)if (isOpenBluetooth()) bleCore.setPhyScanCallback(thisScanActivity)}}//启用定位private val enableLocation registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -if (result.resultCode Activity.RESULT_OK) {showMsg(位置已打开)Log.d(TAG, : 位置已打开)if (isOpenBluetooth()) bleCore.setPhyScanCallback(thisScanActivity)}}//蓝牙连接权限private val requestScan registerForActivityResult(ActivityResultContracts.RequestPermission()) {showMsg(if (it) 可以开始扫描设备了 else Android12 Android12 中不授予此权限无法扫描蓝牙)}这里使用了Activity Result API需要注意的是它们是与onCreate()函数平级的下面修改onClick()函数中的代码 override fun onClick(v: View) {when (v.id) {//请求蓝牙连接权限R.id.btn_request_connect_permission - if (isAndroid12()) requestConnect.launch(Manifest.permission.BLUETOOTH_CONNECT)//打开蓝牙开关R.id.btn_enable_bluetooth - enableBluetooth.launch(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE))//请求定位权限R.id.btn_request_location_permission - requestLocation.launch(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION))//打开位置开关R.id.btn_enable_location - enableLocation.launch(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))//请求蓝牙扫描权限R.id.btn_request_scan_permission - if (isAndroid12()) requestScan.launch(Manifest.permission.BLUETOOTH_SCAN)//扫描或停止扫描R.id.tv_scan_status - if (bleCore.isScanning()) stopScan() else startScan()else - {}}}这里就比较的简单了下面再修改bluetoothClose()和locationClose()函数在回调时都判断当前是否正在扫描在扫描则停止同时显示对应的布局。 override fun bluetoothClose() {//蓝牙关闭时停止扫描if (bleCore.isScanning()) {stopScan()binding.enableBluetoothLay.root.visibility View.VISIBLE}}override fun locationClose() {//位置关闭时停止扫描if (bleCore.isScanning()) {stopScan()binding.enableLocationLay.root.visibility View.VISIBLE}}最后再增加一个onStop()函数代码如下 override fun onStop() {super.onStop()//页面停止时停止扫描if (bleCore.isScanning()) stopScan()}当页面销毁了或者是进入后台了那么触发回调停止扫描。 五、扫描结果 要显示扫描结果首先要做的是定义一个类去装载扫描结果在ble包下新建一个BleDevice数据类代码如下所示 data class BleDevice(var realName: String? Unknown device, //蓝牙设备真实名称var macAddress: String, //蓝牙设备Mac地址var rssi: Int, //信号强度var device: BluetoothDevice,//蓝牙设备var gatt: BluetoothGatt? null//gatt )扫描的结果我们可以用列表来展示选择使用RecyclerView那么相应的会使用到适配器。 ① 列表适配器 首先创建适配器的布局在layout下新建一个item_device_rv.xml代码如下所示 ?xml version1.0 encodingutf-8? androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsandroid:idid/item_deviceandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:layout_marginBottom1dpandroid:backgroundcolor/whiteandroid:foreground?attr/selectableItemBackgroundandroid:orientationverticalImageViewandroid:idid/imageView2android:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_marginStart16dpandroid:srcdrawable/ic_bluetooth_blueapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintStart_toStartOfparentapp:layout_constraintTop_toTopOfparent /TextViewandroid:idid/tv_device_nameandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_marginStart16dpandroid:layout_marginTop8dpandroid:ellipsizeendandroid:singleLinetrueandroid:text设备名称android:textColorcolor/blackandroid:textSize16spapp:layout_constraintStart_toEndOfid/imageView2app:layout_constraintTop_toTopOfparent /TextViewandroid:idid/tv_mac_addressandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_marginBottom8dpandroid:ellipsizeendandroid:singleLinetrueandroid:textMac地址android:textSize12spapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintStart_toStartOfid/tv_device_nameapp:layout_constraintTop_toBottomOfid/tv_device_name /TextViewandroid:idid/tv_rssiandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_marginEnd16dpandroid:text信号强度android:textSize12spapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintEnd_toEndOfparentapp:layout_constraintTop_toTopOfparent //androidx.constraintlayout.widget.ConstraintLayout这里的内容不多主要内容就是设备名称、地址、信号强度下面我们创建适配器在com.llw.goodble包下新建一个adapter包该包下新建一个OnItemClickListener接口用于实现Item的点击监听代码如下所示 interface OnItemClickListener {fun onItemClick(view: View?, position: Int) }下面我们写适配器在adapter包下新建一个BleDeviceAdapter类代码如下所示 class BleDeviceAdapter(private val mDevices: ListBleDevice ) : RecyclerView.AdapterBleDeviceAdapter.ViewHolder() {private var mOnItemClickListener: OnItemClickListener? nullfun setOnItemClickListener(mOnItemClickListener: OnItemClickListener?) {this.mOnItemClickListener mOnItemClickListener}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val viewHolder ViewHolder(ItemDeviceRvBinding.inflate(LayoutInflater.from(parent.context), parent, false))viewHolder.binding.itemDevice.setOnClickListener { v -if (mOnItemClickListener ! null) mOnItemClickListener!!.onItemClick(v, viewHolder.adapterPosition)}return viewHolder}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val bleDevice: BleDevice mDevices[position]val rssi: Int bleDevice.rssiholder.binding.tvRssi.text String.format(Locale.getDefault(), %d dBm, rssi)//设备名称holder.binding.tvDeviceName.text bleDevice.realName//Mac地址holder.binding.tvMacAddress.text bleDevice.macAddress}override fun getItemCount() mDevices.sizeclass ViewHolder(itemView: ItemDeviceRvBinding) : RecyclerView.ViewHolder(itemView.root) {var binding: ItemDeviceRvBindinginit {binding itemView}} }这里就是基本的写法结合了ViewBinding在onBindViewHolder()中进行数据渲染那么适配器就写好了下面我们回到ScanActivity中去完成后的扫描结果显示。 ② 扫描结果处理 首先我们声明变量在ScanActivity中增加如下代码 private var mAdapter: BleDeviceAdapter? null//设备列表private val mList: MutableListBleDevice mutableListOf()private fun findIndex(bleDevice: BleDevice, mList: MutableListBleDevice): Int {var index 0for (devi in mList) {if (bleDevice.macAddress.contentEquals(devi.macAddress)) return indexindex 1}return -1}这个findIndex()函数用于在列表中找是否有添加过设备下面修改扫描的回调函数onScanResult()代码如下所示 override fun onScanResult(result: ScanResult) {if (result.scanRecord!!.deviceName null) returnif (result.scanRecord!!.deviceName!!.isEmpty()) returnval bleDevice BleDevice(result.scanRecord!!.deviceName,result.device.address,result.rssi,result.device)Log.d(TAG, onScanResult: ${bleDevice.macAddress})if (mList.size 0) {mList.add(bleDevice)} else {val index findIndex(bleDevice, mList)if (index -1) {//添加新设备mList.add(bleDevice)} else {//更新已有设备的rssimList[index].rssi bleDevice.rssi}}//如果未扫描到设备则显示空内容布局binding.emptyLay.root.visibility if (mList.size 0) View.VISIBLE else View.GONE//如果mAdapter为空则会执行run{}中的代码进行相关配置最终返回配置的结果mAdaptermAdapter ?: run {mAdapter BleDeviceAdapter(mList)binding.rvDevice.apply {(itemAnimator as SimpleItemAnimator).supportsChangeAnimations falselayoutManager LinearLayoutManager(thisScanActivity)adapter mAdapter}mAdapter!!.setOnItemClickListener(thisScanActivity)mAdapter}mAdapter!!.notifyDataSetChanged()}那么在开始扫描的时候我们最好清理一下列表修改一下startScan()函数代码如下所示 private fun startScan() {mList.clear()mAdapter?.notifyDataSetChanged()bleCore.startScan()binding.tvScanStatus.text 停止binding.pbScanLoading.visibility View.VISIBLE}同时在扫描回调中还有一个适配器的Item点击监听先实现它修改代码 class ScanActivity : BaseActivity(), View.OnClickListener, BleScanCallback, ReceiverCallback,OnItemClickListener {重写onItemClick()函数代码如下 override fun onItemClick(view: View?, position: Int) {if (bleCore.isScanning()) stopScan()//选中设备处理val intent Intent()intent.putExtra(device, mList[position].device)setResult(RESULT_OK, intent)finish()}我们是通过MainActivity进入到ScanActivity的那么在选中设备之后将设备对象返回并销毁当前页面。ScanActivity中还有最后一个修改的地方那就是在onResume()函数中增加开始扫描的代码代码如下所示 override fun onResume() {...//开始扫描if (!bleCore.isScanning()) startScan()}这里的意思就是当进入页面检查到条件都满足时就开始扫描。 ③ 接收结果 最后我们在MainActivity中接收结果修改代码如下所示 class MainActivity : BaseActivity() {private val binding by viewBinding(ActivityMainBinding::inflate)SuppressLint(MissingPermission)private val scanIntent registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -if (result.resultCode Activity.RESULT_OK) {if (result.data null) returnregisterForActivityResult//获取选中的设备val device if (Build.VERSION.SDK_INT Build.VERSION_CODES.TIRAMISU) {result.data!!.getParcelableExtra(device, BluetoothDevice::class.java)} else {result.data!!.getParcelableExtra(device) as BluetoothDevice?}showMsg(${device?.name} , ${device?.address})}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)binding.toolbar.setNavigationOnClickListener { scanIntent.launch(Intent(this,ScanActivity::class.java)) }} }下面我们运行一下 六、源码 如果对你有所帮助的话不妨 Star 或 Fork山高水长后会有期~ 源码地址GoodBle
http://www.pierceye.com/news/277977/

相关文章:

  • 网站app推广怎么做wordpress 手机号注册
  • 网站开发到上线需要多久骆驼有没有做网站的公司
  • 中小企业网站建设示范平台wordpress停用react
  • 网站怎样防止攻击seo顾问培训
  • 网站建设后需要维护吗微信安全中心官网
  • dw可以做h5网站设计素材网站0
  • 建设银行郑州中心支行网站青海商会网站建设公司
  • 国外小型网站中国视觉设计网
  • 沈阳专业网站制作团队泰安网络软件公司
  • 网站建设招聘兼职0基础建站教程
  • 如何从零开始做网站文学网站建设平台
  • 企业网站的网址通常包含dchaser wordpress
  • 什么是做网站flash是怎么做网站的
  • 什么是速成网站石家庄网站建设就找
  • 张家界网站建设要求滨州网站建设费用
  • wordpress订阅插件南昌优化排名推广
  • 国外做网站公司能赚钱吗wordpress登录锚点弹
  • 微网站平台微网站建设方案邢台市有几个区几个县
  • 茶叶外贸网站建设中国建设银行是国企还是央企
  • 成都网站建设公司有哪些内容必应搜索
  • 青岛建设集团招聘信息网站公司官网怎么制作
  • 本溪网站开发公司电话县蒙文网站建设汇报
  • 广州建网站有哪些怎么做网站建设赚钱
  • led视频网站建设邯郸建设信息网站
  • 开发网站培训班网站详情页用cdr做可以吗
  • 相亲网站开发与设计报告广州网站维护
  • 门户网站 技术方案北京感染人数最新消息
  • 网站制作过程流程酷家乐在线设计官网
  • 六年级做网站的软件小程序开发文档微信小程序
  • 做网站要准备哪些汕头建站公司模板