Android 不需要的 onItemSelected 调用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21747917/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 05:00:53  来源:igfitidea点击:

Undesired onItemSelected calls

androidandroid-spinner

提问by Vedavyas Bhat

I have 36 spinners that I have initialized with some values. I have used onItemSelectedListener with them. As usual, the user can interact with these spinners, firing the onItemSeected function.

我有 36 个微调器,我已经用一些值初始化了它们。我和他们一起使用了 onItemSelectedListener 。像往常一样,用户可以与这些微调器交互,触发 onItemSected 函数。

One problem is that the call is made during init, but I found solutions to it here and avoided that using a global variable "count" and checking if count > 36 before executing code inside onItemSelected.

一个问题是调用是在 init 期间进行的,但我在这里找到了解决方案,并避免使用全局变量“count”并在执行 onItemSelected 中的代码之前检查 count > 36。

My problem is this: The user has the option to click on a button called "Previous", upon which I have to reset SOME of the spinner values.

我的问题是:用户可以选择单击一个名为“上一个”的按钮,我必须在该按钮上重置一些微调器值。

I tried changing the value of count to 0 before resetting the spinners, and then changing it back to 37 after resetting, but I have come to understand that the onItemSelected is called only after every other function is done executing, so it is called AFTER count is changed back to 37 even though the spinner values are set as soon as they are selected by user.

我尝试在重置微调器之前将 count 的值更改为 0,然后在重置后将其更改回 37,但我已经理解 onItemSelected 仅在所有其他函数执行完成后才被调用,因此它被称为 AFTER count即使微调值在用户选择后立即设置,也会更改回 37。

I need to repeatedly refresh some spinners WITHOUT firing off the onItemSelected function. Can anyone please help me find a solution? Thanks.

我需要在不触发 onItemSelected 函数的情况下反复刷新一些微调器。任何人都可以帮我找到解决方案吗?谢谢。

回答by Vedavyas Bhat

I found a simple and, I think, elegant solution. Using tags. I first created a new XML file called 'tags' and put in the following code:

我找到了一个简单而优雅的解决方案。使用标签。我首先创建了一个名为“tags”的新 XML 文件并输入以下代码:

<resources xmlns:android="http://schemas.android.com/apk/res/android">
  <item name="pos" type="id" />
</resources>

Whenever I myself use spin.setSelection(pos), I also do spin.setTag(R.id.pos, pos), so I am setting the current position as a tag.

每当我自己使用 时spin.setSelection(pos),我也会使用,spin.setTag(R.id.pos, pos)所以我将当前位置设置为标签。

Then, in onItemSelected, I am executing code only if(spin.getTag(R.id.pos) != position), where position is the position variable supplied by the function. In this way, my code is executed only when the user is making a selection. Since the user has made a selection, the tag has not been updated, so after the processing is done, I update the tag as spin.setTag(R.id.pos, position).

然后,在 onItemSelected 中,我只执行代码if(spin.getTag(R.id.pos) != position),其中 position 是函数提供的位置变量。这样,我的代码只有在用户进行选择时才会执行。由于用户进行了选择,标签没有更新,所以处理完成后,我将标签更新为spin.setTag(R.id.pos, position)

NOTE: It is important to use the same adapter throughout, or the "position" variable might point to different elements.

注意:始终使用相同的适配器很重要,否则“位置”变量可能指向不同的元素。

EDIT:As kaciula pointed out, if you're not using multiple tags, you can use the simpler version, that is spin.setTag(pos)and spin.getTag()WITHOUT the need for an XML file.

编辑:作为kaciula指出的那样,如果你不使用多个标签,则可以使用简单的版本,那就是spin.setTag(pos)spin.getTag()不需要的XML文件。

回答by Ivo Stoyanov

When Spinner.setSelection(position) is used, it always activates setOnItemSelectedListener()

当使用 Spinner.setSelection(position) 时,它总是激活 setOnItemSelectedListener()

To avoid firing the code twice I use this solution:

为了避免两次触发代码,我使用了这个解决方案:

     private Boolean mIsSpinnerFirstCall = true;

    ...
    Spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            //If a new value is selected (avoid activating on setSelection())
            if(!mIsSpinnerFirstCall) {
                // Your code goes gere
            }
            mIsSpinnerFirstCall = false;
        }

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

回答by user8118328

I don't know if this solution is as foolproof as the chosen one here, but it works well for me and seems even simpler:

我不知道这个解决方案是否像这里选择的那样万无一失,但它对我来说效果很好,而且看起来更简单:

boolean executeOnItemSelected = false;
spinner.setSelection(pos)

And then in the OnItemSelectedListener

然后在 OnItemSelectedListener 中

public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    if(executeOnItemSelected){
        //Perform desired action
    } else {
        executeOnItemSelected = true;
    }
}

回答by M. Oosterlee

The way I solved this is by saving the OnItemSelectedListener first. Then set the OnItemSelectedListener of the Spinner to the null value. After setting the item in the Spinner by code, restore the OnItemSelectedListener again. This worked for me.

我解决这个问题的方法是先保存 OnItemSelectedListener 。然后将 Spinner 的 OnItemSelectedListener 设置为空值。通过代码设置Spinner中的item后,再次恢复OnItemSelectedListener。这对我有用。

See code below:

见下面的代码:

        // disable the onItemClickListener before changing the selection by code. Set it back again afterwards
        AdapterView.OnItemSelectedListener onItemSelectedListener = historyPeriodSpinner.getOnItemSelectedListener();
        historyPeriodSpinner.setOnItemSelectedListener(null);
        historyPeriodSpinner.setSelection(0);
        historyPeriodSpinner.setOnItemSelectedListener(onItemSelectedListener);

回答by Clyde

Here's my solution to this problem. I extend AppCompatSpinnerand add a method pgmSetSelection(int pos)that allows programmatic selection setting without triggering a selection callback. I've coded this with RxJava so that the selection events are delivered via an Observable.

这是我对这个问题的解决方案。我扩展AppCompatSpinner并添加了一种方法pgmSetSelection(int pos),该方法允许在不触发选择回调的情况下进行编程选择设置。我已经用 RxJava 对此进行了编码,以便选择事件通过Observable.

package com.controlj.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;

import io.reactivex.Observable;

/**
 * Created by clyde on 22/11/17.
 */

public class FilteredSpinner extends android.support.v7.widget.AppCompatSpinner {
    private int lastSelection = INVALID_POSITION;


    public void pgmSetSelection(int i) {
        lastSelection = i;
        setSelection(i);
    }

    /**
     * Observe item selections within this spinner. Events will not be delivered if they were triggered
     * by a call to setSelection(). Selection of nothing will return an event equal to INVALID_POSITION
     *
     * @return an Observable delivering selection events
     */
    public Observable<Integer> observeSelections() {
        return Observable.create(emitter -> {
            setOnItemSelectedListener(new OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if(i != lastSelection) {
                        lastSelection = i;
                        emitter.onNext(i);
                    }
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {
                    onItemSelected(adapterView, null, INVALID_POSITION, 0);
                }
            });
        });
    }

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

    public FilteredSpinner(Context context, int mode) {
        super(context, mode);
    }

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

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
        super(context, attrs, defStyleAttr, mode);
    }
}

An example of its usage, called in onCreateView()in a Fragmentfor example:

其用法示例,例如onCreateView()在 a 中调用Fragment

    mySpinner = view.findViewById(R.id.history);
    mySpinner.observeSelections()
        .subscribe(this::setSelection);

where setSelection()is a method in the enclosing view that looks like this, and which is called both from user selection events via the Observableand also elsewhere programmatically, so the logic for handling selections is common to both selection methods.

wheresetSelection()是封闭视图中的一个方法,看起来像这样,并且通过Observable和其他地方以编程方式从用户选择事件中调用,因此处理选择的逻辑对于两种选择方法都是通用的。

private void setSelection(int position) {
    if(adapter.isEmpty())
        position = INVALID_POSITION;
    else if(position >= adapter.getCount())
        position = adapter.getCount() - 1;
    MyData result = null;
    mySpinner.pgmSetSelection(position);
    if(position != INVALID_POSITION) {
        result = adapter.getItem(position);
    }
    display(result);  // show the selected item somewhere
}