【问题标题】:Spinner onItemSelected called erroneously (without user action)Spinner onItemSelected 被错误调用(没有用户操作)
【发布时间】:2011-07-04 17:37:46
【问题描述】:

我有一个在对话框视图中显示的微调器,并且在对话框开始时调用onItemSelected。我真的不想处理这个,但只有当用户做出选择时。所以我要么需要阻止这种情况(可能是因为没有设置默认值?),要么我需要知道不是用户做出了这个选择?

【问题讨论】:

标签: android spinner android-spinner


【解决方案1】:

问题是我们在微调器完成渲染之前设置了监听器。一旦渲染,它会自动选择它的 datasets 1st 选项(这很好),但这会导致我们的监听器触发。

解决办法是等待具体的spinner布局好,然后才设置监听器:

binding.spinnerLanguage.post( new Runnable() {
    @Override
    public void run() {
        binding.spinnerLanguage.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                GeneralUtils.log(TAG, "finally, not triggering on initial render");
            }

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

            }
        });
    }
});

【讨论】:

    【解决方案2】:

    您可以尝试使用两个参数设置微调器,如下所示:

    spinner.setSelection(count, false);
    

    所以,把它放在设置的 OnItemSelectedListener 之前:

    spinner.setSelection(0,false);
    

    您可以从开发者页面查看更多信息:

    https://developer.android.com/reference/android/widget/Spinner.html

    【讨论】:

      【解决方案3】:

      如果您不介意使用提示职位,您可以在每次想在微调器上做员工时执行类似的操作。首先将选择设置为提示:

      spinner.setselected(0);
      spinner.add("something");
      ...
      

      然后在选择发生时做类似的事情:

      spinner.ItemSelected += (object sender, AdapterView.ItemSelectedEventArgs e) => 
                  {
                      if (spinner.SelectedItemPosition != 0)
                      {
                         //do staff
                      } 
                  }
      

      【讨论】:

        【解决方案4】:

        Bill Mote 解决方案精神的另一个选择是将OnItemSelectedListener 也设为OnTouchListener。然后,可以在 onTouch 方法中将用户交互标志设置为 true,并在处理了选择更改后在 onItemSelected() 中重置。我更喜欢这个解决方案,因为用户交互标志是专门为微调器处理的,而不是为活动中可能影响所需行为的其他视图处理的。

        在代码中:

        为微调器创建监听器:

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

        将侦听器作为OnItemSelectedListenerOnTouchListener 添加到微调器:

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

        【讨论】:

        • 有史以来最好的解决方案!在恢复 Fragment 的状态时将我从那些不需要的项目选择中拯救出来。
        • 绝对是最好的解决方案,比添加布尔值来忽略第一个选择要好得多
        【解决方案5】:

        你应该这样做。顺序是关键。

        spinner.setAdapter(adapter);
        spinner.setSelection(position);
        spinner.setOnItemSelectedListener(listener);
        

        【讨论】:

          【解决方案6】:

          您可以简单地添加一个 int 计数来解决它:)

           sp.setOnItemSelectedListener(new OnItemSelectedListener() {
              int count=0;
              @Override
              public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
                  if(count >= 1){
                      int item = sp.getSelectedItemPosition();
                      Toast.makeText(getBaseContext(),
                           "You have selected the book: " + androidBooks[item],
                            Toast.LENGTH_SHORT).show();
                  }
                  count++;
              }
          
          
              @Override
              public void onNothingSelected(AdapterView<?> arg0) {
              }
          });
          

          【讨论】:

          • 就我而言,我只是使用了布尔值isLoaded,因为这比计数器更有意义
          • 简单易行的解决方案!
          • @Mike 是的,除非您有多个微调器使用同一个侦听器。
          【解决方案7】:

          它对我有用,

          private boolean isSpinnerInitial = true;

          @Override
          public void onItemSelected(AdapterView<?> parent, View view,
                  int position, long id) {
          
              if(isSpinnerInitial)
              {
                  isSpinnerInitial = false;
              }
              else  {
                  // do your work...
              }
          
          }
          

          【讨论】:

            【解决方案8】:

            从 API 级别 3 开始,您可以在带有布尔值的 Activity 上使用 onUserInteraction() 来确定用户是否正在与设备进行交互。

            http://developer.android.com/reference/android/app/Activity.html#onUserInteraction()

            @Override
            public void onUserInteraction() {
                super.onUserInteraction();
                userIsInteracting = true;
            }
            

            作为我拥有的 Activity 的一个字段:

            private boolean userIsInteracting;
            

            最后,我的微调器:

                mSpinnerView.setOnItemSelectedListener(new OnItemSelectedListener() {
            
                    @Override
                    public void onItemSelected(AdapterView<?> arg0, View view, int position, long arg3) {
                        spinnerAdapter.setmPreviousSelectedIndex(position);
                        if (userIsInteracting) {
                            updateGUI();
                        }
                    }
            
                    @Override
                    public void onNothingSelected(AdapterView<?> arg0) {
            
                    }
                });
            

            当您来回进行活动时,布尔值将重置为 false。像魅力一样工作。

            【讨论】:

            • 非常感谢.. 在做了很多事情之后,我的代码中没有任何效果,因为适配器数据在运行时通知,但您提供的解决方案 userIsInteracting 解决了问题
            • 你能详细说明“当你来来去去的活动” - 因为你的代码中没有任何东西将 userIsInteracting 设置为 false。
            • 你是对的。导致问题的交互与最初填充的微调器有关。当项目被添加到列表时,它错误地调用 onItemSelected()。如果列表已经填充,那么您可能不会再次添加任何内容和/或您已经离开活动并且您将通过 onCreate() 返回,这将重置该值。
            【解决方案9】:

            如果您也想购买initially unselected Spinner,您可以选择

            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                if(pos != 0) {
                    pos--;
            
                    // your code here
                }
            }
            

            因为永远不可能选择第 0 项。干杯:-)

            【讨论】:

            • 这绝对是最好的解决方案 - 也适用于多个微调器 - 谢谢 :-)
            • 我认为您假设位置 0 的项目是提示。即便如此,如果用户改变主意并决定再次选择会怎样。那么他的选择会被撞倒一个吗?不可取的行为。不过我喜欢这个概念,而是使用布尔字段来实现。
            • 我相信 NothingSelectedSpinnerAdapter 永远不会改变项目的数量,但是对于索引 0 使用 isEnabled(int position) false。
            【解决方案10】:

            我遇到了同样的问题。但是接受的解决方案对我不起作用,因为我的表单上有多个微调器,其中一些是隐藏的,当微调器被隐藏时,侦听器不会触发,所以计数不是我的解决方案。

            我找到了另一种解决方案

            1. 在初始化微调器时、在活动的 onCreate() 或 Fragment 的 onCreateView 中以及在设置侦听器之前在微调器上设置默认 TAG
            2. 在监听器中,检查标签,如果存在,则删除它,不执行代码,返回。

            就是这样,它就像一个魅力!

            监听器中的示例:

                    if(((String)parent.getTag()).equalsIgnoreCase(TAG_SPINNERVALUE))
                    {
                        parent.setTag("");
                        return;
                    }
            

            附:这适用于设置为多个微调器的同一个侦听器!

            【讨论】:

            • 我不明白你为什么要比较字符串的内容。我认为设置一个常量TAG_INITIALIZING="";(或任何其他对象)然后与`if (parentView.getTag() == TAG_INITIALIZING) {parentView.setTag(null);return;}`比较更优雅
            • 是的,但是你忘记了下次你必须检查null,如果你在其他地方不做这个检查,你会得到NullReferenceException。这样使用更安全。此外,像这样,您可以将标签重用于任何其他字符串值,例如我的应用程序中需要这样的东西:)(即我检查标签的多个值)
            【解决方案11】:

            我遇到了同样的问题,我通过记住之前选择的微调器值解决了这个问题。在每次 onItemSelected 调用中,我都会将新值与前一个值进行比较,如果相同,则不再进一步处理代码。

            【讨论】:

            • 我也做了同样的事情,因为其他一些原因我不得不记住这个位置。谢谢哥们的提示
            【解决方案12】:

            我也在互联网上寻找了一个好的解决方案,但没有找到任何满足我需求的解决方案。 所以我在 Spinner 类上编写了这个扩展,这样你就可以设置一个简单的 OnItemClickListener,它的行为与 ListView 相同。

            只有当一个项目被“选中”时,才会调用 onItemClickListener。

            玩得开心!

            public class MySpinner extends Spinner
            {
                private OnItemClickListener onItemClickListener;
            
            
                public MySpinner(Context context)
                {
                    super(context);
                }
            
                public MySpinner(Context context, AttributeSet attrs)
                {
                    super(context, attrs);
                }
            
                public MySpinner(Context context, AttributeSet attrs, int defStyle)
                {
                    super(context, attrs, defStyle);
                }
            
                @Override
                public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener inOnItemClickListener)
                {
                    this.onItemClickListener = inOnItemClickListener;
                }
            
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    super.onClick(dialog, which);
            
                    if (this.onItemClickListener != null)
                    {
                        this.onItemClickListener.onItemClick(this, this.getSelectedView(), which, this.getSelectedItemId());
                    }
                }
            }
            

            【讨论】:

            • 看起来它在 HC 和 ICS 上可能会失败。有人见过吗?
            • 它在 ICS 上确实失败了,似乎它只适用于 Gingerbread_MR1 及以下。
            【解决方案13】:

            我已经用不同的方式解决了这个问题,因为我注意到有时 onItemSelected 方法在方向改变时被调用两次,有时被调用一次。

            这似乎取决于当前的微调器选择。所以标志或计数器对我不起作用。相反,我使用 onResume() 和 onCreate 记录系统时间 widgetsRestartTime = System.currentTimeMillis()

            widgetsRestartTime 被声明为双精度变量和实例变量。

            现在在 onItemSelected() 方法中,我再次检查当前时间并从中减去 widgetsRestartTime,如下所示: if (System.currentTimeMillis() - widgetsRestartTime > 200) { // 用户触发事件 - 所以这是一个真正的微调器事件,所以处理它 } 别的 { // 系统生成的事件方向改变,活动启动。所以忽略 }

            【讨论】:

              【解决方案14】:

              Androider,我已经找到了解决这个问题的方法,并在此处发布(附代码示例):

              Spinner onItemSelected() executes when it is not suppose to

              【讨论】:

              • 哈哈哈,作为该问题的副本而关闭的问题的链接...被接受为该问题的答案:)
              • 对“情节扭曲”一词的纯粹澄清
              • 其实这个词应该是“递归”
              • 无限循环。 .
              【解决方案15】:

              是的,您看到的是正确的并且是默认行为,您无法阻止这种情况。 OnItemSelected 回调在初始加载时调用。在初始加载之后,仅当用户更改选择或您从代码中更改选择时才会触发它。您可以有一个指示器,它可以告诉您该事件是否是初始加载的结果,如果您不想处理它,则忽略第一个事件。

              【讨论】:

              • 我想该指标将是 onclick 内的某种标志。
              • @Androider 你能告诉我你是怎么解决这个问题的吗?我在 onCreate() 中尝试了一个标志。它在首次创建应用程序时有效,但在方向更改后出现问题。谢谢
              • new View.OnClickListener() { Boolean autoSelected=true; // 在这里声明 public void onClick(View v) {
              • someSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView> parent, View view, int index, long arg3) {
              • if (autoSelected==false){ autoSelected=true; viewDialog.cancel(); }else { autoSelected=false; }
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-11-13
              相关资源
              最近更新 更多