关联用户滑动

This commit is contained in:
2026-05-23 08:57:31 +08:00
parent f3c1ae70d4
commit b16ced1c18
3 changed files with 117 additions and 38 deletions

View File

@@ -16,6 +16,7 @@ data class LinkedUser(
class LinkedUserAdapter( class LinkedUserAdapter(
private val deleteText: String, private val deleteText: String,
private val isUnlinkMode: Boolean,
private val onItemClick: (LinkedUser, Int) -> Unit, private val onItemClick: (LinkedUser, Int) -> Unit,
private val onDeleteClick: (LinkedUser, Int) -> Unit private val onDeleteClick: (LinkedUser, Int) -> Unit
) : RecyclerView.Adapter<LinkedUserAdapter.LinkedUserViewHolder>() { ) : RecyclerView.Adapter<LinkedUserAdapter.LinkedUserViewHolder>() {
@@ -35,6 +36,12 @@ class LinkedUserAdapter(
} }
} }
fun closeSwipe(position: Int) {
if (position in items.indices) {
notifyItemChanged(position)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LinkedUserViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LinkedUserViewHolder {
val binding = ItemLinkedUserBinding.inflate( val binding = ItemLinkedUserBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
@@ -56,18 +63,38 @@ class LinkedUserAdapter(
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: LinkedUser, position: Int) { fun bind(item: LinkedUser, position: Int) {
binding.tvPhone.text ="手机号码:${item.phone}" binding.tvPhone.text = "手机号码:${item.phone}"
binding.tvIdCard.text ="身份证号:${item.idCard}" binding.tvIdCard.text = "身份证号:${item.idCard}"
binding.llContent.translationX = 0f binding.llContent.translationX = 0f
binding.tvUserName.text = item.name binding.tvUserName.text = item.name
binding.tvTime.text = item.time binding.tvTime.text = item.time
//设置删除按钮的文字
// MODE_SETTINGS → 显示"解除关联"MODE_PERSONAL → 显示"删除"
if (isUnlinkMode) {
binding.tvUnlinkAction.visibility = View.VISIBLE
binding.tvDeleteAction.visibility = View.GONE
} else {
binding.tvUnlinkAction.visibility = View.GONE
binding.tvDeleteAction.visibility = View.VISIBLE
binding.tvDeleteAction.text = deleteText binding.tvDeleteAction.text = deleteText
binding.llContent.setOnClickListener {
onItemClick(item, position)
} }
binding.llDelete.setOnClickListener { binding.llContent.setOnClickListener {
if (binding.llContent.translationX < 0f) {
binding.llContent.animate()
.translationX(0f)
.setDuration(200)
.start()
} else {
onItemClick(item, position)
}
}
binding.tvDeleteAction.setOnClickListener {
onDeleteClick(item, position)
}
binding.tvUnlinkAction.setOnClickListener {
onDeleteClick(item, position) onDeleteClick(item, position)
} }
} }

View File

@@ -2,7 +2,7 @@ package com.example.defenseapplication.liveVideo
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -73,8 +73,10 @@ class LinkedUserActivity : AppCompatActivity() {
private fun initView() { private fun initView() {
val deleteText = if (mode == MODE_SETTINGS) getString(R.string.unlink) else getString(R.string.delete) val deleteText = if (mode == MODE_SETTINGS) getString(R.string.unlink) else getString(R.string.delete)
val isUnlinkMode = mode == MODE_SETTINGS
adapter = LinkedUserAdapter( adapter = LinkedUserAdapter(
deleteText = deleteText, deleteText = deleteText,
isUnlinkMode = isUnlinkMode,
onItemClick = { user, position -> onItemClick = { user, position ->
val intent = Intent(this, UserDetailsActivity::class.java).apply { val intent = Intent(this, UserDetailsActivity::class.java).apply {
putExtra("name", user.name) putExtra("name", user.name)
@@ -91,9 +93,14 @@ class LinkedUserActivity : AppCompatActivity() {
binding.rvLinkedUser.layoutManager = LinearLayoutManager(this) binding.rvLinkedUser.layoutManager = LinearLayoutManager(this)
binding.rvLinkedUser.adapter = adapter binding.rvLinkedUser.adapter = adapter
val swipeThreshold = -300f // 根据模式只显示一个按钮,滑动露出完整按钮
val swipeThresholdPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, -100f, resources.displayMetrics
)
val heldPositions = mutableMapOf<Int, Boolean>() val heldPositions = mutableMapOf<Int, Boolean>()
var currentlyHeldPosition: Int = -1
val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) { val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
override fun onMove( override fun onMove(
recyclerView: RecyclerView, recyclerView: RecyclerView,
@@ -117,21 +124,53 @@ class LinkedUserActivity : AppCompatActivity() {
val contentLayout = viewHolder.getContentLayout() val contentLayout = viewHolder.getContentLayout()
val position = viewHolder.adapterPosition val position = viewHolder.adapterPosition
// 如果当前滑动的位置和已展开的位置不同,先收起已展开的项
if (isCurrentlyActive && currentlyHeldPosition != -1 && currentlyHeldPosition != position) {
val oldViewHolder = recyclerView.findViewHolderForAdapterPosition(currentlyHeldPosition)
if (oldViewHolder is LinkedUserAdapter.LinkedUserViewHolder) {
oldViewHolder.getContentLayout().animate()
.translationX(0f)
.setDuration(200)
.start()
}
heldPositions.remove(currentlyHeldPosition)
currentlyHeldPosition = -1
}
if (isCurrentlyActive) { if (isCurrentlyActive) {
val swipeDistance = if (dX < swipeThreshold) swipeThreshold else dX val isHeld = heldPositions[position]
if (isHeld == true) {
if (dX > 0) {
// 已展开时向右滑动 → 平滑收回
val newX = swipeThresholdPx + dX
// 不能超过原位
contentLayout.translationX = if (newX > 0) 0f else newX
} else {
// 保持展开或继续左滑(限制不超过阈值)
contentLayout.translationX = swipeThresholdPx
}
} else {
// 未展开,正常滑动限制(左滑不超过阈值,右滑不超过原位)
val swipeDistance = when {
dX < swipeThresholdPx -> swipeThresholdPx
dX > 0 -> 0f
else -> dX
}
contentLayout.translationX = swipeDistance contentLayout.translationX = swipeDistance
}
} else { } else {
val isHeld = heldPositions[position] val isHeld = heldPositions[position]
if (isHeld == true) { if (isHeld == true) {
contentLayout.translationX = swipeThreshold contentLayout.translationX = swipeThresholdPx
} }
} }
} }
} }
/* /*
- 滑动超过一半后松手:列表项 保持在滑动位置 ,显示"解除关联"按钮 - 滑动超过一半后松手:列表项 保持在滑动位置,显示按钮
- 滑动未超过一半松手:列表项 恢复原位 - 滑动未超过一半松手:列表项 恢复原位
- 已展开状态下向右拖动归位或拖动超过一半松手:恢复原位
* */ * */
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder) super.clearView(recyclerView, viewHolder)
@@ -139,11 +178,16 @@ class LinkedUserActivity : AppCompatActivity() {
val position = viewHolder.adapterPosition val position = viewHolder.adapterPosition
val contentLayout = viewHolder.getContentLayout() val contentLayout = viewHolder.getContentLayout()
if (contentLayout.translationX < swipeThreshold / 2) { // 判断滑动距离是否超过一半阈值(注意都是负数,所以绝对值比较)
if (Math.abs(contentLayout.translationX) > Math.abs(swipeThresholdPx / 2)) {
heldPositions[position] = true heldPositions[position] = true
contentLayout.translationX = swipeThreshold currentlyHeldPosition = position
contentLayout.translationX = swipeThresholdPx
} else { } else {
heldPositions.remove(position) heldPositions.remove(position)
if (currentlyHeldPosition == position) {
currentlyHeldPosition = -1
}
contentLayout.animate() contentLayout.animate()
.translationX(0f) .translationX(0f)
.setDuration(300) .setDuration(300)

View File

@@ -1,40 +1,48 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<!-- 背景删除按钮 --> <!-- 背景按钮层:删除 + 解除关联 两个按钮 -->
<LinearLayout <LinearLayout
android:id="@+id/llDelete" android:id="@+id/llActions"
android:layout_width="100dp" android:layout_width="match_parent"
android:layout_height="120dp" android:layout_height="120dp"
android:background="@color/red" android:gravity="end"
android:gravity="center" android:orientation="horizontal">
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView <TextView
android:id="@+id/tvDeleteAction" android:id="@+id/tvDeleteAction"
android:layout_width="wrap_content" android:layout_width="100dp"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:background="@color/red"
android:gravity="center"
android:text="@string/delete"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvUnlinkAction"
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="@color/red"
android:gravity="center"
android:text="@string/unlink" android:text="@string/unlink"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold"/> android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<!-- 前景内容 --> <!-- 前景内容层:随 swipe 移动 -->
<LinearLayout <LinearLayout
android:id="@+id/llContent" android:id="@+id/llContent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="120dp" android:layout_height="120dp"
android:background="@color/white" android:background="@color/white"
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp" android:padding="16dp">
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -48,31 +56,31 @@
android:layout_weight="1" android:layout_weight="1"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold"/> android:textStyle="bold" />
<TextView <TextView
android:id="@+id/tvTime" android:id="@+id/tvTime"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="@color/gray" android:textColor="@color/gray"
android:textSize="12sp"/> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
<TextView <TextView
android:id="@+id/tvPhone" android:id="@+id/tvPhone"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textColor="@color/gray" android:textColor="@color/gray"
android:textSize="14sp" android:textSize="14sp" />
android:layout_marginTop="8dp"/>
<TextView <TextView
android:id="@+id/tvIdCard" android:id="@+id/tvIdCard"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textColor="@color/gray" android:textColor="@color/gray"
android:textSize="14sp" android:textSize="14sp" />
android:layout_marginTop="4dp"/>
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </FrameLayout>