选项卡选择器(Spinner)在没有用户操作的情况下被错误地调用了onItemSelected方法

29

我有一个spinner,在对话框视图中显示,当对话框启动时,onItemSelected被调用。我不想在这个时候处理它,只想在用户进行选择时处理。所以我要么需要阻止这种情况发生(也许因为没有设置默认值?),要么我需要知道不是用户进行此选择?

我有一个Spinner在对话框中显示,对话框启动时即调用了onItemSelected。我想等到用户进行选择后再进行处理,该怎么办?可能可以防止这种情况发生(例如没有设置默认值),或者确认是用户进行了选择。

一些代码会很有帮助。如果您正在以编程方式设置选择,则将调用onItemSelected方法。 - ninjasense
这是早先在stackoverflow上提过相同问题的重复,而且有更好的答案(特别是这个:https://dev59.com/RnE85IYBdhLWcg3w43wX#17336944)。请参考它。 - Jonik
16个回答

45

在 Bill Mote 的解决方案精神中,另一个选择是将 OnItemSelectedListener 也作为 OnTouchListener。然后可以在 onTouch 方法中设置用户交互标志为 true,在处理了选择更改后在 onItemSelected() 中重置它。我更喜欢这个解决方案,因为用户交互标志仅针对下拉列表进行处理,而不针对活动中可能影响所需行为的其他视图。

在代码中:

为下拉列表创建您的监听器:

public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {

    boolean userSelect = false;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        userSelect = true;
        return false;
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        if (userSelect) { 
            // Your selection handling code here
            userSelect = false;
        }
    }

}

将监听器作为 OnItemSelectedListenerOnTouchListener 添加到下拉列表中:

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);

3
最好的解决方案!在恢复“片段”状态时,它可以使我免受意外选择不需要的项目的困扰。 - Sufian
绝对是最好的解决方案,比添加布尔值来忽略第一个选择要好得多。 - user924941

24

你可以简单地加一个 int 类型的计数器来解决它 :)

 sp.setOnItemSelectedListener(new OnItemSelectedListener() {
    int count=0;
    @Override
    public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        if(count >= 1){
            int item = sp.getSelectedItemPosition();
            Toast.makeText(getBaseContext(),
                 "You have selected the book: " + androidBooks[item],
                  Toast.LENGTH_SHORT).show();
        }
        count++;
    }


    @Override
    public void onNothingSelected(AdapterView<?> arg0) {
    }
});

17
在我的情况下,我只是使用了一个布尔类型的isLoaded变量,因为它比计数器更有意义。 - Mike Richards
简单易行的解决方案! - Pierre
@Mike 是的,除非您有多个使用相同监听器的旋转器。 - LarsH

11

从API Level 3开始,您可以在Activity中使用onUserInteraction()方法并传递一个布尔值来确定用户是否正在与设备交互。

http://developer.android.com/reference/android/app/Activity.html#onUserInteraction()

@Override
public void onUserInteraction() {
    super.onUserInteraction();
    userIsInteracting = true;
}

作为活动的一个字段,我有:

private boolean userIsInteracting;

最后,我的旋转器:

    mSpinnerView.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> arg0, View view, int position, long arg3) {
            spinnerAdapter.setmPreviousSelectedIndex(position);
            if (userIsInteracting) {
                updateGUI();
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> arg0) {

        }
    });

当您在活动中进出时,布尔值将被重置为false。运作得很好。


1
非常感谢。在做了很多事情后,我的代码仍然无法正常工作,因为适配器数据在运行时通知,但是你提供的解决方案userIsInteracting解决了这个问题。 - Amandeep Rohila
你能详细说明一下“因为你在活动中来来去去”,因为你的代码中没有设置userIsInteracting为false。 - tbm
你是正确的。导致问题的交互与初始填充微调器有关。随着项目被添加到列表中,它错误地调用了onItemSelected()。如果列表已经填充,则可能不会再次添加任何内容和/或您已经离开活动并通过onCreate()返回,这将重置该值。 - Bill Mote

6

50
哈哈哈,作为这个问题的答案,接受将关闭的问题链接视为重复问题的答案 :) - Qwertie
"Plot Twist"的纯粹解释。 - Muhammed Refaat
实际上,这个词应该是“递归”。 - saiyancoder
无限循环。 - Srujan Simha

4

我采用了一种不同的方法来解决这个问题,因为我注意到onItemSelected方法有时在方向更改时被调用两次,有时只调用一次。

它似乎取决于当前的下拉列表选择。所以标志或计数器对我没有用。相反,我使用 widgetsRestartTime = System.currentTimeMillis()在onResume()和onCreate中记录系统时间

widgetsRestartTime声明为double和实例变量。

现在,在onItemSelected()方法中,我再次检查当前时间并从中减去widgetsRestartTime,如下所示: if (System.currentTimeMillis() - widgetsRestartTime > 200) { // 用户触发的事件-因此这是一个真正的下拉列表事件,所以要处理它 } else { // 系统生成的事件,例如方向更改、活动启动。所以忽略 }


3
我有同样的问题。但是接受的解决方案对我没有用,因为我的表单上有多个旋转器,其中一些是隐藏的,当旋转器被隐藏时,监听器就不会触发,所以对我来说计数不是一个解决方案。
但是我找到了另一个解决方案:
1. 在初始化旋转器时,在您的活动的onCreate()或片段的onCreateView()中设置默认标记并在设置侦听器之前。 2. 在监听器中,检查标记是否存在,如果存在,则删除它并通过返回来防止执行代码。
这就是全部,它像魅力一样工作!
以下是监听器中的示例:
        if(((String)parent.getTag()).equalsIgnoreCase(TAG_SPINNERVALUE))
        {
            parent.setTag("");
            return;
        }

另外,这个方法也适用于将同一个监听器设置给多个下拉列表!


我不明白为什么要比较字符串的内容。我认为更优雅的做法是设置一个常量“TAG_INITIALIZING =”(或任何其他对象),然后与之进行比较,如下所示:if (parentView.getTag() == TAG_INITIALIZING) {parentView.setTag(null);return;} - rds
1
是的,但你忘了下一次必须检查是否为空,如果在其他任何地方你没有进行此检查,你将会得到NullReferenceException。这样使用更加安全。而且,这样你可以重复使用标签来匹配任何其他字符串值,例如我需要在我的应用程序中使用这样的东西 :) (即我检查标签的多个值)。 - XMight

3

我曾经遇到同样的问题,通过记住之前选择的下拉框值来解决。在每个onItemSelected调用中,我会将新值与之前的值进行比较,如果相同,就不再进一步处理代码。


我也是这么做的,因为我需要记住位置,还有其他一些原因。谢谢伙计给的提示。 - Ajith M A

2

对我来说行得通,

私有布尔值isSpinnerInitial = true;

@Override
public void onItemSelected(AdapterView<?> parent, View view,
        int position, long id) {

    if(isSpinnerInitial)
    {
        isSpinnerInitial = false;
    }
    else  {
        // do your work...
    }

}

2
如果您也需要购买一个未选择的下拉框,可以选择以下内容:如何创建一个带有初始文本的Android下拉框。请注意保留HTML标签。
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
    if(pos != 0) {
        pos--;

        // your code here
    }
}

由于选项0永远不可能被选择。干杯 :-)


1
这绝对是最好的解决方案 - 也适用于多个旋转器 - 谢谢 :-) - Darwind
1
我认为你假设在位置0的项目是提示。即使如此,如果用户改变主意并决定第二次选择呢?那么他的选择将被向下推一个位置?这不是期望的行为。虽然我喜欢这个概念,但我用布尔字段代替实现了它。 - ygesher
我相信NothingSelectedSpinnerAdapter从不更改项目数量,但对于索引0使用isEnabled(int position) false。 - ThomasRS

2

是的,您所看到的是正确的,并且这是默认行为,您无法阻止它。在初始加载时,OnItemSelected回调会被调用。在此之后,只有当用户更改选择或者您从代码中更改选择时才会触发它。您可以使用一个指示器来告诉您事件是否是初始加载的结果,如果您不想处理第一个事件,则可以忽略它。


我想指示器应该是onclick内部的某种标志。 - Androider
@Androider,你能告诉我你是怎么解决这个问题的吗?我在onCreate()中尝试使用了一个标志位。它可以在应用程序第一次创建时工作,但在屏幕方向改变后就会出现问题。谢谢。 - Giorgio Vespucci
new View.OnClickListener() { Boolean autoSelected=true; // 在此处声明 public void onClick(View v) { - Androider
someSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView<?> parent, View view, int index, long arg3) { - Androider
如果(autoSelected == false){ autoSelected = true; viewDialog.cancel(); } else { autoSelected = false; } - Androider
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接