在 Android,有什么方法可以禁用 Spinner 的长按行为吗?
On Android, is there any way to disable a Spinner's long-press behavior?
默认的微调器行为是,当它处于 "closed" 时,长按它会 "open" 它并显示下拉视图。我发现这种行为对用户来说可能是非常有问题的。例如,如果他们试图在屏幕上滚动某些东西,并且碰巧 "grab" 一个有微调器的地方,那么它不会滚动,而是在一秒钟左右后打开下拉视图,然后用户基本上只剩下手指放在其中一个下拉选项上(他们现在可能会不小心按下)。
所以,我想禁用长按行为,并在仅单击而不是长按时使用微调器 "open"。这可能吗?
你在 xml 中尝试过 android:longClickable="false" 了吗?
或者尝试这样的事情:
spinner.setOnTouchListener(new View.OnTouchListener() {
private static final int MAX_CLICK_DISTANCE = 15;
private static final int MAX_CLICK_DURATION = 1000;
private long pressStartTime;
private float pressedX;
private float pressedY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
pressStartTime = System.currentTimeMillis();
pressedX = event.getX();
pressedY = event.getY();
break;
}
case MotionEvent.ACTION_UP: {
long pressDuration = System.currentTimeMillis() - pressStartTime;
if (pressDuration < MAX_CLICK_DURATION && distance(pressedX, pressedY, event.getX(), event.getY()) < MAX_CLICK_DISTANCE) {
//when long clicked.
}
break;
}
}
return false;
}
所以,我想出了一个相对简单的方法来做到这一点,尽管它不是很优雅。基本上,我在 Spinner 的顶部创建了一个透明的覆盖视图,并将其设置为具有仅触发 Spinner 的点击的 OnClickListener。
XML:
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="40dip" />
<View android:id="@+id/spinner_overlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/spinner"
android:layout_alignRight="@id/spinner"
android:layout_alignTop="@id/spinner"
android:layout_alignBottom="@id/spinner" />
Java:
View spinnerOverlay = findViewById(R.id.spinner_overlay);
spinnerOverlay.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
spinner.performClick();
}
});
如果您只需要恢复标准的点击行为,这是的更简单、更紧凑的版本:
spinner.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
v.performClick();
}
return true;
}
});
进一步思考
这种行为也让我很恼火。查了一下,好像有个名字:拖动打开。您可以在 AppCompatSpinner#onTouchEvent()
方法的源代码中查看它是如何定义的。
我看到这种强制行为的一些问题(以及一些人想要禁用它的原因):
- 它允许用户在微调器中 select 禁用值。通过长按、拖动和释放,您可以获得 select 值,这甚至无法通过正常交互 select 实现(点击打开 + 点击-select)
- 作为#1 的结果,它也很容易使 Espresso 测试失败。事实上,在 Espresso 中,点击的持续时间相当不稳定,一次点击可以很快变成长按和 select 交互。
- 最后,这里最大的问题是没有方法/XML 属性来禁用 "drag to open" 行为...
让我们修复它!
我在 AOSP 问题跟踪器上开了一张与此相关的工单:#228953。欢迎关注,如有遗漏欢迎评论。
有点晚了,但可能对某人仍然有用:
在 onLongClick
中设置 returns true
的 OnLongClickListener
可防止(无意中)打开 Spinner
,因为它就像回调一样工作如 documentation.
中所述消耗长按
spinner.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return true;
}
});
唯一的缺点是,微调器在长按时仍然会有 onTouch 动画。
默认的微调器行为是,当它处于 "closed" 时,长按它会 "open" 它并显示下拉视图。我发现这种行为对用户来说可能是非常有问题的。例如,如果他们试图在屏幕上滚动某些东西,并且碰巧 "grab" 一个有微调器的地方,那么它不会滚动,而是在一秒钟左右后打开下拉视图,然后用户基本上只剩下手指放在其中一个下拉选项上(他们现在可能会不小心按下)。
所以,我想禁用长按行为,并在仅单击而不是长按时使用微调器 "open"。这可能吗?
你在 xml 中尝试过 android:longClickable="false" 了吗?
或者尝试这样的事情:
spinner.setOnTouchListener(new View.OnTouchListener() {
private static final int MAX_CLICK_DISTANCE = 15;
private static final int MAX_CLICK_DURATION = 1000;
private long pressStartTime;
private float pressedX;
private float pressedY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
pressStartTime = System.currentTimeMillis();
pressedX = event.getX();
pressedY = event.getY();
break;
}
case MotionEvent.ACTION_UP: {
long pressDuration = System.currentTimeMillis() - pressStartTime;
if (pressDuration < MAX_CLICK_DURATION && distance(pressedX, pressedY, event.getX(), event.getY()) < MAX_CLICK_DISTANCE) {
//when long clicked.
}
break;
}
}
return false;
}
所以,我想出了一个相对简单的方法来做到这一点,尽管它不是很优雅。基本上,我在 Spinner 的顶部创建了一个透明的覆盖视图,并将其设置为具有仅触发 Spinner 的点击的 OnClickListener。
XML:
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="40dip" />
<View android:id="@+id/spinner_overlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/spinner"
android:layout_alignRight="@id/spinner"
android:layout_alignTop="@id/spinner"
android:layout_alignBottom="@id/spinner" />
Java:
View spinnerOverlay = findViewById(R.id.spinner_overlay);
spinnerOverlay.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
spinner.performClick();
}
});
如果您只需要恢复标准的点击行为,这是
spinner.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
v.performClick();
}
return true;
}
});
进一步思考
这种行为也让我很恼火。查了一下,好像有个名字:拖动打开。您可以在 AppCompatSpinner#onTouchEvent()
方法的源代码中查看它是如何定义的。
我看到这种强制行为的一些问题(以及一些人想要禁用它的原因):
- 它允许用户在微调器中 select 禁用值。通过长按、拖动和释放,您可以获得 select 值,这甚至无法通过正常交互 select 实现(点击打开 + 点击-select)
- 作为#1 的结果,它也很容易使 Espresso 测试失败。事实上,在 Espresso 中,点击的持续时间相当不稳定,一次点击可以很快变成长按和 select 交互。
- 最后,这里最大的问题是没有方法/XML 属性来禁用 "drag to open" 行为...
让我们修复它!
我在 AOSP 问题跟踪器上开了一张与此相关的工单:#228953。欢迎关注,如有遗漏欢迎评论。
有点晚了,但可能对某人仍然有用:
在 onLongClick
中设置 returns true
的 OnLongClickListener
可防止(无意中)打开 Spinner
,因为它就像回调一样工作如 documentation.
spinner.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return true;
}
});
唯一的缺点是,微调器在长按时仍然会有 onTouch 动画。