refactor: 统一时钟管理并优化状态栏显示逻辑

- 在 BaseActivity 中实现统一的时钟管理机制,避免各子类重复实现
- LogActivity 和 ShelfActivity 覆写 onClockTick() 自定义状态栏显示
- HomeActivity 移除重复的时间更新逻辑,启用右侧状态栏菜单
- 提取协议常量到 ProtocolConstants,提高代码可维护性
- 优化开锁逻辑判断,改为基于 deviceId 的动态判断
- 新增右侧状态栏弹出菜单布局文件

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mazengfei
2026-04-01 17:07:41 +08:00
co-authored by Claude Sonnet 4.6
parent 3e5c7c6caa
commit 133f309d4b
7 changed files with 261 additions and 62 deletions
@@ -43,18 +43,16 @@ import com.shuwei.intelligent.shelves.utils.ext.toast
import com.shuwei.intelligent.shelves.utils.ext.visible import com.shuwei.intelligent.shelves.utils.ext.visible
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class HomeActivity : BaseActivity() { class HomeActivity : BaseActivity() {
companion object { companion object {}
private const val MM_DD_EEEE__HH_MM_SS = "MM月dd日 EEEE HH:mm:ss"
} override val enableRightStatusMenu: Boolean = true
// 货架列表数据 // 货架列表数据
private val list: MutableList<ShelfModel> = mutableListOf() private val list: MutableList<ShelfModel> = mutableListOf()
@@ -68,8 +66,8 @@ class HomeActivity : BaseActivity() {
// 当前湿度 // 当前湿度
private var showHumidity = "0" private var showHumidity = "0"
// 是否在点击货架时发送开锁指令;有门锁的部署点(如1楼餐厅)为 true,无门锁的部署点(如401室)为 false // 仅特定 deviceId 的部署点需要发送开锁指令
private var isNeedOpenLock = true private fun isNeedOpenLock() = App.deviceId == "4787e213-90ab-3e32-88e0-ac271a937751"
private var deviceName = "" private var deviceName = ""
@@ -304,7 +302,7 @@ class HomeActivity : BaseActivity() {
noDataWarningDialog?.dismiss() noDataWarningDialog?.dismiss()
noDataWarningDialog = null noDataWarningDialog = null
SerialPortManager.close() runBlocking { SerialPortManager.close() }
saveTaskJob?.cancel() saveTaskJob?.cancel()
overdueTaskJob?.cancel() overdueTaskJob?.cancel()
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
@@ -325,7 +323,7 @@ class HomeActivity : BaseActivity() {
private fun onShelfItemClicked(position: Int) { private fun onShelfItemClicked(position: Int) {
shelfIndex = position shelfIndex = position
lifecycleScope.launch { lifecycleScope.launch {
if (isNeedOpenLock) { if (isNeedOpenLock()) {
val openCmd = if (list[position].deviceNo in 1..5) val openCmd = if (list[position].deviceNo in 1..5)
LEFT_SHELF_OPEN_CMD LEFT_SHELF_OPEN_CMD
else else
@@ -374,31 +372,17 @@ class HomeActivity : BaseActivity() {
} }
} }
private val dateTimeFormatter by lazy { SimpleDateFormat(MM_DD_EEEE__HH_MM_SS, Locale.CHINA) }
private fun updateDateTime() {
updateRightStatus(dateTimeFormatter.format(Date()))
}
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private val updateTask = object : Runnable {
override fun run() {
updateDateTime()
handler.postDelayed(this, 1000)
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
hideStatusBar() hideStatusBar()
handler.post(updateTask)
// 启动无数据看门狗(每30秒检查一次) // 启动无数据看门狗(每30秒检查一次)
handler.postDelayed(noDataWatchdogTask, 30 * 1000L) handler.postDelayed(noDataWatchdogTask, 30 * 1000L)
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
handler.removeCallbacks(updateTask)
// 停止无数据看门狗 // 停止无数据看门狗
handler.removeCallbacks(noDataWatchdogTask) handler.removeCallbacks(noDataWatchdogTask)
} }
@@ -1,28 +1,53 @@
package com.shuwei.intelligent.shelves.activity package com.shuwei.intelligent.shelves.activity
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.Gravity
import android.widget.TextView
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.shuwei.intelligent.shelves.adapter.LogAdapter import com.shuwei.intelligent.shelves.adapter.LogAdapter
import com.shuwei.intelligent.shelves.base.BaseActivity import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityLogListBinding import com.shuwei.intelligent.shelves.databinding.ActivityLogListBinding
import com.shuwei.intelligent.shelves.dialog.CommonDialog import com.shuwei.intelligent.shelves.dialog.CommonDialog
import com.shuwei.intelligent.shelves.utils.ext.copyTextToClipboard import com.shuwei.intelligent.shelves.utils.ext.copyTextToClipboard
import com.shuwei.intelligent.shelves.utils.ext.dp
import com.shuwei.intelligent.shelves.utils.ext.gone import com.shuwei.intelligent.shelves.utils.ext.gone
import com.shuwei.intelligent.shelves.utils.ext.startActivity import com.shuwei.intelligent.shelves.utils.ext.startActivity
import com.shuwei.intelligent.shelves.utils.ext.toast import com.shuwei.intelligent.shelves.utils.ext.toast
import com.shuwei.intelligent.shelves.serial.ProtocolConstants
import com.shuwei.intelligent.shelves.utils.ext.visible import com.shuwei.intelligent.shelves.utils.ext.visible
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class LogActivity : BaseActivity() { class LogActivity : BaseActivity() {
companion object { companion object {
const val IS_LOG_DIR = "isLogDir" const val IS_LOG_DIR = "isLogDir"
const val LOG_DATE = "logDate" const val LOG_DATE = "logDate"
const val YYYY_MM_DD__EEEE_HH_MM_SS = "yyyy年MM月dd日 EEEE***HH:mm:ss"
}
/**
* 覆写时钟回调:左侧状态栏显示年月日星期,右侧状态栏显示时分秒
*/
override fun onClockTick() {
val sdf = SimpleDateFormat(YYYY_MM_DD__EEEE_HH_MM_SS, Locale.CHINA)
val arr = sdf.format(Date()).split("***")
updateLeftStatus(arr[0])
updateRightStatus(arr[1])
} }
private lateinit var binding:ActivityLogListBinding private lateinit var binding:ActivityLogListBinding
private var isLogDir = false private var isLogDir = false
private var logDate = "" private var logDate = ""
private val logList = mutableListOf<String>() private val logList = mutableListOf<String>()
private val originalLogList = mutableListOf<String>() // 原始日志备份
private val logAdapter by lazy { private val logAdapter by lazy {
LogAdapter(logList).apply { LogAdapter(logList).apply {
setOnItemClickListener { _, _, position -> setOnItemClickListener { _, _, position ->
@@ -62,24 +87,20 @@ class LogActivity : BaseActivity() {
logAdapter.notifyDataSetChanged() logAdapter.notifyDataSetChanged()
} }
if (isLogDir) {
logList.addAll(fileLogger.getLogFiles())
} else {
logList.addAll(fileLogger.readLogsByName(logDate))
}
binding.rvLogList.let { binding.rvLogList.let {
it.layoutManager = LinearLayoutManager(this) it.layoutManager = LinearLayoutManager(this)
it.adapter = logAdapter it.adapter = logAdapter
} }
setupSearch()
setupJumpButton()
binding.btnFind.run { binding.btnFind.run {
if (isLogDir) gone() else visible() if (isLogDir) gone() else visible()
} }
binding.btnFind.setOnClickListener { binding.btnFind.setOnClickListener {
if (isLogDir) { if (isLogDir) return@setOnClickListener
return@setOnClickListener val list = logList.filter { it.contains("${ProtocolConstants.HEADER}${ProtocolConstants.CMD_TEMP_REPORT}") }
}
val list = logList.filter { it.contains("${HomeActivity.HEADER}1001") }
if (list.isEmpty()) { if (list.isEmpty()) {
toast("未找到到1001指令") toast("未找到到1001指令")
return@setOnClickListener return@setOnClickListener
@@ -88,6 +109,95 @@ class LogActivity : BaseActivity() {
logList.addAll(list) logList.addAll(list)
logAdapter.notifyDataSetChanged() logAdapter.notifyDataSetChanged()
} }
// 后台加载日志数据
lifecycleScope.launch {
binding.progressBar.visible()
val data = withContext(Dispatchers.IO) {
if (isLogDir) fileLogger.getLogFiles()
else fileLogger.readLogsByName(logDate)
}
logList.addAll(data)
originalLogList.addAll(data)
logAdapter.notifyDataSetChanged()
binding.progressBar.gone()
setupTimeNavigation()
}
}
// 搜索功能
private fun setupSearch() {
binding.etSearch.addTextChangedListener(afterTextChanged = { s ->
val keyword = s?.toString() ?: ""
logList.clear()
logList.addAll(if (keyword.isEmpty()) originalLogList else originalLogList.filter { it.contains(keyword, ignoreCase = true) })
logAdapter.notifyDataSetChanged()
})
}
// 快速跳转功能
private fun setupJumpButton() {
var isAtTop = true
binding.fabJump.setOnClickListener {
if (isAtTop) {
binding.rvLogList.scrollToPosition(logList.size - 1)
} else {
binding.rvLogList.scrollToPosition(0)
}
isAtTop = !isAtTop
}
}
// 时间导航功能
private fun setupTimeNavigation() {
if (isLogDir) {
binding.hsvTimeNav.gone()
return
}
val timeMap = mutableMapOf<String, Int>()
val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
originalLogList.forEachIndexed { index, log ->
val timeStr = log.substringAfter("[").substringBefore("]")
try {
val date = dateFormat.parse(timeStr)
date?.let {
val hourKey = SimpleDateFormat("HH:00", Locale.getDefault()).format(it)
if (!timeMap.containsKey(hourKey)) {
timeMap[hourKey] = index
}
}
} catch (e: Exception) {
// 忽略解析失败的日志
}
}
if (timeMap.isEmpty()) {
binding.hsvTimeNav.gone()
return
}
timeMap.toSortedMap().forEach { (hour, position) ->
val btn = TextView(this).apply {
text = hour
textSize = 14f
setTextColor(Color.BLACK)
setPadding(12.dp, 8.dp, 12.dp, 8.dp)
gravity = Gravity.CENTER
setBackgroundColor(Color.parseColor("#E0E0E0"))
setOnClickListener {
binding.rvLogList.scrollToPosition(position)
}
}
val params = android.widget.LinearLayout.LayoutParams(
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT,
android.widget.LinearLayout.LayoutParams.MATCH_PARENT
).apply {
marginEnd = 8.dp
}
binding.llTimeNav.addView(btn, params)
}
} }
} }
@@ -3,8 +3,6 @@ package com.shuwei.intelligent.shelves.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log import android.util.Log
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.activity.viewModels import androidx.activity.viewModels
@@ -18,7 +16,7 @@ import com.chad.library.adapter4.util.setOnDebouncedItemClick
import com.scwang.smart.refresh.layout.constant.RefreshState import com.scwang.smart.refresh.layout.constant.RefreshState
import com.shuwei.intelligent.shelves.App import com.shuwei.intelligent.shelves.App
import com.shuwei.intelligent.shelves.R import com.shuwei.intelligent.shelves.R
import com.shuwei.intelligent.shelves.activity.HomeActivity.Companion.DEVICE_INFO_CMD import com.shuwei.intelligent.shelves.serial.ProtocolConstants
import com.shuwei.intelligent.shelves.adapter.SearchAdapter import com.shuwei.intelligent.shelves.adapter.SearchAdapter
import com.shuwei.intelligent.shelves.base.BaseActivity import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityShelfBinding import com.shuwei.intelligent.shelves.databinding.ActivityShelfBinding
@@ -148,7 +146,7 @@ class ShelfActivity : BaseActivity() {
//binding.tvFoodWeight.text = "0克" //binding.tvFoodWeight.text = "0克"
EventBus.getDefault().post(ClearShelfEvent(shelfModel!!.deviceNo, tempWeight)) EventBus.getDefault().post(ClearShelfEvent(shelfModel!!.deviceNo, tempWeight))
} }
sendCmd(DEVICE_INFO_CMD) sendCmd(ProtocolConstants.DEVICE_INFO_CMD)
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.getGoodsListUiState.collect { state -> viewModel.getGoodsListUiState.collect { state ->
@@ -305,31 +303,23 @@ class ShelfActivity : BaseActivity() {
} }
} }
private fun updateDateTime() { /**
* 覆写时钟回调:左侧状态栏显示年月日星期,右侧状态栏显示时分秒
*/
override fun onClockTick() {
val sdf = SimpleDateFormat(YYYY_MM_DD__EEEE_HH_MM_SS, Locale.CHINA) val sdf = SimpleDateFormat(YYYY_MM_DD__EEEE_HH_MM_SS, Locale.CHINA)
val dateTime = sdf.format(Date()) val arr = sdf.format(Date()).split("***")
val arr = dateTime.split("***")
updateLeftStatus(arr[0]) updateLeftStatus(arr[0])
updateRightStatus(arr[1]) updateRightStatus(arr[1])
} }
private val handler = Handler(Looper.getMainLooper())
private val updateTask = object : Runnable {
override fun run() {
updateDateTime()
handler.postDelayed(this, 1000)
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
hideStatusBar() hideStatusBar()
handler.post(updateTask)
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
handler.removeCallbacks(updateTask)
} }
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
@@ -371,17 +361,17 @@ class ShelfActivity : BaseActivity() {
val shelfNo = shelfModel?.deviceNo ?: 0 val shelfNo = shelfModel?.deviceNo ?: 0
val hex = shelfNo.toString(16).padStart(2, '0').uppercase() val hex = shelfNo.toString(16).padStart(2, '0').uppercase()
Log.d(TAG, "onCreate: hex=$hex") Log.d(TAG, "onCreate: hex=$hex")
val zeroClearingCmd = "${HomeActivity.HEADER}0401${hex}${HomeActivity.FOOTER}" val zeroClearingCmd = "${ProtocolConstants.HEADER}0401${hex}${ProtocolConstants.FOOTER}"
log("编号${shelfNo}货架执行清零指令:${zeroClearingCmd}") log("编号${shelfNo}货架执行清零指令:${zeroClearingCmd}")
sendCmd(zeroClearingCmd) sendCmd(zeroClearingCmd)
sendCmd( sendCmd(
if (shelfNo in 1..5) HomeActivity.LEFT_SHELF_OPEN_CMD else HomeActivity.RIGHT_SHELF_OPEN_CMD if (shelfNo in 1..5) ProtocolConstants.LEFT_SHELF_OPEN_CMD else ProtocolConstants.RIGHT_SHELF_OPEN_CMD
) )
//清零 //清零
realWeight = 0 realWeight = 0
// binding.tvFoodWeight.text = "${realWeight}克" // binding.tvFoodWeight.text = "${realWeight}克"
sendCmd(DEVICE_INFO_CMD) sendCmd(ProtocolConstants.DEVICE_INFO_CMD)
startTime = System.currentTimeMillis() startTime = System.currentTimeMillis()
Loading.show(this@ShelfActivity) Loading.show(this@ShelfActivity)
// window.decorView.postDelayed({ Loading.dismiss() }, 5000) // window.decorView.postDelayed({ Loading.dismiss() }, 5000)
@@ -22,6 +22,7 @@ import com.shuwei.intelligent.shelves.serial.ProtocolConstants.LIGHT_OPEN
import com.shuwei.intelligent.shelves.serial.SerialPortManager import com.shuwei.intelligent.shelves.serial.SerialPortManager
import com.shuwei.intelligent.shelves.utils.FileLogger import com.shuwei.intelligent.shelves.utils.FileLogger
import com.shuwei.intelligent.shelves.utils.ext.clickWithDebounce import com.shuwei.intelligent.shelves.utils.ext.clickWithDebounce
import com.shuwei.intelligent.shelves.utils.ext.dp
import com.shuwei.intelligent.shelves.utils.ext.startActivity import com.shuwei.intelligent.shelves.utils.ext.startActivity
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -29,6 +30,9 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import com.shuwei.intelligent.shelves.utils.ext.setOnDoubleClickListener import com.shuwei.intelligent.shelves.utils.ext.setOnDoubleClickListener
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
open class BaseActivity : AppCompatActivity() { open class BaseActivity : AppCompatActivity() {
@@ -67,10 +71,13 @@ open class BaseActivity : AppCompatActivity() {
finish() finish()
} }
binding.tvRightStatus.setOnDoubleClickListener { binding.tvRightStatus.setOnDoubleClickListener {
showRightStatusPopup(binding.tvRightStatus) if (enableRightStatusMenu) showRightStatusPopup(binding.tvRightStatus)
} }
} }
/** 是否允许右侧状态栏双击弹出菜单,默认关闭,仅 HomeActivity 开启 */
protected open val enableRightStatusMenu: Boolean = false
fun hideStatusBar() {} fun hideStatusBar() {}
fun statusBarDarkFont() { fun statusBarDarkFont() {
@@ -107,6 +114,26 @@ open class BaseActivity : AppCompatActivity() {
onUserInactive() onUserInactive()
} }
// ---- 右侧状态栏时钟 ----
// 默认时间格式:月日星期 时:分:秒
private val CLOCK_FORMAT = "MM月dd日 EEEE HH:mm:ss"
private val clockFormatter by lazy { SimpleDateFormat(CLOCK_FORMAT, Locale.CHINA) }
/**
* 每秒时钟回调,默认将当前时间显示在右侧状态栏。
* 子类可覆写以自定义格式或同时更新左侧状态栏。
*/
open fun onClockTick() {
updateRightStatus(clockFormatter.format(Date()))
}
private val clockTask = object : Runnable {
override fun run() {
onClockTick()
mHandler.postDelayed(this, 1000)
}
}
/** /**
* 用户超时无操作时回调,默认空实现,子类按需覆写 * 用户超时无操作时回调,默认空实现,子类按需覆写
*/ */
@@ -128,14 +155,18 @@ open class BaseActivity : AppCompatActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
// 启动定时器 // 启动超时定时器
mHandler.postDelayed(mTimeoutRunnable, TIME_OUT) mHandler.postDelayed(mTimeoutRunnable, TIME_OUT)
// 启动右侧状态栏时钟
mHandler.post(clockTask)
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
// 取消定时器 // 取消超时定时器
mHandler.removeCallbacks(mTimeoutRunnable) mHandler.removeCallbacks(mTimeoutRunnable)
// 停止右侧状态栏时钟
mHandler.removeCallbacks(clockTask)
} }
fun switchLight(isOpen: Boolean) { fun switchLight(isOpen: Boolean) {
@@ -159,8 +190,8 @@ open class BaseActivity : AppCompatActivity() {
val menuBinding = PopupRightStatusMenuBinding.inflate(LayoutInflater.from(this)) val menuBinding = PopupRightStatusMenuBinding.inflate(LayoutInflater.from(this))
val popup = PopupWindow( val popup = PopupWindow(
menuBinding.root, menuBinding.root,
android.view.ViewGroup.LayoutParams.WRAP_CONTENT, 120.dp,
android.view.ViewGroup.LayoutParams.WRAP_CONTENT, 101.dp,
true true
) )
menuBinding.tvEnvSwitch.setOnClickListener { menuBinding.tvEnvSwitch.setOnClickListener {
@@ -69,7 +69,7 @@ object SerialPortManager {
} }
// 关闭串口 // 关闭串口
fun close() { suspend fun close() {
scope.cancel() scope.cancel()
serialPort?.close() serialPort?.close()
} }
+53 -1
View File
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@@ -39,8 +44,55 @@
android:text="查找1001指令"/> android:text="查找1001指令"/>
</FrameLayout> </FrameLayout>
<EditText
android:id="@+id/etSearch"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginHorizontal="12dp"
android:layout_marginTop="8dp"
android:background="@android:color/white"
android:hint="搜索日志..."
android:paddingHorizontal="12dp"
android:textSize="14sp"
android:singleLine="true"
tools:ignore="Autofill,TextFields" />
<HorizontalScrollView
android:id="@+id/hsvTimeNav"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:scrollbars="none">
<LinearLayout
android:id="@+id/llTimeNav"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingHorizontal="12dp" />
</HorizontalScrollView>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvLogList" android:id="@+id/rvLogList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
</LinearLayout> </LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabJump"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@android:drawable/ic_menu_sort_by_size"
app:fabSize="normal"
tools:ignore="ContentDescription" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:elevation="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_env_switch"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="切换环境"
android:textColor="#333333"
android:textSize="14sp" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#E0E0E0" />
<TextView
android:id="@+id/tv_log"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="查看日志"
android:textColor="#333333"
android:textSize="14sp" />
</LinearLayout>