refactor(activity): 重构 BaseActivity 并优化相关页面
- 将 handler 改为 private,onDestroy 统一清理所有待执行任务 - 新增 protected postDelayed 方法供子类安全调度延时任务 - 将 DateTimeFormatter 提取为 companion object 常量,避免每秒重复创建 - setTitleBar 调用时自动显示标题栏 - 拆分 permissionCallback 为单权限和多权限独立回调,避免覆盖 - 将权限 launcher 改为 private,收敛访问入口 - 删除死代码 bgState/useBackground 及注释掉的旧代码 - InitActivity 倒计时改用私有 countdownHandler/countdownTask,解除对父类 handler 的依赖 - PrepareFoodActivity 替换 handler.postDelayed 为 BaseActivity.postDelayed - FoodRecognizeActivity 持有弹窗引用防止重复打开 - BaseApp 使用 ScaleDeviceConfig.DEVICE_ID_2 替换硬编码设备 ID
This commit is contained in:
@@ -9,8 +9,6 @@ import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -35,9 +33,14 @@ import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
import kotlin.getValue
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
open class BaseActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
/** 日期时间格式化器,复用避免每秒重复创建 */
|
||||
private val DATE_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy年MM月dd日 EEEE***HH:mm:ss", Locale.CHINA)
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityBaseBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -56,13 +59,6 @@ open class BaseActivity : AppCompatActivity() {
|
||||
}
|
||||
setContentView(binding.root)
|
||||
statusBarDarkFont(enable = false)
|
||||
|
||||
// window.setDecorFitsSystemWindows(false) // 启用 Edge-to-Edge
|
||||
// window.insetsController?.apply {
|
||||
// hide(WindowInsets.Type.statusBars()) // 隐藏状态栏
|
||||
// systemBarsBehavior =
|
||||
// WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE // 滑动时临时显示
|
||||
// }
|
||||
// 主设备垂直 padding 30dp,子设备 15dp
|
||||
val verticalPadding = if (GlobalData.deviceRole == DeviceRole.MASTER) 30.dp else 10.dp
|
||||
binding.tvLeftTime.setPadding(0, verticalPadding, 0, verticalPadding)
|
||||
@@ -71,27 +67,18 @@ open class BaseActivity : AppCompatActivity() {
|
||||
binding.ivBack.setOnClickListener { finish() }
|
||||
}
|
||||
|
||||
private var bgState = true
|
||||
fun useBackground(enable: Boolean) {
|
||||
bgState = enable
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
dismissLoading()
|
||||
super.onDestroy()
|
||||
ActivityManager.removeActivity(this)
|
||||
}
|
||||
|
||||
fun hideStatusBar() {
|
||||
// enableEdgeToEdge()
|
||||
//// val uiOptions = (View.SYSTEM_UI_FLAG_FULLSCREEN // 隐藏状态栏
|
||||
//// or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) // 隐藏导航栏(可选)
|
||||
// window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
hideSystemBars()
|
||||
}
|
||||
|
||||
fun statusBarDarkFont(enable: Boolean) {
|
||||
//window.decorView.systemUiVisibility = if (enable) View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR else 0
|
||||
val color = ContextCompat.getColor(
|
||||
this, if (enable) R.color.black else R.color.white
|
||||
)
|
||||
@@ -105,6 +92,7 @@ open class BaseActivity : AppCompatActivity() {
|
||||
titleAction: ((TextView) -> Unit)? = null,
|
||||
rightIconActon: ((ImageView) -> Unit)? = null
|
||||
) {
|
||||
binding.llTitleBar.visible()
|
||||
titleBarAction?.invoke(binding.llTitleBar)
|
||||
backAction?.invoke(binding.ivBack)
|
||||
titleAction?.invoke(binding.tvTitle)
|
||||
@@ -117,7 +105,7 @@ open class BaseActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
public fun setHeaderBackground(isHomePage: Boolean = false) {
|
||||
fun setHeaderBackground(isHomePage: Boolean = false) {
|
||||
binding.ivHeaderBg.setImageResource(
|
||||
if (isHomePage) R.drawable.bg_home_page else R.drawable.bg_other_page
|
||||
)
|
||||
@@ -135,14 +123,13 @@ open class BaseActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun updateDateTime() {
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 EEEE***HH:mm:ss", Locale.CHINA)
|
||||
val dateTime = formatter.format(LocalDateTime.now(ZoneId.of("Asia/Shanghai")))
|
||||
val dateTime = DATE_FORMATTER.format(LocalDateTime.now(ZoneId.of("Asia/Shanghai")))
|
||||
val arr = dateTime.split("***")
|
||||
binding.tvRightTime.text = arr[0]
|
||||
binding.tvLeftTime.text = arr[1]
|
||||
}
|
||||
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val updateTask = object : Runnable {
|
||||
override fun run() {
|
||||
updateDateTime()
|
||||
@@ -182,27 +169,37 @@ open class BaseActivity : AppCompatActivity() {
|
||||
}, 500)
|
||||
}
|
||||
|
||||
/**
|
||||
* 在主线程延迟执行任务,供子类使用
|
||||
* @param delayMs 延迟毫秒数
|
||||
* @param block 待执行的任务
|
||||
*/
|
||||
protected fun postDelayed(delayMs: Long, block: () -> Unit) {
|
||||
handler.postDelayed(block, delayMs)
|
||||
}
|
||||
|
||||
|
||||
val netViewModel: NetViewModel by viewModels()
|
||||
val appViewModel: DbViewModel by viewModels()
|
||||
|
||||
private var permissionCallback: ((isGranted: Boolean) -> Unit)? = null
|
||||
private var singlePermissionCallback: ((isGranted: Boolean) -> Unit)? = null
|
||||
private var multiplePermissionsCallback: ((isGranted: Boolean) -> Unit)? = null
|
||||
private var activityCallback: ((intent: Intent?) -> Unit)? = null
|
||||
|
||||
fun requestMultiplePermissions(
|
||||
permissions: Array<String>,
|
||||
callback: (isGranted: Boolean) -> Unit
|
||||
) {
|
||||
this.permissionCallback = callback
|
||||
this.multiplePermissionsCallback = callback
|
||||
requestMultiplePermissionsLauncher.launch(permissions)
|
||||
}
|
||||
|
||||
fun requestPermission(permission: String, callback: (isGranted: Boolean) -> Unit) {
|
||||
this.permissionCallback = callback
|
||||
this.singlePermissionCallback = callback
|
||||
requestPermissionLauncher.launch(permission)
|
||||
}
|
||||
|
||||
val requestMultiplePermissionsLauncher = registerForActivityResult(
|
||||
private val requestMultiplePermissionsLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
) { permissions ->
|
||||
var isGranted = true
|
||||
@@ -211,14 +208,14 @@ open class BaseActivity : AppCompatActivity() {
|
||||
isGranted = false
|
||||
}
|
||||
}
|
||||
permissionCallback?.invoke(isGranted)
|
||||
multiplePermissionsCallback?.invoke(isGranted)
|
||||
}
|
||||
|
||||
// 权限请求回调
|
||||
val requestPermissionLauncher = registerForActivityResult(
|
||||
private val requestPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted ->
|
||||
permissionCallback?.invoke(isGranted)
|
||||
singlePermissionCallback?.invoke(isGranted)
|
||||
}
|
||||
|
||||
fun startActivity(intent: Intent, callback: (Intent?) -> Unit) {
|
||||
|
||||
@@ -9,29 +9,38 @@ import android.util.Log
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.db.DatabaseProvider
|
||||
import com.shuwei.dish.match.objbox.ObjectBox
|
||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||
import com.shuwei.dish.match.scale.ScaleServiceManager
|
||||
import com.shuwei.dish.match.ui.InitActivity.Companion.TAG
|
||||
import com.shuwei.dish.match.utils.AppUtil
|
||||
import com.shuwei.dish.match.utils.BootReceiver
|
||||
import com.shuwei.dish.match.utils.CrashHandler
|
||||
import com.shuwei.dish.match.utils.Weigher2
|
||||
|
||||
|
||||
/**
|
||||
* BaseApp is the main application class that extends Android's Application class.
|
||||
* It handles initialization of core components, device role determination,
|
||||
* and service management for the weighing system.
|
||||
*/
|
||||
class BaseApp : Application() {
|
||||
|
||||
// Lazy initialization of the database instance
|
||||
val database by lazy { DatabaseProvider(this).instance }
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
// Set the application instance
|
||||
instance = this
|
||||
// Initialize crash handler for error tracking
|
||||
CrashHandler.init(this)
|
||||
// Get and store the unique device ID
|
||||
val deviceId = AppUtil.getUDID(this)
|
||||
Log.d(TAG, "onCreate: deviceId=$deviceId")
|
||||
GlobalData.deviceId = deviceId
|
||||
|
||||
// 根据设备 ID 判断角色:指定 ID 为主设备,其余为子设备
|
||||
// 主设备是/dev/ttyS7,子设备是/dev/ttyS4
|
||||
if (deviceId == "8fc2ab34-2137-3112-acca-f884ea8736d4") {
|
||||
if (deviceId == ScaleDeviceConfig.DEVICE_ID_2) {
|
||||
GlobalData.deviceRole = DeviceRole.MASTER
|
||||
Weigher2.setDevicePort("/dev/ttyS7")
|
||||
} else {
|
||||
@@ -49,7 +58,7 @@ class BaseApp : Application() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val TAG = "BaseApp"
|
||||
// const val canteenId = "1678234139391512577"
|
||||
var canteenId = "0"
|
||||
// var configUrl = ""
|
||||
|
||||
@@ -99,6 +99,9 @@ class FoodRecognizeActivity : BaseActivity() {
|
||||
/** 右侧自定义滚动指示条最小高度 */
|
||||
private val scrollIndicatorMinHeightPx by lazy { (24 * resources.displayMetrics.density).toInt() }
|
||||
|
||||
/** 食材搜索弹窗,持有引用防止重复打开 */
|
||||
private var foodSearchDialog: FoodSearchDialog? = null
|
||||
|
||||
private val scrollListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
updateScrollIndicator()
|
||||
@@ -259,14 +262,15 @@ class FoodRecognizeActivity : BaseActivity() {
|
||||
* 初始化按钮点击事件
|
||||
*/
|
||||
private fun initClickListeners() {
|
||||
// 更换食材:打开食材搜索弹窗
|
||||
// 更换食材:打开食材搜索弹窗,已打开时跳过
|
||||
binding.btnChangeFood.clickWithDebounce {
|
||||
// val currentName = list.getOrNull(selectedPosition)?.goodsName
|
||||
FoodSearchDialog(
|
||||
if (foodSearchDialog?.isShowing == true) return@clickWithDebounce
|
||||
foodSearchDialog = FoodSearchDialog(
|
||||
activity = this,
|
||||
// defGoodsName = currentName,
|
||||
onItemSelected = { item -> updateSelectedFood(item) }
|
||||
).show()
|
||||
).also { it.show() }
|
||||
}
|
||||
|
||||
// 取消:关闭页面
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.shuwei.dish.match.ui
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import android.view.animation.RotateAnimation
|
||||
import androidx.activity.addCallback
|
||||
@@ -33,6 +35,34 @@ class InitActivity : BaseActivity() {
|
||||
private lateinit var binding: ActivityInitBinding
|
||||
private var startTime = 0L // 倒计时开始时间
|
||||
private var lastNetworkCheckTime = 0L // 上次检测网络的时间
|
||||
private val countdownHandler = Handler(Looper.getMainLooper())
|
||||
private val countdownTask = object : Runnable {
|
||||
override fun run() {
|
||||
val elapsedTime = SystemClock.elapsedRealtime() - startTime
|
||||
val remainingTime = 60 - (elapsedTime / 1000).toInt()
|
||||
val currentNetworkCheckTime = (elapsedTime / 1000).toInt()
|
||||
|
||||
// 每 10 秒检测一次网络
|
||||
if (currentNetworkCheckTime > 0 && currentNetworkCheckTime % 10 == 0 && currentNetworkCheckTime != lastNetworkCheckTime.toInt()) {
|
||||
lastNetworkCheckTime = currentNetworkCheckTime.toLong()
|
||||
checkNetworkConnection()
|
||||
}
|
||||
|
||||
// 倒计时未结束,继续更新 UI
|
||||
if (remainingTime > 0) {
|
||||
binding.tvCountdown.text = "${remainingTime}秒"
|
||||
countdownHandler.postDelayed(this, 100)
|
||||
} else {
|
||||
// 倒计时结束,最后检测一次网络
|
||||
if (NetworkUtils.isNetworkConnected(this@InitActivity)) {
|
||||
startNextPage()
|
||||
} else {
|
||||
// 网络未连接,显示"连接网络"按钮
|
||||
showNetworkButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -108,6 +138,7 @@ class InitActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
countdownHandler.removeCallbacksAndMessages(null)
|
||||
WeightUtil.stopContinuousRead()
|
||||
ScaleServiceManager.stop()
|
||||
super.onDestroy()
|
||||
@@ -155,7 +186,7 @@ class InitActivity : BaseActivity() {
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
// 页面不可见时停止倒计时
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
countdownHandler.removeCallbacks(countdownTask)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,31 +209,8 @@ class InitActivity : BaseActivity() {
|
||||
* 使用 SystemClock.elapsedRealtime() 确保精确计时
|
||||
*/
|
||||
private fun scheduleCountdown() {
|
||||
handler.postDelayed({
|
||||
val elapsedTime = SystemClock.elapsedRealtime() - startTime
|
||||
val remainingTime = 60 - (elapsedTime / 1000).toInt()
|
||||
val currentNetworkCheckTime = (elapsedTime / 1000).toInt()
|
||||
|
||||
// 每 10 秒检测一次网络
|
||||
if (currentNetworkCheckTime > 0 && currentNetworkCheckTime % 10 == 0 && currentNetworkCheckTime != lastNetworkCheckTime.toInt()) {
|
||||
lastNetworkCheckTime = currentNetworkCheckTime.toLong()
|
||||
checkNetworkConnection()
|
||||
}
|
||||
|
||||
// 倒计时未结束,继续更新 UI
|
||||
if (remainingTime > 0) {
|
||||
binding.tvCountdown.text = "${remainingTime}秒"
|
||||
scheduleCountdown()
|
||||
} else {
|
||||
// 倒计时结束,最后检测一次网络
|
||||
if (NetworkUtils.isNetworkConnected(this)) {
|
||||
startNextPage()
|
||||
} else {
|
||||
// 网络未连接,显示"连接网络"按钮
|
||||
showNetworkButton()
|
||||
}
|
||||
}
|
||||
}, 100) // 每 100ms 检查一次,确保精确性
|
||||
countdownHandler.removeCallbacks(countdownTask)
|
||||
countdownHandler.postDelayed(countdownTask, 100)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,7 +219,7 @@ class InitActivity : BaseActivity() {
|
||||
private fun checkNetworkConnection() {
|
||||
if (NetworkUtils.isNetworkConnected(this)) {
|
||||
// 网络连接成功,停止倒计时并跳转到 HomeActivity
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
countdownHandler.removeCallbacks(countdownTask)
|
||||
startNextPage()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,9 +390,7 @@ class PrepareFoodActivity : BaseActivity() {
|
||||
dishPartAdapter.notifyItemChanged(position)
|
||||
return@SwipeCallback
|
||||
}
|
||||
list.removeAt(position)
|
||||
dishPartAdapter.notifyItemRemoved(position)
|
||||
dishPartAdapter.notifyItemRangeChanged(position, list.size - position)
|
||||
dishPartAdapter.removeAt(position)
|
||||
toast("已删除")
|
||||
})
|
||||
itemTouchHelper.attachToRecyclerView(this)
|
||||
@@ -537,10 +535,10 @@ class PrepareFoodActivity : BaseActivity() {
|
||||
)
|
||||
}
|
||||
Log.d(TAG, "takePhoto queryFood, 数据添加完成")
|
||||
handler.postDelayed({
|
||||
postDelayed(1000) {
|
||||
dismissLoading()
|
||||
loadRecognizeResultPage(foodList)
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:src="@drawable/bg_home_page" />
|
||||
tools:src="@drawable/bg_other_page" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLeftTime"
|
||||
|
||||
Reference in New Issue
Block a user