feat(设置界面): 添加长按重启应用功能并优化界面布局

- 在设置界面新增长按重启按钮,包含进度条动画反馈
- 添加自定义进度条样式以增强视觉体验
- 优化IP配置区域的布局结构,将保存按钮与输入框水平排列
- 移除MqttManager中冗余的注释
- 更新字符串资源以支持新功能
This commit is contained in:
2026-03-10 20:40:37 +08:00
parent 15fba9d1f9
commit 03cc654468
5 changed files with 135 additions and 47 deletions

View File

@@ -56,7 +56,7 @@ class MqttManager(private val context: Context, private val serverIp: String) {
isAutomaticReconnect = false isAutomaticReconnect = false
isCleanSession = true isCleanSession = true
connectionTimeout = 10 connectionTimeout = 10
keepAliveInterval = 60 // 设置心跳间隔为60秒 keepAliveInterval = 60
userName = "lzwc" userName = "lzwc"
password = "Lzwc@4187.".toCharArray() password = "Lzwc@4187.".toCharArray()
} }

View File

@@ -1,35 +1,38 @@
package com.example.lzwcai_terminal_temi package com.example.lzwcai_terminal_temi
import android.animation.ValueAnimator
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.example.lzwcai_terminal_temi.databinding.ActivitySettingsBinding import com.example.lzwcai_terminal_temi.databinding.ActivitySettingsBinding
import kotlin.system.exitProcess
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsBinding private lateinit var binding: ActivitySettingsBinding
private var restartAnimator: ValueAnimator? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivitySettingsBinding.inflate(layoutInflater) binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
// 默认隐藏软键盘
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE) val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
val savedIp = prefs.getString("network_ip", "") val savedIp = prefs.getString("network_ip", "")
binding.etIpAddress.setText(savedIp) binding.etIpAddress.setText(savedIp)
// 点击外部隐藏键盘 binding.root.setOnClickListener { hideKeyboard() }
binding.root.setOnClickListener {
hideKeyboard()
}
binding.btnSave.setOnClickListener { binding.btnSave.setOnClickListener {
hideKeyboard() hideKeyboard()
@@ -50,8 +53,61 @@ class SettingsActivity : AppCompatActivity() {
finish() finish()
} }
setupRestartButton()
} }
private fun setupRestartButton() {
binding.btnRestart.setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startRestartAnimation()
true
}
MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL -> {
cancelRestartAnimation()
true
}
else -> false
}
}
}
private fun startRestartAnimation() {
binding.restartProgressBar.visibility = View.VISIBLE
restartAnimator = ValueAnimator.ofInt(0, 100).apply {
duration = 3000
addUpdateListener { animation ->
binding.restartProgressBar.progress = animation.animatedValue as Int
}
addListener(object : android.animation.Animator.AnimatorListener {
override fun onAnimationEnd(animation: android.animation.Animator) {
if (binding.restartProgressBar.progress == 100) {
restartApplication()
}
}
override fun onAnimationStart(animation: android.animation.Animator) {}
override fun onAnimationCancel(animation: android.animation.Animator) {}
override fun onAnimationRepeat(animation: android.animation.Animator) {}
})
start()
}
}
private fun cancelRestartAnimation() {
restartAnimator?.cancel()
binding.restartProgressBar.progress = 0
binding.restartProgressBar.visibility = View.INVISIBLE
}
private fun restartApplication() {
val intent = packageManager.getLaunchIntentForPackage(packageName)
val pendingIntent = PendingIntent.getActivity(this, 123456, intent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent)
exitProcess(0)
}
private fun hideKeyboard() { private fun hideKeyboard() {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
currentFocus?.let { currentFocus?.let {

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dp" />
<solid android:color="#E0E0E0" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="5dp" />
<solid android:color="#4CAF50" />
</shape>
</clip>
</item>
</layer-list>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true"> android:fillViewport="true">
@@ -9,7 +10,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="24dp"> android:padding="24dp">
<!-- 返回按钮 -->
<ImageButton <ImageButton
android:id="@+id/btnBack" android:id="@+id/btnBack"
android:layout_width="50dp" android:layout_width="50dp"
@@ -20,7 +20,6 @@
android:contentDescription="@string/btn_back" android:contentDescription="@string/btn_back"
android:src="@android:drawable/ic_menu_revert" /> android:src="@android:drawable/ic_menu_revert" />
<!-- 标题 -->
<TextView <TextView
android:id="@+id/tvSettingsTitle" android:id="@+id/tvSettingsTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -31,57 +30,72 @@
android:textSize="32sp" android:textSize="32sp"
android:textStyle="bold" /> android:textStyle="bold" />
<!-- IP 配置部分 -->
<LinearLayout <LinearLayout
android:id="@+id/ipConfigLayout" android:id="@+id/ipConfigLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/tvSettingsTitle" android:layout_below="@id/tvSettingsTitle"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:orientation="vertical"> android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <EditText
android:id="@+id/etIpAddress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:hint="@string/hint_ip_address"
android:inputType="number|numberDecimal"
android:digits="0123456789."
android:minHeight="60dp"
android:textSize="24sp" />
<Button
android:id="@+id/btnSave"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="60dp"
android:layout_marginBottom="16dp" android:text="@string/btn_save"
android:text="@string/label_ip_config" android:textSize="24sp" />
android:textSize="24sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<EditText
android:id="@+id/etIpAddress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="16dp"
android:hint="@string/hint_ip_address"
android:inputType="number|numberDecimal"
android:digits="0123456789."
android:minHeight="60dp"
android:textSize="24sp" />
<Button
android:id="@+id/btnSave"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:text="@string/btn_save"
android:textSize="24sp" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<!-- 日志部分 -->
<LinearLayout <LinearLayout
android:id="@+id/restartLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/ipConfigLayout" android:layout_below="@id/ipConfigLayout"
android:layout_marginTop="32dp" android:layout_marginTop="32dp"
android:gravity="center"
android:orientation="vertical">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/btnRestart"
android:layout_width="200dp"
android:layout_height="80dp"
android:text="@string/btn_restart_app"
android:textSize="18sp" />
<ProgressBar
android:id="@+id/restartProgressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="200dp"
android:layout_height="80dp"
android:max="100"
android:progress="0"
android:progressDrawable="@drawable/custom_progress_bar"
android:visibility="invisible" />
</FrameLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/restartLayout"
android:layout_marginTop="32dp"
android:orientation="vertical"> android:orientation="vertical">
<View <View

View File

@@ -16,4 +16,5 @@
<string name="log_placeholder">日志将显示在这里...</string> <string name="log_placeholder">日志将显示在这里...</string>
<string name="btn_random_expression">随机表情</string> <string name="btn_random_expression">随机表情</string>
<string name="btn_speak">让机器人说话</string> <string name="btn_speak">让机器人说话</string>
<string name="btn_restart_app">长按重启应用</string>
</resources> </resources>