Android 屏幕旋转后多次调用微调器 onItemSelected

Android spinner onItemSelected called multiple times after screen rotation

我有一个包含三个微调器的布局。它们的不同之处在于下拉列表中显示的选项。
在我的 onCreateView 中,我有一种设置微调器的方法。在那个方法里面我有这样的东西:

  mySpinner = (Spinner) view.findViewById(R.id.my_spinner);
  ArrayAdapter<String> mySpinner =
            new ArrayAdapter<String>(getActivity(), R.layout.background,
                    new ArrayList<String>(Arrays.asList(getResources().getStringArray(R.array.spinner_one_data))));
  mySpinner.setDropDownViewResource(R.layout.spinner_text);
  mySpinner.setAdapter(mySpinner);
  mySpinner.setOnItemSelectedListener(this);

正如我所说,我的另外两个微调器几乎相同,但选项不同。

我知道 "first setup" 中的每个微调器都会调用一次 onItemSelected,所以我有一个标志来防止这个问题。使用此标志解决方案,我的微调器按预期工作。

问题是当我 select 在每个微调器中选择一个选项然后旋转屏幕时。现在,onItemSelected 被调用了 6 次,而不是我预期的 3 次(我设置了一个标志来管理这种调用 3 次的情况)。

为什么会这样,我应该处理这个吗?

我找到了适合我的解决方案。

我有 3 个微调器,因此 onItemSelected 在初始微调器设置时被调用 3 次。为了避免 onItemSelected 在初始设置中触发方法,我创建了一个计数器,因此 onItemSelected 仅根据计数器值触发方法。

我意识到在我的情况下,如果旋转屏幕,onItemSelected 会再次触发 3 次,加上每个不在 [= 位置的微调器的时间19=].

一个例子:

我有 3 个旋转器,用户将其中 2 个旋转器更改为位置 0 以外的可用选项之一,所以他最终遇到了这样的情况:

First spinner - > Item 2 selected
Second spinner -> Item 0 selected (no changes)
Third spinner -> Item 1 selected

现在,当我旋转屏幕时,onItemSelected 将被触发 3 次初始微调器设置加上 2 次不在位置 0.

@Override
public void onSaveInstanceState(Bundle outState) {
    int changedSpinners = 0;
    if (spinner1.getSelectedItemPosition() != 0) {
        changedSpinners += 1;
    }
    if (spinner2.getSelectedItemPosition() != 0) {
        changedSpinners += 1;
    }
    if (spinner3.getSelectedItemPosition() != 0) {
        changedSpinners += 1;
    }
    outState.putInt("changedSpinners", changedSpinners);
}

我已将状态保存在 onSaveInstanceState 中,然后在 onCreateView 中检查是否 savedInstanceState != null 如果是,则从包中提取 changedSpinners 并更新我的反对采取相应行动。

总的来说,我发现有许多不同的事件可以触发 onItemSelected 方法,并且很难跟踪所有这些事件。相反,我发现使用 OnTouchListener 仅响应用户启动的更改更简单。

为微调器创建监听器:

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

}

将侦听器作为 OnItemSelectedListener 和 OnTouchListener 添加到微调器:

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

要扩展 Andres Q. 的答案...如果您使用的是 Java8,则可以通过使用 lambda 表达式以更少的代码行来完成此操作。此方法还放弃了创建单独的 class 以实现 onTouchListener

的需要
Boolean spinnerTouched; //declare this as an instance or class variable

spinnerTouched = false;
yourSpinner.setOnTouchListener((v,me) -> {spinnerTouched = true; v.performClick(); return false;});

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

        if(spinnerTouched){
            //do your stuff here
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        //nothing selected
    }
});

如果只检查片段是否处于恢复状态呢?像这样思考:

private AdapterView.OnItemSelectedListener mFilterListener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if (isResumed()) {
              //your code
            }
        }

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

        }
    };

@Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        //set mFilterListener
}

它消除了旋转问题,也消除了第一个设置问题。没有标志等。我在使用 TextWatchers 时遇到了同样的问题,发现这个 answer 带有评论,这启发了我这个解决方案。