Spinner onItemSelected 被错误调用(无需用户操作)

发布于 2024-10-19 10:52:49 字数 134 浏览 9 评论 0原文

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

I have a spinner which I am showing in a dialog view, and the moment the dialog starts onItemSelected is called. I don't really want to process this but only when user makes the selection. So I either need to prevent this (maybe because no default value is set?), or I need to know it is not the user that is making this selection?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

眼泪也成诗 2024-10-26 10:52:49

符合 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);

Another option in the spirit of Bill Mote's solution is to make the OnItemSelectedListener also an OnTouchListener. The user interaction flag can then be set to true in the onTouch method and reset in onItemSelected() once the selection change has been handled. I prefer this solution because the user interaction flag is handled exclusively for the spinner, and not for other views in the activity that may affect the desired behavior.

In code:

Create your listener for the spinner:

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;
        }
    }

}

Add the listener to the spinner as both an OnItemSelectedListener and an OnTouchListener:

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
夜灵血窟げ 2024-10-26 10:52:49

你可以简单地添加一个 int count 来解决它:)

 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) {
    }
});

You can simply add an int count to solve it :)

 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) {
    }
});
滿滿的愛 2024-10-26 10:52:49

从 API 级别 3 开始,您可以在 Activity 上使用带有布尔值的 onUserInteraction() 来确定用户是否正在与设备交互。

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

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

作为 Activity 上的一个字段,我有:

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) {

        }
    });

当您进入并浏览 Activity 时,布尔值将重置为 false。就像魅力一样。

Beginning with API level 3 you can use onUserInteraction() on an Activity with a boolean to determine if the user is interacting with the device.

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

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

As a field on the Activity I have:

private boolean userIsInteracting;

Finally, my spinner:

    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) {

        }
    });

As you come and go through the activity the boolean is reset to false. Works like a charm.

只想待在家 2024-10-26 10:52:49

Androider,我找到了这个问题的解决方案并将其发布在这里(带有代码示例):

Spinner onItemSelected() 在不应该执行时执行

Androider, I have found a solution for this problem and posted it here (with code sample):

Spinner onItemSelected() executes when it is not suppose to

蓝戈者 2024-10-26 10:52:49

我用不同的方式解决了这个问题,因为我注意到有时 onItemSelected 方法在方向改变时被调用两次,有时甚至一次。

这似乎取决于当前微调器的选择。所以旗帜或计数器对我不起作用。相反,我使用 onResume() 和 onCreate 记录系统时间
widgetsRestartTime = System.currentTimeMillis()

widgetsRestartTime 被声明为 double 和实例变量。

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

I have solved this problem a different way as I noticed sometimes the onItemSelected method was called twice on orientation change or sometimes once.

It seemed to depend on the current spinner selection. So a flag or counter didn't work for me. Instead I record the system time in BOTH onResume() and onCreate using
widgetsRestartTime = System.currentTimeMillis()

widgetsRestartTime is declared as a double and as an intance variable.

Now in the onItemSelected() method I check the current time again and subtract widgetsRestartTime from it as follows:
if (System.currentTimeMillis() - widgetsRestartTime > 200) {
// user triggered event - so this is a real spinner event so process it
} else {
// system generated event e.g. orientation change, activity startup. so ignore
}

债姬 2024-10-26 10:52:49

我遇到了同样的问题,我通过记住之前选择的微调值来解决它。在每次 onItemSelected 调用中,我都会将新值与前一个值进行比较,如果相同,我不会进一步处理代码。

I had the same problem, and I solved it by remembering the previous selected spinner value. On each onItemSelected call I compare the new value with the previous, and if it's the same I don't process the code further.

沙与沫 2024-10-26 10:52:49

我也有同样的问题。但接受的解决方案对我来说不起作用,因为我的表单上有多个旋转器,其中一些是隐藏的,当旋转器隐藏时,监听器不会触发,所以计数对我来说不是一个解决方案。

不过,我找到了另一个解决方案

  1. 在初始化微调器时、在 Activity 的 onCreate() 或 Fragment 的 onCreateView 中以及设置侦听器之前,在微调器上设置默认标签
  2. < strong>在监听器中,检查标签,如果存在,则删除它并且不执行代码,返回。

就是这样,它就像一个魅力!

侦听器中的示例:

        if(((String)parent.getTag()).equalsIgnoreCase(TAG_SPINNERVALUE))
        {
            parent.setTag("");
            return;
        }

PS 这适用于设置为多个微调器的同一个侦听器!

I had the same problem. But the accepted solution didn't work for me because I have multiple spinners on my form, and some of them are hidden, and when the spinners are hidden, the listener does not fire, so counting is not a solution for me.

I have found another solution though:

  1. Set a default TAG on spinner while initializing it, in onCreate() of your activity or onCreateView of your Fragment, and before setting the listener
  2. In the listener, check the tag, if it is present, then delete it and do not execute the code, by returning.

That's it, it works like a charm!

Example in the listener:

        if(((String)parent.getTag()).equalsIgnoreCase(TAG_SPINNERVALUE))
        {
            parent.setTag("");
            return;
        }

P.S. And this works for the same listener set to multiple spinners!

熊抱啵儿 2024-10-26 10:52:49

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

Yes what you are seeing is correct and is the default behaviour,You cannot prevent this. The OnItemSelected callback is called on initial load. After that initial load it is only triggered whenever user changes selection or if you change the selection from within your code. You can have a indicator which can tell you whether the event was a result of initial load and ignore the first event if you do not want to process it.

那片花海 2024-10-26 10:52:49

如果您还购买 最初未选择的微调器,您可以选择

@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
    if(pos != 0) {
        pos--;

        // your code here
    }
}

因为项目 0 永远不可能被选择。干杯:-)

If you also are shopping for an initially unselected Spinner, you can go with

@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
    if(pos != 0) {
        pos--;

        // your code here
    }
}

Since item 0 is never possible to select. Cheers :-)

荒芜了季节 2024-10-26 10:52:49

它对我有用,

private boolean isSpinnerInitial = true;

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

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

}

It worked for me,

private boolean isSpinnerInitial = true;

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

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

}
凑诗 2024-10-26 10:52:49

我也在互联网上寻找好的解决方案,但没有找到满足我需求的解决方案。
因此,我在 Spinner 类上编写了此扩展,以便您可以设置一个简单的 OnItemClickListener,它具有与 ListView 相同的行为。

仅当“选择”某个项目时,才会调用 onItemClickListener。

玩得开心!

public class MySpinner extends Spinner
{
    private OnItemClickListener onItemClickListener;


    public MySpinner(Context context)
    {
        super(context);
    }

    public MySpinner(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public MySpinner(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    @Override
    public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener inOnItemClickListener)
    {
        this.onItemClickListener = inOnItemClickListener;
    }

    @Override
    public void onClick(DialogInterface dialog, int which)
    {
        super.onClick(dialog, which);

        if (this.onItemClickListener != null)
        {
            this.onItemClickListener.onItemClick(this, this.getSelectedView(), which, this.getSelectedItemId());
        }
    }
}

I also looked for a good solution on the internet but didn't find any that satisfied my needs.
So I've written this extension on the Spinner class so you can set a simple OnItemClickListener, which has the same behaviour as a ListView.

Only when an item gets 'selected', the onItemClickListener is called.

Have fun with it!

public class MySpinner extends Spinner
{
    private OnItemClickListener onItemClickListener;


    public MySpinner(Context context)
    {
        super(context);
    }

    public MySpinner(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public MySpinner(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    @Override
    public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener inOnItemClickListener)
    {
        this.onItemClickListener = inOnItemClickListener;
    }

    @Override
    public void onClick(DialogInterface dialog, int which)
    {
        super.onClick(dialog, which);

        if (this.onItemClickListener != null)
        {
            this.onItemClickListener.onItemClick(this, this.getSelectedView(), which, this.getSelectedItemId());
        }
    }
}
无法言说的痛 2024-10-26 10:52:49

你应该这样做。顺序是关键。

spinner.setAdapter(adapter);
spinner.setSelection(position);
spinner.setOnItemSelectedListener(listener);

You should do like this.The sequence is the key.

spinner.setAdapter(adapter);
spinner.setSelection(position);
spinner.setOnItemSelectedListener(listener);
落花浅忆 2024-10-26 10:52:49

您可以尝试使用两个参数设置微调器,如下所示:

spinner.setSelection(count, false);

因此,将其放在设置 OnItemSelectedListener 之前:

spinner.setSelection(0,false);

您可以从开发人员页面查看更多信息:

https://developer.android.com/reference/android/widget/Spinner.html

You can try to set the spinner using two arguments, like this:

spinner.setSelection(count, false);

So, put this before the set OnItemSelectedListener :

spinner.setSelection(0,false);

You can check more from the developers page:

https://developer.android.com/reference/android/widget/Spinner.html

最丧也最甜 2024-10-26 10:52:49

如果您不介意使用提示位置,则每次您想要在旋转器上做工作人员时都可以执行类似的操作。首先将选择设置为提示:

spinner.setselected(0);
spinner.add("something");
...

然后在选择发生时执行类似的操作:

spinner.ItemSelected += (object sender, AdapterView.ItemSelectedEventArgs e) => 
            {
                if (spinner.SelectedItemPosition != 0)
                {
                   //do staff
                } 
            }

If you dont mind using a position for promt you can do something like this every time you want to do staff on the spinner. First set selection to the promt:

spinner.setselected(0);
spinner.add("something");
...

And then do something like that when selection happens:

spinner.ItemSelected += (object sender, AdapterView.ItemSelectedEventArgs e) => 
            {
                if (spinner.SelectedItemPosition != 0)
                {
                   //do staff
                } 
            }
-残月青衣踏尘吟 2024-10-26 10:52:49

问题是我们在微调器完成渲染之前设置侦听器。一旦渲染,它就会自动选择数据集的第一个选项(这很好),但这会导致我们的侦听器触发。

解决方案是等待特定的微调器被布局,然后才设置监听器:

binding.spinnerLanguage.post( new Runnable() {
    @Override
    public void run() {
        binding.spinnerLanguage.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                GeneralUtils.log(TAG, "finally, not triggering on initial render");
            }

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

            }
        });
    }
});

The issue is that we are setting listener before the spinner has done rendering. And once it renders, it does select it's datasets 1st option automatically (which is fine), but that causes our listener to trigger.

The solution is to wait for the specific spinner to be laid out, and only then set the listener:

binding.spinnerLanguage.post( new Runnable() {
    @Override
    public void run() {
        binding.spinnerLanguage.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                GeneralUtils.log(TAG, "finally, not triggering on initial render");
            }

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

            }
        });
    }
});
余生共白头 2024-10-26 10:52:49

这是 Andres Q 答案的 Kotlin 版本,并进行了一些改进。

匿名接口实现:

val listener = object : AdapterView.OnItemSelectedListener, OnTouchListener
{
    var userSelect: Boolean = false

    override fun onItemSelected(
        parent: AdapterView<*>?,
        view: View?,
        position: Int,
        id: Long
    )
    {
        if (userSelect)
        {
            // Your selection handling code here
            userSelect = false
        }
    }

    override fun onNothingSelected(parent: AdapterView<*>?)
    {
        //nothing to do
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouch(view: View?, event: MotionEvent?): Boolean
    {
        if (event?.action == MotionEvent.ACTION_DOWN)
        {
            userSelect = true
        }
        return false
    }
}

然后,将微调器的侦听器设置为 OnItemSelectedListener 和 OnTouchListener:

aSpinner.setOnTouchListener(listener)
aSpinner.onItemSelectedListener = listener

This is the Kotlin version of Andres Q's answer with some improvements.

Anonymous interface implementation:

val listener = object : AdapterView.OnItemSelectedListener, OnTouchListener
{
    var userSelect: Boolean = false

    override fun onItemSelected(
        parent: AdapterView<*>?,
        view: View?,
        position: Int,
        id: Long
    )
    {
        if (userSelect)
        {
            // Your selection handling code here
            userSelect = false
        }
    }

    override fun onNothingSelected(parent: AdapterView<*>?)
    {
        //nothing to do
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouch(view: View?, event: MotionEvent?): Boolean
    {
        if (event?.action == MotionEvent.ACTION_DOWN)
        {
            userSelect = true
        }
        return false
    }
}

Then, Set the listener to the spinner as both an OnItemSelectedListener and an OnTouchListener:

aSpinner.setOnTouchListener(listener)
aSpinner.onItemSelectedListener = listener
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文