feat: 绑定页UI对齐原型图V3

- 二维码页面:标题移到上方,白色圆角背景框,说明文字居中
- 新增配对中Loading状态(spinner + "正在配对…")
- 颜色值对齐原型图(blue=#3B9EFF, green=#4ADE80, orange=#FFB340, red=#FF6B6B)
- 新增 bg_qr_frame.xml 白色圆角背景

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dongliang
2026-04-27 16:46:59 +09:30
parent cd49471a15
commit d06bf11b95
4 changed files with 126 additions and 66 deletions

View File

@@ -1,5 +1,6 @@
package com.xiaoqu.watch.ui.bind
import android.graphics.Bitmap
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -10,7 +11,6 @@ import com.google.gson.Gson
import com.google.zxing.BarcodeFormat
import com.google.zxing.MultiFormatWriter
import com.google.zxing.common.BitMatrix
import android.graphics.Bitmap
import com.xiaoqu.watch.R
import com.xiaoqu.watch.data.device.WatchBindInfo
import com.xiaoqu.watch.data.prefs.DevicePrefs
@@ -28,8 +28,9 @@ import javax.inject.Inject
/**
* 设备绑定页面
* 显示二维码供手机 APP 扫码绑定
* 监听 MQTT messageType=2 绑定成功消息
* 两种状态:
* 1. 二维码展示 — 等待手机 APP 扫码
* 2. 配对中 — 收到 MQTT 绑定消息后,显示 LoadingAPI 确认后跳转首页
*/
@AndroidEntryPoint
class BindFragment : BaseFragment<FragmentBindBinding>() {
@@ -57,10 +58,10 @@ class BindFragment : BaseFragment<FragmentBindBinding>() {
/**
* 生成二维码
* 编码设备信息 JSON{bluetoothName, imei, serial, power}
* 与旧版 codeScanPair.vue 格式一致
*/
private fun generateQrCode() {
try {
// 构建设备信息 JSON与旧版 codeScanPair.vue 一致)
val qrData = gson.toJson(mapOf(
"bluetoothName" to devicePrefs.bluetoothName,
"imei" to devicePrefs.imei,
@@ -69,8 +70,8 @@ class BindFragment : BaseFragment<FragmentBindBinding>() {
))
Timber.d("绑定: QR 数据=$qrData")
// 使用 ZXing 生成二维码 Bitmap
val size = 500 // 生成 500×500 像素ImageView 会缩放
// ZXing 生成二维码 Bitmap
val size = 500
val bitMatrix: BitMatrix = MultiFormatWriter().encode(
qrData, BarcodeFormat.QR_CODE, size, size
)
@@ -82,10 +83,7 @@ class BindFragment : BaseFragment<FragmentBindBinding>() {
}
}
/**
* 将 ZXing BitMatrix 转换为 Android Bitmap
* 黑色像素用黑色,白色像素用白色
*/
/** 将 ZXing BitMatrix 转换为 Android Bitmap */
private fun bitMatrixToBitmap(matrix: BitMatrix): Bitmap {
val width = matrix.width
val height = matrix.height
@@ -93,9 +91,9 @@ class BindFragment : BaseFragment<FragmentBindBinding>() {
for (y in 0 until height) {
for (x in 0 until width) {
pixels[y * width + x] = if (matrix[x, y]) {
0xFF000000.toInt() // 黑色
0xFF000000.toInt() // 黑色模块
} else {
0xFFFFFFFF.toInt() // 白色
0xFFFFFFFF.toInt() // 白色背景
}
}
}
@@ -104,7 +102,7 @@ class BindFragment : BaseFragment<FragmentBindBinding>() {
/**
* 监听 MQTT 绑定成功消息messageType=2
* 收到后存储用户信息 → 调用 API 确认 → 导航到首页
* 收到后切换到配对中状态 → 存储用户信息 → API 确认 → 导航到首页
*/
private fun observeBindEvent() {
viewLifecycleOwner.lifecycleScope.launch {
@@ -118,14 +116,18 @@ class BindFragment : BaseFragment<FragmentBindBinding>() {
/**
* 处理绑定成功
* 1. 解析用户信息并存入 UserPrefs
* 2. 调用 bindWatchConfirm API 确认
* 3. 导航到首页
* 1. 切换到配对中 Loading 状态
* 2. 解析用户信息并存入 UserPrefs
* 3. 调用 bindWatchConfirm API 确认
* 4. 导航到首页
*/
private fun handleBindSuccess(rawJson: String) {
try {
Timber.d("绑定: 收到绑定消息 $rawJson")
// 切换到配对中状态
showLoading()
// 解析用户信息
val bindInfo = gson.fromJson(rawJson, WatchBindInfo::class.java)
@@ -137,22 +139,35 @@ class BindFragment : BaseFragment<FragmentBindBinding>() {
headUrl = bindInfo.headUrl
)
// 调用 API 确认绑定
// 调用 API 确认绑定,然后导航到首页
viewLifecycleOwner.lifecycleScope.launch {
val params = mapOf<String, Any>(
"imei" to devicePrefs.imei,
"userId" to bindInfo.userId
)
safeApiCall { commonApi.bindWatchConfirm(params) }
Timber.d("绑定: 确认 API 已调用")
}
Timber.d("绑定: 确认 API 已调用,导航到首页")
// 导航到首页
Timber.d("绑定: 绑定成功,导航到首页")
findNavController().navigate(R.id.action_bind_to_home)
// 导航到首页
findNavController().navigate(R.id.action_bind_to_home)
}
} catch (e: Exception) {
Timber.e(e, "绑定: 处理绑定消息异常")
// 异常时恢复二维码显示
showQrCode()
}
}
/** 显示二维码状态 */
private fun showQrCode() {
binding.qrWrap.visibility = View.VISIBLE
binding.loadingWrap.visibility = View.GONE
}
/** 显示配对中 Loading 状态 */
private fun showLoading() {
binding.qrWrap.visibility = View.GONE
binding.loadingWrap.visibility = View.VISIBLE
}
}