自定义告警弹窗
This commit is contained in:
@@ -11,7 +11,9 @@ data class DeviceUi(
|
||||
val isOnline: Boolean
|
||||
)
|
||||
|
||||
class DeviceAdapter : RecyclerView.Adapter<DeviceAdapter.DeviceViewHolder>() {
|
||||
class DeviceAdapter(
|
||||
private val onItemClick: (DeviceUi) -> Unit = {}
|
||||
) : RecyclerView.Adapter<DeviceAdapter.DeviceViewHolder>() {
|
||||
|
||||
private val items = mutableListOf<DeviceUi>()
|
||||
|
||||
@@ -44,6 +46,9 @@ class DeviceAdapter : RecyclerView.Adapter<DeviceAdapter.DeviceViewHolder>() {
|
||||
binding.tvDeviceStatus.text = "\u25CF 离线"
|
||||
binding.tvDeviceStatus.setTextColor(binding.root.context.getColor(R.color.red))
|
||||
}
|
||||
binding.root.setOnClickListener {
|
||||
onItemClick(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.example.defenseapplication.home
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
@@ -16,13 +17,15 @@ import com.example.defenseapplication.adapter.DeviceAdapter
|
||||
import com.example.defenseapplication.adapter.DeviceUi
|
||||
import com.example.defenseapplication.adapter.FamilyAdapter
|
||||
import com.example.defenseapplication.databinding.HomeMenuFragmentBinding
|
||||
import com.example.defenseapplication.view.DefenseDialogs
|
||||
import com.example.defenseapplication.view.showDefenseDialog
|
||||
|
||||
class HomeMenuFragment : Fragment() {
|
||||
// ViewBinding
|
||||
private var _binding: HomeMenuFragmentBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val familyAdapter by lazy { FamilyAdapter(::onFamilySelected) }
|
||||
private val deviceAdapter by lazy { DeviceAdapter() }
|
||||
private val deviceAdapter by lazy { DeviceAdapter(::onDeviceClicked) }
|
||||
|
||||
private var isFamilyExpanded = false
|
||||
private var currentFamily = "我的家庭"
|
||||
@@ -63,6 +66,10 @@ class HomeMenuFragment : Fragment() {
|
||||
startColor = Color.parseColor("#EBDCED"),
|
||||
endColor = Color.parseColor("#CAF4FF")
|
||||
)
|
||||
binding.message.setOnClickListener {
|
||||
val intent = Intent(requireContext(), MessageActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
@@ -115,6 +122,13 @@ class HomeMenuFragment : Fragment() {
|
||||
deviceAdapter.submitList(familyDeviceMap[currentFamily].orEmpty())
|
||||
}
|
||||
|
||||
private fun onDeviceClicked(device: DeviceUi) {
|
||||
if (device.isOnline) return
|
||||
showDefenseDialog(
|
||||
config = DefenseDialogs.deviceOfflineConfig()
|
||||
)
|
||||
}
|
||||
|
||||
private fun dpToPx(dp: Int): Int {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.example.defenseapplication.home
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.example.defenseapplication.adapter.MessageListAdapter
|
||||
import com.example.defenseapplication.adapter.MessageListItem
|
||||
import com.example.defenseapplication.databinding.MessageActivityBinding
|
||||
|
||||
class MessageActivity : AppCompatActivity() {
|
||||
private lateinit var binding: MessageActivityBinding
|
||||
private val messageAdapter by lazy { MessageListAdapter() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = MessageActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.main) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
initView()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.ivBack.setOnClickListener { finish() }
|
||||
binding.rvMessageList.layoutManager = LinearLayoutManager(this)
|
||||
binding.rvMessageList.adapter = messageAdapter
|
||||
messageAdapter.submitList(buildMockData())
|
||||
}
|
||||
|
||||
private fun buildMockData(): List<MessageListItem> {
|
||||
return listOf(
|
||||
MessageListItem.Header("今天"),
|
||||
MessageListItem.Message(
|
||||
time = "15:54",
|
||||
title = "有人入侵",
|
||||
dateTime = "2026-05-06 15:54:25",
|
||||
unread = true
|
||||
),
|
||||
MessageListItem.Message(
|
||||
time = "12:54",
|
||||
title = "有人入侵",
|
||||
dateTime = "2026-05-06 15:54:25",
|
||||
unread = true
|
||||
),
|
||||
MessageListItem.Message(
|
||||
time = "11:54",
|
||||
title = "有人入侵",
|
||||
dateTime = "2026-05-06 15:54:25",
|
||||
unread = true
|
||||
),
|
||||
MessageListItem.Header("05-05"),
|
||||
MessageListItem.Message(
|
||||
time = "15:54",
|
||||
title = "设备故障",
|
||||
dateTime = "2026-05-06 15:54:25",
|
||||
unread = false
|
||||
),
|
||||
MessageListItem.Message(
|
||||
time = "08:07",
|
||||
title = "设备离线",
|
||||
dateTime = "2026-05-06 15:54:25",
|
||||
unread = false
|
||||
),
|
||||
MessageListItem.Message(
|
||||
time = "08:07",
|
||||
title = "到期提醒",
|
||||
dateTime = "2026-05-06 15:54:25",
|
||||
unread = false
|
||||
),
|
||||
MessageListItem.Message(
|
||||
time = "11:54",
|
||||
title = "有人入侵",
|
||||
dateTime = "2026-05-06 15:54:25",
|
||||
unread = false
|
||||
),
|
||||
MessageListItem.Message(
|
||||
time = "11:54",
|
||||
title = "有人入侵",
|
||||
dateTime = "2026-05-06 15:54:25",
|
||||
unread = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.example.defenseapplication.home
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.bumptech.glide.Glide
|
||||
import com.example.defenseapplication.R
|
||||
import com.example.defenseapplication.databinding.PersonalFragmentBinding
|
||||
import com.example.defenseapplication.personal.PersonalProfileActivity
|
||||
|
||||
class PersonalFragment : Fragment() {
|
||||
// ViewBinding
|
||||
@@ -19,6 +22,14 @@ class PersonalFragment : Fragment() {
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = PersonalFragmentBinding.inflate(inflater, container, false)
|
||||
Glide.with(this)
|
||||
.load(R.mipmap.ic_launcher_round)
|
||||
.circleCrop()
|
||||
.into(binding.ivAvatar)
|
||||
binding.headerLayout.setOnClickListener {
|
||||
val intent= Intent(requireContext(), PersonalProfileActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.example.defenseapplication.personal
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.bumptech.glide.Glide
|
||||
import com.example.defenseapplication.R
|
||||
import com.example.defenseapplication.databinding.PersonalProfileActivityBinding
|
||||
|
||||
class PersonalProfileActivity : AppCompatActivity() {
|
||||
private lateinit var binding: PersonalProfileActivityBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = PersonalProfileActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.main) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
initView()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.ivBack.setOnClickListener { finish() }
|
||||
|
||||
// 默认展示数据(后续可接你的用户信息接口/数据库)
|
||||
Glide.with(this)
|
||||
.load(R.mipmap.ic_launcher_round)
|
||||
.circleCrop()
|
||||
.into(binding.ivAvatar)
|
||||
|
||||
binding.tvNickname.text = "这里是我的昵称"
|
||||
binding.tvPhone.text = "13589898989"
|
||||
|
||||
// 点击行可跳转到“修改昵称/修改手机号”等页面
|
||||
//binding.itemAvatar.setOnClickListener { /* TODO */ }
|
||||
//binding.itemNickname.setOnClickListener { /* TODO */ }
|
||||
//binding.itemPhone.setOnClickListener { /* TODO */ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package com.example.defenseapplication.view
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.example.defenseapplication.R
|
||||
|
||||
class CustomDialogFragment : androidx.fragment.app.DialogFragment() {
|
||||
|
||||
data class Config(
|
||||
val title: String,
|
||||
val message: String = "",
|
||||
val positiveText: String = "确定",
|
||||
val negativeText: String? = "取消",
|
||||
val cancelOnTouchOutside: Boolean = true,
|
||||
val showInput: Boolean = false,
|
||||
val inputHint: String = "",
|
||||
val inputText: String = "",
|
||||
val inputType: Int = InputType.TYPE_CLASS_TEXT
|
||||
)
|
||||
|
||||
private var onPositiveClick: ((String) -> Unit)? = null
|
||||
private var onNegativeClick: (() -> Unit)? = null
|
||||
private lateinit var config: Config
|
||||
|
||||
fun setOnPositiveClick(block: (String) -> Unit): CustomDialogFragment = apply {
|
||||
onPositiveClick = block
|
||||
}
|
||||
|
||||
fun setOnNegativeClick(block: () -> Unit): CustomDialogFragment = apply {
|
||||
onNegativeClick = block
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
config = Config(
|
||||
title = requireArguments().getString(ARG_TITLE).orEmpty(),
|
||||
message = requireArguments().getString(ARG_MESSAGE).orEmpty(),
|
||||
positiveText = requireArguments().getString(ARG_POSITIVE_TEXT).orEmpty(),
|
||||
negativeText = requireArguments().getString(ARG_NEGATIVE_TEXT),
|
||||
cancelOnTouchOutside = requireArguments().getBoolean(ARG_CANCEL_ON_TOUCH_OUTSIDE),
|
||||
showInput = requireArguments().getBoolean(ARG_SHOW_INPUT),
|
||||
inputHint = requireArguments().getString(ARG_INPUT_HINT).orEmpty(),
|
||||
inputText = requireArguments().getString(ARG_INPUT_TEXT).orEmpty(),
|
||||
inputType = requireArguments().getInt(ARG_INPUT_TYPE)
|
||||
)
|
||||
isCancelable = config.cancelOnTouchOutside
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply {
|
||||
setCanceledOnTouchOutside(config.cancelOnTouchOutside)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return inflater.inflate(R.layout.dialog_custom, container, false)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
dialog?.window?.let { window ->
|
||||
window.setLayout(
|
||||
(resources.displayMetrics.widthPixels * 0.88f).toInt(),
|
||||
WindowManager.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
window.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val tvTitle = view.findViewById<TextView>(R.id.tv_title)
|
||||
val tvMessage = view.findViewById<TextView>(R.id.tv_message)
|
||||
val etInput = view.findViewById<EditText>(R.id.et_input)
|
||||
val btnNegative = view.findViewById<TextView>(R.id.btn_negative)
|
||||
val btnPositive = view.findViewById<TextView>(R.id.btn_positive)
|
||||
val dividerCenter = view.findViewById<View>(R.id.v_divider_center)
|
||||
|
||||
tvTitle.text = config.title
|
||||
tvMessage.text = config.message
|
||||
tvMessage.visibility = if (config.message.isBlank()) View.GONE else View.VISIBLE
|
||||
|
||||
etInput.visibility = if (config.showInput) View.VISIBLE else View.GONE
|
||||
etInput.hint = config.inputHint
|
||||
etInput.setText(config.inputText)
|
||||
etInput.inputType = config.inputType
|
||||
|
||||
btnPositive.text = config.positiveText
|
||||
|
||||
val hasNegative = !config.negativeText.isNullOrBlank()
|
||||
btnNegative.visibility = if (hasNegative) View.VISIBLE else View.GONE
|
||||
dividerCenter.visibility = if (hasNegative) View.VISIBLE else View.GONE
|
||||
if (hasNegative) {
|
||||
btnNegative.text = config.negativeText
|
||||
} else {
|
||||
btnPositive.layoutParams = LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
|
||||
btnNegative.setOnClickListener {
|
||||
onNegativeClick?.invoke()
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
btnPositive.setOnClickListener {
|
||||
onPositiveClick?.invoke(etInput.text?.toString().orEmpty())
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARG_TITLE = "arg_title"
|
||||
private const val ARG_MESSAGE = "arg_message"
|
||||
private const val ARG_POSITIVE_TEXT = "arg_positive_text"
|
||||
private const val ARG_NEGATIVE_TEXT = "arg_negative_text"
|
||||
private const val ARG_CANCEL_ON_TOUCH_OUTSIDE = "arg_cancel_on_touch_outside"
|
||||
private const val ARG_SHOW_INPUT = "arg_show_input"
|
||||
private const val ARG_INPUT_HINT = "arg_input_hint"
|
||||
private const val ARG_INPUT_TEXT = "arg_input_text"
|
||||
private const val ARG_INPUT_TYPE = "arg_input_type"
|
||||
|
||||
fun newInstance(config: Config): CustomDialogFragment {
|
||||
return CustomDialogFragment().apply {
|
||||
arguments = bundleOf(
|
||||
ARG_TITLE to config.title,
|
||||
ARG_MESSAGE to config.message,
|
||||
ARG_POSITIVE_TEXT to config.positiveText,
|
||||
ARG_NEGATIVE_TEXT to config.negativeText,
|
||||
ARG_CANCEL_ON_TOUCH_OUTSIDE to config.cancelOnTouchOutside,
|
||||
ARG_SHOW_INPUT to config.showInput,
|
||||
ARG_INPUT_HINT to config.inputHint,
|
||||
ARG_INPUT_TEXT to config.inputText,
|
||||
ARG_INPUT_TYPE to config.inputType
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DefenseDialogs {
|
||||
private const val DEFAULT_TAG = "custom_dialog_fragment"
|
||||
|
||||
fun showDialog(
|
||||
activity: FragmentActivity,
|
||||
config: CustomDialogFragment.Config,
|
||||
tag: String = DEFAULT_TAG,
|
||||
onPositiveClick: (String) -> Unit = {},
|
||||
onNegativeClick: () -> Unit = {}
|
||||
): CustomDialogFragment {
|
||||
return CustomDialogFragment.newInstance(config)
|
||||
.setOnPositiveClick(onPositiveClick)
|
||||
.setOnNegativeClick(onNegativeClick)
|
||||
.also { it.show(activity.supportFragmentManager, tag) }
|
||||
}
|
||||
|
||||
fun showDialog(
|
||||
fragment: Fragment,
|
||||
config: CustomDialogFragment.Config,
|
||||
tag: String = DEFAULT_TAG,
|
||||
onPositiveClick: (String) -> Unit = {},
|
||||
onNegativeClick: () -> Unit = {}
|
||||
): CustomDialogFragment {
|
||||
return CustomDialogFragment.newInstance(config)
|
||||
.setOnPositiveClick(onPositiveClick)
|
||||
.setOnNegativeClick(onNegativeClick)
|
||||
.also { it.show(fragment.parentFragmentManager, tag) }
|
||||
}
|
||||
|
||||
fun intrusionAlertConfig(deviceName: String, timeText: String): CustomDialogFragment.Config {
|
||||
return CustomDialogFragment.Config(
|
||||
title = "入侵告警",
|
||||
message = "${timeText}有人入侵\n【${deviceName}】",
|
||||
positiveText = "去看一下",
|
||||
negativeText = "取消"
|
||||
)
|
||||
}
|
||||
|
||||
fun warmPromptConfig(deviceName: String, runDays: Int): CustomDialogFragment.Config {
|
||||
return CustomDialogFragment.Config(
|
||||
title = "温馨提示",
|
||||
message = "【${deviceName}】设备已经运行\n${runDays}天请及时维护",
|
||||
positiveText = "知道了",
|
||||
negativeText = null
|
||||
)
|
||||
}
|
||||
|
||||
fun deviceOfflineConfig(): CustomDialogFragment.Config {
|
||||
return CustomDialogFragment.Config(
|
||||
title = "设备离线",
|
||||
message = "设备已离线,请检查设备的WIF及电\n源情况",
|
||||
positiveText = "知道了",
|
||||
negativeText = null
|
||||
)
|
||||
}
|
||||
|
||||
fun strikeDistanceConfig(defaultDistance: String): CustomDialogFragment.Config {
|
||||
return CustomDialogFragment.Config(
|
||||
title = "打击距离",
|
||||
message = "",
|
||||
positiveText = "确定",
|
||||
negativeText = "取消",
|
||||
showInput = true,
|
||||
inputHint = "请输入打击距离",
|
||||
inputText = defaultDistance,
|
||||
inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun FragmentActivity.showDefenseDialog(
|
||||
config: CustomDialogFragment.Config,
|
||||
tag: String = "custom_dialog_fragment",
|
||||
onPositiveClick: (String) -> Unit = {},
|
||||
onNegativeClick: () -> Unit = {}
|
||||
): CustomDialogFragment {
|
||||
return DefenseDialogs.showDialog(this, config, tag, onPositiveClick, onNegativeClick)
|
||||
}
|
||||
|
||||
fun Fragment.showDefenseDialog(
|
||||
config: CustomDialogFragment.Config,
|
||||
tag: String = "custom_dialog_fragment",
|
||||
onPositiveClick: (String) -> Unit = {},
|
||||
onNegativeClick: () -> Unit = {}
|
||||
): CustomDialogFragment {
|
||||
return DefenseDialogs.showDialog(this, config, tag, onPositiveClick, onNegativeClick)
|
||||
}
|
||||
|
||||
fun FragmentManager.showDefenseDialog(
|
||||
config: CustomDialogFragment.Config,
|
||||
tag: String = "custom_dialog_fragment",
|
||||
onPositiveClick: (String) -> Unit = {},
|
||||
onNegativeClick: () -> Unit = {}
|
||||
): CustomDialogFragment {
|
||||
return CustomDialogFragment.newInstance(config)
|
||||
.setOnPositiveClick(onPositiveClick)
|
||||
.setOnNegativeClick(onNegativeClick)
|
||||
.also { it.show(this, tag) }
|
||||
}
|
||||
Reference in New Issue
Block a user