fix(phone): PhoneDataLayout 下拉改为底部弹出列表

筛选面板与弹框里的 PhoneDataLayout 原用系统 Spinner,点开是贴在输入框下的下拉菜单。
SPINNER 形态改为与 PhoneFormRow 同款 XPopup 底部列表(标题取 hint),保留原取值语义:
无 hint 默认选中第一项、有 hint 为空值,筛选「重置」逻辑不变。19 个手机布局随之生效。

Refs: 禅道 Bug #623

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
YANG JIANKUAN
2026-10-14 20:21:04 -07:00
parent fa6c3f473e
commit d1d995c698

View File

@@ -13,12 +13,10 @@ import android.widget.TextView
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.databinding.InverseBindingListener import androidx.databinding.InverseBindingListener
import com.lukouguoji.module_base.R import com.lukouguoji.module_base.R
import com.lukouguoji.module_base.adapter.bindAdapter
import com.lukouguoji.module_base.adapter.bindOnSelected
import com.lukouguoji.module_base.interfaces.IOnSpinnerSelected
import com.lukouguoji.module_base.ktx.formatDate import com.lukouguoji.module_base.ktx.formatDate
import com.lukouguoji.module_base.ktx.getActivity import com.lukouguoji.module_base.ktx.getActivity
import com.lukouguoji.module_base.util.Common import com.lukouguoji.module_base.util.Common
import com.lxj.xpopup.XPopup
import dev.utils.app.info.KeyValue import dev.utils.app.info.KeyValue
import java.util.Calendar import java.util.Calendar
@@ -32,7 +30,7 @@ import java.util.Calendar
* |-----------|-----------|----------| * |-----------|-----------|----------|
* | `INPUT` | EditText | 无 | * | `INPUT` | EditText | 无 |
* | `DATE` | TextView(点击弹日历)| 日历 | * | `DATE` | TextView(点击弹日历)| 日历 |
* | `SPINNER` | Spinner | 下箭头 | * | `SPINNER` | TextView(点击弹底部列表,与 PhoneFormRow SELECT 同款)| 下箭头 |
* *
* XML 用法: * XML 用法:
* ```xml * ```xml
@@ -58,6 +56,7 @@ class PhoneDataLayout @JvmOverloads constructor(
val llContent: LinearLayout val llContent: LinearLayout
val et: EditText val et: EditText
val tv: TextView val tv: TextView
/** 保留以兼容布局 id;Bug #623 起 SPINNER 形态改为底部弹出列表,原生 Spinner 恒隐藏 */
val spinner: Spinner val spinner: Spinner
val iv: ImageView val iv: ImageView
@@ -85,7 +84,7 @@ class PhoneDataLayout @JvmOverloads constructor(
field = v field = v
et.hint = v et.hint = v
tv.hint = v tv.hint = v
bindAdapter(spinner, list, v, R.layout.item_spinner_list_14sp) applyDefaultSelection()
} }
var value: String = "" var value: String = ""
@@ -99,7 +98,7 @@ class PhoneDataLayout @JvmOverloads constructor(
var list: List<KeyValue> = emptyList() var list: List<KeyValue> = emptyList()
set(v) { set(v) {
field = v field = v
bindAdapter(spinner, v, hint, R.layout.item_spinner_list_14sp) applyDefaultSelection()
applyValue() applyValue()
} }
@@ -114,7 +113,6 @@ class PhoneDataLayout @JvmOverloads constructor(
field = v field = v
llContent.isEnabled = v llContent.isEnabled = v
et.isEnabled = v et.isEnabled = v
spinner.isEnabled = v
} }
init { init {
@@ -136,13 +134,6 @@ class PhoneDataLayout @JvmOverloads constructor(
} }
} }
bindOnSelected(spinner, object : IOnSpinnerSelected {
override fun onSelected(position: Int) {
value = list.getOrNull(position)?.value ?: ""
refreshCallBack?.invoke()
}
})
applyType() applyType()
} }
@@ -157,6 +148,28 @@ class PhoneDataLayout @JvmOverloads constructor(
} }
} }
/** SPINNER:底部弹出列表(标题取 hint,无 hint 时取 title),选中后回写 value 并回调 */
private val spinnerClick: (v: View) -> Unit = {
if (enable && list.isNotEmpty()) {
XPopup.Builder(context)
.asBottomList(hint.ifEmpty { title }, list.map { it.key }.toTypedArray()) { position, _ ->
value = list.getOrNull(position)?.value ?: ""
refreshCallBack?.invoke()
}
.show()
}
}
/**
* 保持原生 Spinner 的默认取值语义:无 hint 时自动选中第一项(value 为空才补),
* 有 hint 时保持空值显示占位文字。
*/
private fun applyDefaultSelection() {
if (type == PhoneDataLayoutType.SPINNER && hint.isEmpty() && value.isEmpty() && list.isNotEmpty()) {
value = list[0].value
}
}
private fun applyType() { private fun applyType() {
when (type) { when (type) {
PhoneDataLayoutType.INPUT -> { PhoneDataLayoutType.INPUT -> {
@@ -179,12 +192,12 @@ class PhoneDataLayout @JvmOverloads constructor(
PhoneDataLayoutType.SPINNER -> { PhoneDataLayoutType.SPINNER -> {
et.visibility = GONE et.visibility = GONE
tv.visibility = GONE tv.visibility = VISIBLE
spinner.visibility = VISIBLE spinner.visibility = GONE
iv.visibility = VISIBLE iv.visibility = VISIBLE
iv.setImageResource(R.drawable.ic_phone_chevron_down) iv.setImageResource(R.drawable.ic_phone_chevron_down)
llContent.setOnClickListener(null) llContent.setOnClickListener(spinnerClick)
llContent.isClickable = false applyDefaultSelection()
} }
} }
} }
@@ -200,10 +213,8 @@ class PhoneDataLayout @JvmOverloads constructor(
} }
PhoneDataLayoutType.SPINNER -> { PhoneDataLayoutType.SPINNER -> {
if (value.isNotEmpty()) { // 显示选中项文案;列表里找不到就按原值显示(如后端返回的历史值),空值显示 hint
val position = list.indexOfFirst { it.value == value } tv.text = list.firstOrNull { it.value == value }?.key ?: value
if (position >= 0) spinner.setSelection(position)
}
} }
} }
} }
@@ -223,10 +234,10 @@ class PhoneDataLayout @JvmOverloads constructor(
/** 重置为空值(筛选弹层「重置」按钮使用) */ /** 重置为空值(筛选弹层「重置」按钮使用) */
fun reset() { fun reset() {
value = "" // 与原 Spinner 版本一致:有 hint 回到占位(空值);无 hint 选中最后一项
if (type == PhoneDataLayoutType.SPINNER && list.isNotEmpty()) { value = if (type == PhoneDataLayoutType.SPINNER && hint.isEmpty() && list.isNotEmpty()) {
spinner.setSelection(if (hint.isEmpty()) list.size - 1 else list.size) list.last().value
} } else ""
} }
} }