问题描述
我在Jetpack Compose中实现了一列按钮。我们意识到可以同时单击多个项目(例如,使用多个手指),并且我们希望禁用此功能。
是否有现成的方法可以使用父列修饰符同时禁用对子合成内容的多个单击?
以下是我的用户界面的当前状态示例,请注意有两个选中的项目和两个未选中的项目。
以下是它如何实现的一些代码(精简)
Column(
modifier = modifier
.fillMaxSize()
.verticalScroll(nestedScrollParams.childScrollState),
) {
viewDataList.forEachIndexed { index, viewData ->
Row(modifier = modifier.fillMaxWidth()
.height(dimensionResource(id = 48.dp)
.background(colorResource(id = R.color.large_button_background))
.clickable { onClick(viewData) },
verticalAlignment = Alignment.CenterVertically
) {
//Internal composables, etc
}
}
推荐答案
以下是四个解决方案:
单击去反弹(视图模型)r
为此,您需要使用视图模型。视图模型处理单击事件。您应该传递一些id(或数据)来标识被点击的项。在您的示例中,您可以传递一个分配给每个项目的id(如按钮id):
// IMPORTANT: Make sure to import kotlinx.coroutines.flow.collect
class MyViewModel : ViewModel() {
val debounceState = MutableStateFlow<String?>(null)
init {
viewModelScope.launch {
debounceState
.debounce(300)
.collect { buttonId ->
if (buttonId != null) {
when (buttonId) {
ButtonIds.Support -> displaySupport()
ButtonIds.About -> displayAbout()
ButtonIds.TermsAndService -> displayTermsAndService()
ButtonIds.Privacy -> displayPrivacy()
}
}
}
}
}
fun onItemClick(buttonId: String) {
debounceState.value = buttonId
}
}
object ButtonIds {
const val Support = "support"
const val About = "about"
const val TermsAndService = "termsAndService"
const val Privacy = "privacy"
}
取消保镖会忽略从最后一次收到的500毫秒内传入的任何点击。我已经测试过了,它起作用了。您永远不能一次单击多个项目。虽然您一次可以触摸两个并且这两个都将突出显示,但只有您触摸的第一个将生成点击处理程序。
单击去弹跳器(修改器)
这是对点击去保护器的另一种处理,但被设计为用作修饰语。这可能是你最想用到的。大多数应用程序都会使用滚动列表,让你轻点列表项。如果您多次快速点击一个项目,clickable
修饰符中的代码将执行多次。这可能是一件麻烦的事。虽然用户通常不会多次点击,但我见过即使是偶然的双击也会触发Clickable两次。由于您希望在您的应用程序中不仅在列表上而且在按钮上避免这种情况,您可能应该使用自定义修饰符来修复这个问题,而不必求助于上面所示的视图模型方法。
创建自定义修改器。我已将其命名为onClick
:
fun Modifier.onClick(
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
onClick: () -> Unit
) = composed(
inspectorInfo = debugInspectorInfo {
name = "clickable"
properties["enabled"] = enabled
properties["onClickLabel"] = onClickLabel
properties["role"] = role
properties["onClick"] = onClick
}
) {
Modifier.clickable(
enabled = enabled,
onClickLabel = onClickLabel,
onClick = {
App.debounceClicks {
onClick.invoke()
}
},
role = role,
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
)
}
您会注意到,在上面的代码中,我使用了App.debounceClicks
。这当然不存在于你的应用程序中。你需要在你的应用程序中的某个地方创建这个函数,它可以在全球范围内访问。这可能是一个单例对象。在我的代码中,我使用了一个继承自应用程序的类,因为这是应用程序启动时实例化的类:
class App : Application() {
override fun onCreate() {
super.onCreate()
}
companion object {
private val debounceState = MutableStateFlow { }
init {
GlobalScope.launch(Dispatchers.Main) {
// IMPORTANT: Make sure to import kotlinx.coroutines.flow.collect
debounceState
.debounce(300)
.collect { onClick ->
onClick.invoke()
}
}
}
fun debounceClicks(onClick: () -> Unit) {
debounceState.value = onClick
}
}
}
别忘了在你的androidManifest中包括你的类名:
<application
android:name=".App"
现在不使用clickable
,而使用onClick
:
Text("Do Something", modifier = Modifier.onClick { })
全局禁用多点触控
在主活动中,覆盖DispatchTouchEvent:
class MainActivity : AppCompatActivity() {
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
return ev?.getPointerCount() == 1 && super.dispatchTouchEvent(ev)
}
}
这将全局禁用多点触控。如果你的应用程序有谷歌地图,你会想要向dispatchTouchEvent添加一些代码,以确保当显示地图的屏幕可见时,它仍保持启用状态。用户将使用两个手指在地图上进行缩放,这需要启用多点触摸。
状态管理的点击处理程序
使用单个单击事件处理程序来存储被单击的项的状态。当第一个项目调用Click时,它设置状态以指示Click处理程序是&正在使用&。如果第二个项尝试调用单击处理程序,并且将&in-use";设置为TRUE,则它只返回,而不执行处理程序的代码。这实质上等同于同步处理程序,但任何进一步的调用都将被忽略,而不是阻塞。这篇关于如何禁用同时点击Jetpack Compose List/Column/Row(开箱即用)中的多个项目?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!