【问题标题】:Android Spinner's onItemSelected executed twice when going back to FragmentAndroid Spinner 的 onItemSelected 在返回 Fragment 时执行了两次
【发布时间】:2015-05-15 06:49:55
【问题描述】:

我为我的 Fragment 创建了一个微调器,它使用从 HTTP 标注检索到的数据填充它。首次创建 Fragment 时,我使用其选择选项填充微调器,设置其 setOnItemSelectedListener 并在 onCreateView() 中设置其初始选择。

    stateSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

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


            if (spinnerPosition != position)
            {
                spinnerPosition = position;
                TextView stateSelected = (TextView) view;
                String stateSelectedStr = stateSelected.getText().toString();


                LinearLayout ballotsDisplay = (LinearLayout) getActivity().findViewById(R.id.ballotsDisplay);
                ballotsDisplay.removeAllViews();

                Map<String, String> calloutParams = new HashMap<String, String>();
                calloutParams.put("state", stateSelectedStr);

                // Create and execute AsyncTask to retrieve ballots
                new RetrieveBallots().execute(calloutParams);
            }
        }

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

    // Set default selection for spinner
    int defaultState = adapter.getPosition(userState);

    if (defaultState == -1)
    {
        defaultState = 0;
    }

    stateSpinner.setSelection(defaultState);

创建 Fragment 后,一切正常,微调器位置设置为其默认值,并且微调器项被选中一次,如下面的日志所示:

5009-5009/com.project.test D/TEST﹕ onCreateView called
5009-5009/com.project.test D/TEST﹕ stateSpinner.setSelection
5009-5009/com.project.test D/TEST﹕ onActivityCreated called
5009-5009/com.project.test D/TEST﹕ onResume called
5009-5009/com.project.test D/TEST﹕ spinner item selected

当我从 Fragment 导航到另一个 Fragment 时会出现问题(我将 Fragment 存储到 backstack 上)。当我点击返回回到我原来的片段时,微调器似乎选择了它的项目两次:

5009-5009/com.project.test D/TEST﹕ onCreateView called
5009-5009/com.project.test D/TEST﹕ stateSpinner.setSelection
5009-5009/com.project.test D/TEST﹕ onActivityCreated called
5009-5009/com.project.test D/TEST﹕ onResume called
5009-5009/com.project.test D/TEST﹕ spinner item selected
5009-5009/com.project.test D/TEST﹕ spinner item selected

所以我有两个问题:

1) 为什么微调器在从“后退”按钮返回时会记录 2 个项目选择事件。

2) 是否有防止发生 2 个项目选择的修复程序?现在该片段正在填充重复数据,因为它检索了两次数据。

** 编辑 ** 更改为 stateSpinner.setSelection(defaultState, false) 后,我会在 ballotsDisplay.removeAllViews(); 处得到一个空指针异常,似乎 ballotsDisplay 由于某种原因被设置为 null

堆栈跟踪:

05-15 07:25:48.303 6153-6153/com.poliseewriters.polisee E/AndroidRuntime: 致命异常: main java.lang.NullPointerException 在 com.polisee.ballotmeasures.BallotMeasuresFragment$1.onItemSelected(BallotMeasuresFragment.java:287) 在 android.widget.AdapterView.fireOnSelected(AdapterView.java:882) 在 android.widget.AdapterView.selectionChanged(AdapterView.java:865) 在 android.widget.AdapterView.checkSelectionChanged(AdapterView.java:1017) 在 android.widget.Spinner.layout(Spinner.java:363) 在 android.widget.AbsSpinner.setSelectionInt(AbsSpinner.java:292) 在 android.widget.AbsSpinner.setSelection(AbsSpinner.java:269) 在 com.polisee.ballotmeasures.BallotMeasuresFragment.setStateSpinner(BallotMeasuresFragment.java:314) 在 com.polisee.ballotmeasures.BallotMeasuresFragment.onCreateView(BallotMeasuresFragment.java:201) 在 android.support.v4.app.Fragment.performCreateView(Fragment.java:1786) 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:953) 在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1136) 在 android.support.v4.app.BackStackRecord.run(BackStackRecord.java:739) 在 android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1499) 在 android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:456) 在 android.os.Handler.handleCallback(Handler.java:605) 在 android.os.Handler.dispatchMessage(Handler.java:92) 在 android.os.Looper.loop(Looper.java:137) 在 android.app.ActivityThread.main(ActivityThread.java:4441) 在 java.lang.reflect.Method.invokeNative(Native Method) 在 java.lang.reflect.Method.invoke(Method.java:511) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 在 dalvik.system.NativeStart.main(Native Method)

** 编辑:更新代码以防止 onItemSelected 执行两次,需要为 ballotsDisplay 添加空检查 **

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    if (savedInstanceState != null) {
        Log.d("TEST", "bundle = " + savedInstanceState.toString());
    }

    Log.d("TEST", "onCreateView called");

    View view = (View) inflater.inflate(R.layout.fragment_ballot_measures, container, false);

    setStateSpinner(view);

    return view;
}


private void setStateSpinner(View view) {

    try {
        states = Utilities.getAllStateNames();
    }
    catch (Exception e) {
        Log.e("Error", "Error retrieving names: " + e.getMessage());
    }

    Spinner stateSpinner = (Spinner) view.findViewById(R.id.stateSpinner);

    final ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), R.layout.ballotmeasures_state_spinner, states);
    adapter.setDropDownViewResource(R.layout.ballotmeasures_state_spinner_dropdown);

    stateSpinner.setAdapter(adapter);

    stateSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

        // Callback method to invoke when a state has been selected
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
        {
            Log.d("TEST", "spinner item selected");

                TextView stateSelected = (TextView) view;
                String stateSelectedStr = stateSelected.getText().toString();

                // Remove all currently displayed views in the layout
                LinearLayout ballotsDisplay = (LinearLayout) getActivity().findViewById(R.id.ballotsDisplay);
                if (ballotsDisplay != null)
                {
                    ballotsDisplay.removeAllViews();
                }

                Map<String, String> calloutParams = new HashMap<String, String>();
                calloutParams.put("state", stateSelectedStr);

                // AsyncTask to execute data retrieval 
                new RetrieveBallots().execute(calloutParams);
            }

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

    // Set default selection for spinner
    int defaultState = adapter.getPosition(userState);

    if (defaultState == -1)
    {
        defaultState = 0;
    }

    Log.d("TEST", "stateSpinner.setSelection");
    stateSpinner.setSelection(defaultState, false);
}

【问题讨论】:

    标签: android android-fragments android-spinner


    【解决方案1】:

    使用stateSpinner.setSelection(defaultState, false); 代替stateSpinner.setSelection(defaultState);

    【讨论】:

    • 我照你说的做了,但现在我得到一个空指针异常:LinearLayout ballotsDisplay = (LinearLayout) getActivity().findViewById(R.id.ballotsDisplay); ballotsDisplay.removeAllViews();知道为什么在使用 setSelection(...,false) 后现在会发生这种情况吗?
    • 请检查 ballotsDisplay 是否正确充气。看起来像它的空值。
    • 确实如此。如果我将它切换回 stateSpinner.setSelection(default),我不会收到那个空指针异常。一切正常,除了我回击时的双重项目选择。在设置微调器之前,我会膨胀整个布局(ballotsDisplay 在其中)。只有当我将其更改为 setSelection(default, false) 时才会发生此异常
    • 能否请您发布您的堆栈跟踪。
    • setSelection(int position) 与 setSelection(int position, boolean animate) 的主要区别是什么?浏览了 Spinner 文档,并没有真正解释 animate 参数。
    【解决方案2】:

    问题是onItemSelected() 回调被Android 框架调用了两次(可能是设计使然),第一次view 参数是null,第二次被实例化。

    您无法阻止 2 个项目选择的发生,但您可以检查 view 变量是否为空,如果不是,则执行其余操作。

    【讨论】:

    • 由于某种原因,在 Kotlin 中使用 SpinnerOnItemSelectedListener 时,由于这个问题以及使用 OnItemSelectedListener 作为匿名类(实例化直接在setOnItemSelectedListener 上Android Studio 没有使View 可以为空,这导致运行时异常,因为Kotlin 预计View 不为空,这并不明显。谢谢,你指出了我在正确的方向:-)
    【解决方案3】:

    像这样在 ItemSelectedListener 中添加条件视图!=null:

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                        @Override
                        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    
                            if(view!=null)
                            {//write your code
                            }
                        }
    
                        @Override
                        public void onNothingSelected(AdapterView<?> parent) {
    
                        }
                    });
    

    【讨论】:

      猜你喜欢
      • 2012-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-03
      • 1970-01-01
      • 2013-01-11
      • 2011-06-22
      相关资源
      最近更新 更多