关联用户

This commit is contained in:
2026-05-10 11:10:39 +08:00
parent 3df00ad493
commit 2c94ba767d
13 changed files with 584 additions and 9 deletions

View File

@@ -0,0 +1,73 @@
package com.example.defenseapplication.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.databinding.ItemLinkedUserBinding
data class LinkedUser(
val name: String,
val phone: String,
val idCard: String,
val time: String
)
class LinkedUserAdapter(
private val onItemClick: (LinkedUser, Int) -> Unit,
private val onDeleteClick: (LinkedUser, Int) -> Unit
) : RecyclerView.Adapter<LinkedUserAdapter.LinkedUserViewHolder>() {
private val items = mutableListOf<LinkedUser>()
fun submitList(userList: List<LinkedUser>) {
items.clear()
items.addAll(userList)
notifyDataSetChanged()
}
fun removeItem(position: Int) {
if (position in items.indices) {
items.removeAt(position)
notifyItemRemoved(position)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LinkedUserViewHolder {
val binding = ItemLinkedUserBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return LinkedUserViewHolder(binding)
}
override fun onBindViewHolder(holder: LinkedUserViewHolder, position: Int) {
holder.bind(items[position], position)
}
override fun getItemCount(): Int = items.size
inner class LinkedUserViewHolder(
private val binding: ItemLinkedUserBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: LinkedUser, position: Int) {
binding.llContent.translationX = 0f
binding.tvUserName.text = item.name
binding.tvPhone.text = "手机号码:${item.phone}"
binding.tvIdCard.text = "身份证号:${item.idCard}"
binding.tvTime.text = item.time
binding.llContent.setOnClickListener {
onItemClick(item, position)
}
binding.llDelete.setOnClickListener {
onDeleteClick(item, position)
}
}
fun getContentLayout(): View = binding.llContent
}
}

View File

@@ -1,27 +1,199 @@
package com.example.defenseapplication.liveVideo
import android.content.Intent
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.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.DeviceInfoActivityBinding
import com.example.defenseapplication.adapter.LinkedUser
import com.example.defenseapplication.adapter.LinkedUserAdapter
import com.example.defenseapplication.databinding.LinkedUserActivityBinding
import com.example.defenseapplication.view.CustomDialogFragmentText
import com.gyf.immersionbar.ImmersionBar
class LinkedUserActivity : AppCompatActivity() {
private lateinit var binding: LinkedUserActivityBinding
private lateinit var adapter: LinkedUserAdapter
private val userList = mutableListOf<LinkedUser>()
companion object {
const val REQUEST_CODE_SEARCH = 1001
const val EXTRA_SEARCH_RESULT = "search_result"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LinkedUserActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.titleBar(binding.topBar)
.init()
initView()
initData()
initListener()
}
private fun initView() {
adapter = LinkedUserAdapter(
onItemClick = { user, position ->
},
onDeleteClick = { user, position ->
showUnlinkDialog(user, position)
}
)
binding.rvLinkedUser.layoutManager = LinearLayoutManager(this)
binding.rvLinkedUser.adapter = adapter
val swipeThreshold = -300f
val heldPositions = mutableMapOf<Int, Boolean>()
val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean = false
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
override fun onChildDraw(
c: android.graphics.Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
if (viewHolder is LinkedUserAdapter.LinkedUserViewHolder) {
val contentLayout = viewHolder.getContentLayout()
val position = viewHolder.adapterPosition
if (isCurrentlyActive) {
val swipeDistance = if (dX < swipeThreshold) swipeThreshold else dX
contentLayout.translationX = swipeDistance
} else {
val isHeld = heldPositions[position]
if (isHeld == true) {
contentLayout.translationX = swipeThreshold
}
}
}
}
/*
- 滑动超过一半后松手:列表项 保持在滑动位置 ,显示"解除关联"按钮
- 滑动未超过一半松手:列表项 恢复原位
* */
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
if (viewHolder is LinkedUserAdapter.LinkedUserViewHolder) {
val position = viewHolder.adapterPosition
val contentLayout = viewHolder.getContentLayout()
if (contentLayout.translationX < swipeThreshold / 2) {
heldPositions[position] = true
contentLayout.translationX = swipeThreshold
} else {
heldPositions.remove(position)
contentLayout.animate()
.translationX(0f)
.setDuration(300)
.start()
}
}
}
override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float = 0.05f
})
itemTouchHelper.attachToRecyclerView(binding.rvLinkedUser)
}
private var currentSwipePosition: Int = -1
private var currentSwipeHeld: Boolean = false
private fun showUnlinkDialog(user: LinkedUser, position: Int) {
currentSwipePosition = position
currentSwipeHeld = true
val dialog = CustomDialogFragmentText()
dialog.setTitle("解除关联")
dialog.setMessage("确定要解除与 ${user.name} 的关联吗?")
dialog.setShowInput(false)
dialog.setPositiveText("确定")
dialog.setNegativeText("取消")
dialog.setOnConfirmListener {
userList.removeAt(position)
adapter.removeItem(position)
resetSwipeState()
}
dialog.setOnDismissListener {
resetSwipeState()
}
dialog.show(supportFragmentManager, "UnlinkDialog")
}
private fun resetSwipeState() {
if (currentSwipePosition >= 0 && currentSwipeHeld) {
binding.rvLinkedUser.post {
val viewHolder = binding.rvLinkedUser.findViewHolderForAdapterPosition(currentSwipePosition)
if (viewHolder is LinkedUserAdapter.LinkedUserViewHolder) {
viewHolder.getContentLayout().animate()
.translationX(0f)
.setDuration(300)
.start()
}
currentSwipePosition = -1
currentSwipeHeld = false
}
}
}
private fun initData() {
userList.clear()
userList.add(LinkedUser("张三", "12388888888", "123456789012345678", "2026-05-06 16:03:26"))
userList.add(LinkedUser("李四", "13899999999", "987654321098765432", "2026-05-05 14:20:15"))
userList.add(LinkedUser("王五", "15977777777", "456123789012345678", "2026-05-04 10:15:30"))
adapter.submitList(userList)
}
private fun initListener() {
binding.ivBack.setOnClickListener {
finish()
}
binding.searchLayout.setOnClickListener {
val intent = Intent(this, LinkedUserSearchActivity::class.java)
startActivityForResult(intent, REQUEST_CODE_SEARCH)
}
binding.btnAddLinkedUser.setOnClickListener {
val intent = Intent(this, LinkedUserSearchActivity::class.java)
startActivityForResult(intent, REQUEST_CODE_SEARCH)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_SEARCH && resultCode == RESULT_OK) {
data?.getStringExtra(EXTRA_SEARCH_RESULT)?.let { result ->
val newUser = LinkedUser(
name = result,
phone = "13800000000",
idCard = "110101199001011234",
time = "2026-05-10 ${android.text.format.DateFormat.format("HH:mm:ss", System.currentTimeMillis())}"
)
userList.add(0, newUser)
adapter.submitList(userList)
}
}
}
}

View File

@@ -0,0 +1,44 @@
package com.example.defenseapplication.liveVideo
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.defenseapplication.databinding.LinkedUserSearchActivityBinding
import com.gyf.immersionbar.ImmersionBar
class LinkedUserSearchActivity : AppCompatActivity() {
private lateinit var binding: LinkedUserSearchActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LinkedUserSearchActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.titleBar(binding.topBar)
.init()
initListener()
initData()
}
private fun initListener() {
binding.ivBack.setOnClickListener {
finish()
}
binding.btnSearch.setOnClickListener {
initData()
}
}
private fun initData() {
val searchText = binding.etSearch.text.toString().trim()
binding.rvSearchResult.layoutManager = LinearLayoutManager(this)
}
}

View File

@@ -10,7 +10,7 @@ import com.example.defenseapplication.view.CustomDialogFragmentText
import com.example.defenseapplication.view.LanguageDialog
import com.example.defenseapplication.view.RecognitionModeDialog
import com.gyf.immersionbar.ImmersionBar
//设置页面
class SettingsActivity : BaseActivity() {
private lateinit var binding: SettingsActivityBinding
@@ -74,6 +74,10 @@ class SettingsActivity : BaseActivity() {
val intent = Intent(this, DeviceInfoActivity::class.java)
startActivity(intent)
}
binding.itemRelatedUsers.setOnClickListener {
val intent = Intent(this, LinkedUserActivity::class.java)
startActivity( intent)
}
}

View File

@@ -23,6 +23,7 @@ class CustomDialogFragmentText : DialogFragment() {
private var positiveText: String = ""
private var showInput: Boolean = true
private var onConfirmListener: ((String) -> Unit)? = null
private var onDismissListener: (() -> Unit)? = null
fun setTitle(title: String): CustomDialogFragmentText {
this.title = title
@@ -63,6 +64,10 @@ class CustomDialogFragmentText : DialogFragment() {
this.onConfirmListener = listener
}
fun setOnDismissListener(listener: () -> Unit) {
this.onDismissListener = listener
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.let {
@@ -87,7 +92,6 @@ class CustomDialogFragmentText : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Set defaults if not explicitly configured
if (negativeText.isEmpty()) negativeText = getString(R.string.cancel)
if (positiveText.isEmpty()) positiveText = getString(R.string.confirm)
binding.tvTitle.text = title
@@ -112,6 +116,8 @@ class CustomDialogFragmentText : DialogFragment() {
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
onDismissListener?.invoke()
onConfirmListener = null
onDismissListener = null
}
}