【问题标题】:Spinner : onItemSelected not called when selected item remains the same微调器:当所选项目保持不变时,不调用 onItemSelected
【发布时间】:2012-06-06 22:24:46
【问题描述】:

我的Spinner 有一个OnItemSelectedListener,但是当所选项目与前一个相同时,它不会被调用。显然OnClickListener 不是Spinner 的选项。 每次用户单击某个项目时,我都需要捕捉。有什么想法吗?

也许Spinner 位于ActionBar 内部这一事实会干扰正常行为?

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.tracklist_menu, menu);
    Spinner spinner = (Spinner) menu.findItem(R.id.option_ordering_spinner)
            .getActionView();
    spinner.setAdapter(mSpinnerAdapter);
    spinner.setSelection(PrefsHelper.getOrderingSpinnerPos(prefs));
    spinner.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> parent, View view,
                int position, long id) {
            String str = "selected";
            System.out.println(str);
            if (optionMenuInitialized) {

                switch (position) {
                case 0:
                    // rdm
                    getActivity()
                            .sendBroadcast(
                                    new Intent(
                                            MyIntentAction.DO_RESHUFFLE_PLAYLIST));
                    smp.setCurrentTracklistCursorPos(-1);
                    trackAdapter.notifyDataSetChanged();
                    break;
                case 1:
                    // artist
                    getActivity()
                            .sendBroadcast(
                                    new Intent(
                                            MyIntentAction.DO_ORDER_PLAYLIST_BY_ARTIST));
                    smp.setCurrentTracklistCursorPos(-1);
                    trackAdapter.notifyDataSetChanged();
                    break;
                case 2:
                    // folder
                    getActivity()
                            .sendBroadcast(
                                    new Intent(
                                            MyIntentAction.DO_ORDER_PLAYLIST_BY_FOLDER));
                    smp.setCurrentTracklistCursorPos(-1);
                    trackAdapter.notifyDataSetChanged();
                    break;
                }
                PrefsHelper.setOrderingSpinnerPos(prefEditor, position);
                prefEditor.commit();
            }
            optionMenuInitialized = true;
        }

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

【问题讨论】:

  • 你看这篇文章了吗stackoverflow.com/questions/3928071/…
  • 是的,这并没有帮助......无论如何,我刚刚找到了解决方案,并准备在这里写下来=)
  • 这不相关,但将 Spinner 可见性设置为 VIEW.GONE 也会导致此问题。

标签: android spinner listener android-actionbar actionbarsherlock


【解决方案1】:

从@Vishal Yadav 的回答进一步,如果您想通过调用spinner.setSelection(pos, false); 来设置初始位置而不触发 OnItemSelectedListener,那么自定义微调器应该是:

public class CSpinner extends AppCompatSpinner {

    private int lastPosition = 0;

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

    @Override
    public void setSelection(int position, boolean animate) {
        OnItemSelectedListener listener = getOnItemSelectedListener();
        setOnItemSelectedListener(null);
        super.setSelection(position, animate);
        lastPosition = position;
        setOnItemSelectedListener(listener);
    }

    @Override
    public void setSelection(int position) {
        super.setSelection(position);
        boolean sameSelected = lastPosition == getSelectedItemPosition();
        OnItemSelectedListener onItemSelectedListener = getOnItemSelectedListener();
        if (sameSelected && onItemSelectedListener != null) {
            onItemSelectedListener.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
        }
        lastPosition = position;
    }
}

【讨论】:

    【解决方案2】:

    如果它仍然是实际的,正确的回调调用应该是

    @Override
    public void setSelection(int position) {
        super.setSelection(position);
        if(listener != null)
            listener.onItemSelected(this, getChildAt(position), position, 0);
    }
    

    【讨论】:

    • 这不是正确的回调。首先,您应该使用getItemIdAtPosition(position) 作为最后一个参数,第二个getChildAt(position) 在这种情况下不应该使用。 Spinners 使用View 回收就像ListViews 一样,所以getChildAt 不能用于从适配器中检索位置的第项。
    • 最后一个参数是正确的,以获得正确的 ID。但是要获得视图,可以使用 getChildAt() ,因为如果它存在,我们将获得正确的视图,如果不存在,我们将获得 null。在大多数情况下,当单击项目并因此在微调器中可见时会调用代码,因此 getChildAt() 返回正确的视图。至少我从来没有遇到过返回错误视图的情况。
    • 不,你不正确。例如,微调器有 100 个项目,最后一个被单击。但是只有10个可见。在这种情况下,getChildAt() 将始终返回 99 位置的 null,因为它没有 100 个子 Views。你不能依赖getChildAt() 和任何AdapterViews。您可以致电onItemSelected(this, getSelectedView(), position, getItemIdAtPosition(position));
    • 也许我弄错了,但您是否想说 getChildAt() 仅返回索引 0-9 的视图(对于 10 个可见项目)?我没试过,但听起来很奇怪。我希望它应该从底层适配器返回适配器中所有项目的视图 - 更具体地说,如果它是可见的,它应该返回视图,否则它应该返回 null。当我有一些我现在没有的空闲时间时,我会尝试。当我单击第 100 个项目(位置 99)时,具有 100 个项目(索引 0-99)的微调器将返回 null?我不明白。当我单击最后一项时,它必须是可见的并且 getChildAt(99) 必须返回它。还是不行?
    • 你知道view recycling吗?每个项目都没有Views,只添加了一些必要的Views。这就是getChild(99) 将返回 null 的原因。 10 和 99 只是示例,显然这取决于当前情况。
    【解决方案3】:

    这是一个更好的实现 -

    自定义微调器类 -

    import android.content.Context;
    import android.util.AttributeSet;
    import androidx.appcompat.widget.AppCompatSpinner;
    
    public class CSpinner extends AppCompatSpinner {
    
        private int lastPosition = 0;
    
        public CSpinner(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public void setSelection(int position) {
            super.setSelection(position);
            boolean sameSelected = lastPosition == getSelectedItemPosition();
            OnItemSelectedListener onItemSelectedListener = getOnItemSelectedListener();
            if (sameSelected && onItemSelectedListener != null) {
                onItemSelectedListener.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
            }
            lastPosition = position;
        }
    }
    

    设置监听器 -

    spn.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                Log.d("onItemSelected", String.valueOf(position));
            }
    
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                
            }
        });
    

    【讨论】:

    • 点赞!!!你拯救了我的一天。
    【解决方案4】:

    我找到了一个简单的解决方案

    只需再次调用 setAdapter 来代替第二个微调器的 notifyDataSetChanged

    【讨论】:

      【解决方案5】:

      重写了通用解决方案,但使用:

      1. 牢记androidx
      2. 扩展自AppCompatSpinner
      3. 使用内置的OnItemSelectedListener 侦听器而不是创建自己的侦听器
      4. 添加了初始侦听器调用技巧

      这里:

      import android.content.Context;
      
      import androidx.annotation.Nullable;
      import androidx.appcompat.widget.AppCompatSpinner;
      
      
      public class FixedSpinner extends AppCompatSpinner {
          // add other constructors that you need
          public FixedSpinner(Context context, int mode) {
              super(context, mode);
          }
      
          private void processSelection(int position) {
              boolean sameSelected = position == getSelectedItemPosition();
              final OnItemSelectedListener listener = getOnItemSelectedListener();
              if (sameSelected && listener != null) {
                  // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
                  listener.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
              }
          }
      
          @Override
          public void setSelection(int position) {
              processSelection(position);
              super.setSelection(position);
          }
      
          @Override
          public void setSelection(int position, boolean animate) {
              processSelection(position);
              super.setSelection(position, animate);
          }
      
          @Override
          public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
              // This hack fixes bug, when immediately after initialization, listener is called
              // To make this fix work, first add data, only then set listener
              // Having done this, you may refresh adapter data as many times as you want
              setSelection(0, false);
              super.setOnItemSelectedListener(listener);
          }
      }
      

      【讨论】:

      • 太棒了,谢谢。这解决了我在使用微调器时遇到的问题。
      • 布尔sameSelected = position == getSelectedItemPosition();始终为 TRUE,getSelectedItemPosition() 方法返回与位置相同的值。
      【解决方案6】:

      我遇到了同样的问题,我通过在每次适配器更改项目时设置 onItemSelectedListener 来解决它。

      【讨论】:

        【解决方案7】:

        要让你的微调器改变,尽管最后一个索引的值是选择的,只需使用:

        spinner.setSelection(0); 
        

        在调用其他选择之前

        spinner.setSelection(number); 
        

        这样,微调器将触发两次 OnItemSelected 事件。只需确保第二次执行您需要的任何操作即可。

        【讨论】:

        • 同一个item位置为0怎么办?
        • spinner.setSelection(0); if(number!=0) spinner.setSelection(number);
        【解决方案8】:

        这里有一个更好的实现:

        public class SpinnerPlus extends Spinner {
            AdapterView.OnItemSelectedListener listener;
        
            public SpinnerPlus(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
        
            @Override
            public void setSelection(int position) {
                super.setSelection(position);
                if (listener != null)
                    listener.onItemSelected(this, getSelectedView(), position, 0);
            }
        
            public void setOnItemSelectedEvenIfUnchangedListener(
                    AdapterView.OnItemSelectedListener listener) {
                this.listener = listener;
            }
        }
        

        【讨论】:

          【解决方案9】:

          最简单的解决方案:

          spinner.performItemClick(view,position,id)

          【讨论】:

            【解决方案10】:

            我发现了这项工作而不是提供的工作

            /** Spinner extension that calls onItemSelected even when the selection is the same as its previous value */
            public class NDSpinner extends Spinner {
            
              public NDSpinner(Context context)
              { super(context); }
            
              public NDSpinner(Context context, AttributeSet attrs)
              { super(context, attrs); }
            
              public NDSpinner(Context context, AttributeSet attrs, int defStyle)
              { super(context, attrs, defStyle); }
            
              @Override public void
              setSelection(int position, boolean animate)
              {
                boolean sameSelected = position == getSelectedItemPosition();
                super.setSelection(position, animate);
                if (sameSelected) {
                  // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
                  getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
                }
              }
            
              @Override public void
              setSelection(int position)
              {
                boolean sameSelected = position == getSelectedItemPosition();
                super.setSelection(position);
                if (sameSelected) {
                  // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
                  getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
                }
              }
            }
            

            【讨论】:

            • 如果没有设置OnItemSelectedListener并且选择了相同的项目,你将得到一个NPE。
            • 这个也为我工作,接受的答案没有
            【解决方案11】:

            好的,我终于找到了解决方案,通过创建我自己的扩展 Spinner 的类:

            public class MySpinner extends Spinner {
            OnItemSelectedListener listener;
            
            public MySpinner(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
            
            @Override
            public void setSelection(int position) {
                super.setSelection(position);
                if (listener != null)
                    listener.onItemSelected(null, null, position, 0);
            }
            
            public void setOnItemSelectedEvenIfUnchangedListener(
                    OnItemSelectedListener listener) {
                this.listener = listener;
            }
            }
            

            【讨论】:

            • 你能分享一些关于如何为ActionBar导航列表实现相同功能的想法吗?
            • @benoffi7 你的答案在哪里?
            • @dhams 抱歉,我的回答有误,我将其删除。
            • @elgui 这一行 listener.onItemSelected(null, null, position, 0);给我一个 NPE,你能告诉我如何让 AdapterView & View 在上面的行中通过。谢谢
            • 如何在 listener.onItemSelected(null, null, position, 0); 中获取父级和视图?
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-09-20
            • 2012-11-25
            相关资源
            最近更新 更多