Android屏幕旋转后,Spinner的onItemSelected方法被多次调用

11

我有一个包含三个下拉框的布局,每个下拉框中的选项都不同。
在我的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);

就像我之前提到的,我的另外两个微调器几乎相同但有不同的选项。

我知道在“首次设置”中每个微调器都会调用一次onItemSelected,因此我有一个标记来防止这个问题。使用这个标记解决方案后,我的微调器可以正常工作。

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

为什么会发生这种情况,我应该如何处理?


你在清单文件中处理了屏幕旋转吗? - Osama_Almaani
不,我不想改变清单。我应该这样做吗? - Favolas
不更改清单,但添加处理程序以告诉应用程序在方向更改时不要重新绘制元素,否则重新绘制元素将触发方法,我认为这就是您的情况。 - Osama_Almaani
谷歌一下。关于方向改变处理程序 - Android清单文件。 - Osama_Almaani
4个回答

36

通常,我发现有许多不同的事件可以触发onItemSelected方法,很难跟踪所有这些事件。相反,我发现使用OnTouchListener更简单,只响应用户发起的更改。

为您的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;
        }
    }

}

将监听器作为OnItemSelectedListener和OnTouchListener添加到下拉列表中:

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

4
非常好的答案,谢谢。这应该是被采纳的答案。 - Kent Hawkings
3
处理安卓最糟糕的小部件的最佳解决方案。 - AlvaroSantisteban
你真是个英雄。我已经在这上面花了好久了。如果我能给你100个赞,我一定会的。 - ShanieMoonlight
太好了!!这是一个优雅的解决方案! - Thunder Dragon
1
这个没有处理位置为0的项目的点击。 - Ashish Kanswal

2

在补充Andres Q.的答案时... 如果您使用的是Java 8,可以通过使用lambda表达式来减少代码行数。这种方法也避免了创建一个单独的类来实现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
    }
});

1
我找到了一个对我有用的解决方案。
我有3个微调器,因此在初始微调器设置时会调用onItemSelected 3次。为了避免onItemSelected在初始设置中触发方法,我创建了一个计数器,使得onItemSelected仅根据计数器值相应地触发该方法。
我意识到,在我的情况下,如果旋转屏幕,则onItemSelected会再次触发3次,加上每个微调器不处于0位置的时间。
例如:
我有3个微调器,用户将其中2个更改为可用选项之一,而不是位置0,因此他最终处于这种情况:
First spinner - > Item 2 selected
Second spinner -> Item 0 selected (no changes)
Third spinner -> Item 1 selected

现在,当我旋转屏幕时,onItemSelected将被触发3次,用于初始下拉列表设置,再加上那些不在位置0的下拉列表会触发2次。
@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 ,如果存在,则从 bundle 中提取 changedSpinners 并相应地更新计数器。

0
我应该只检查片段是否处于恢复状态吗?就像这样:
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时遇到了相同的问题,发现了这个答案带有评论,这启发了我想出了这个解决方案。

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