【问题标题】:How to disable onItemSelectedListener to be invoked when setting selected item by code通过代码设置所选项目时如何禁用要调用的onItemSelectedListener
【发布时间】:2011-01-18 20:22:53
【问题描述】:

只是想知道您是如何处理以下问题的:根据两个微调器选择的项目计算结果。为了处理 UI 方面的事情,即用户在其中一个微调器中选择一个新项目,我在我的活动的 onCreate() 方法中为微调器使用 setOnItemSelectedListener 安装了一个侦听器。

现在:当然,这行得通。监听器的工作是触发对结果的新计算。

问题:因为我拦截了onPause()onResume() 来保存/恢复上一个状态,所以我有一个方法可以设置这两个微调器的选定项目以编程方式,如下所示:

startSpinner.setSelection(pStart);
destSpinner.setSelection(pDest);

这两个调用也调用了侦听器!我的结果计算方法加上新结果集的通知在这里调用了两次!

一个愚蠢的直接方法是有一个布尔变量禁用无论监听器在里面做什么,在设置所选项目之前设置它并在之后重置它。 好的。但是有没有更好的方法??

我不希望监听器被代码调用——动作,只有用户动作! :-(

你是怎么做到的? 谢谢!

【问题讨论】:

  • 您可以使用 setSelection(pStart, false) 作为动画参数。但它并非一直有效。

标签: android listener spinner


【解决方案1】:

在我看来,区分程序化和用户发起的更改的更简洁的解决方案如下:

为微调器创建你的监听器作为 OnTouchListener 和 OnItemSelectedListener

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

}

将侦听器添加到为两种事件类型注册的微调器

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

这样,由于初始化或重新初始化而对处理程序方法的任何意外调用都将被忽略。

【讨论】:

  • 绝对是我见过的最好的解决方案。有时我希望 Spinner 被弃用!
  • 如果使 SpinnerInteractionListener 抽象类并创建抽象方法 onSelected(AdapterView> parent, View view, int pos, long id, boolean userSelected) 那么您可以捕获所有事件(用户、初始化、等)并以不同的方式对它们做出反应。这就是我需要的。完美的解决方案,谢谢。
  • 我已经尽我所能来解决与 Spinner 相关的问题,这就是最终的解决方案。我相信任何实现过 Spinner 的人都应该这样做。 API 基本上是开箱即用的,这是缺少的功能。非常感谢!
  • 如果用户“触摸”视图 [但不更改数据] 并且稍后如果应用设置微调器,则它会失败 - 然后执行侦听器
【解决方案2】:

好的,我让它按照我现在想要的方式工作。

这里要理解的事情(我在写这个问题时没有理解......)是 Android 中的所有内容都在一个线程中运行 - UI 线程。

含义:即使您在这里和那里设置 Spinner 的值:它们只是更新(视觉上)并且它们的侦听器仅在您当前使用的所有方法之后被调用 (如onCreateonResume 或其他)已完成。

这允许以下操作:

  • 将选定的位置保留在字段变量中。 (如currentPos1currentPos2
  • 监听器onItemSelectedListener() 调用类似refreshMyResult() 之类的方法。
  • 以编程方式设置位置时,设置微调器在此之后立即手动调用您自己的刷新方法。

refreshMyResult() 方法如下所示:

int newPos1 = mySpinner1.getSelectedItemPosition();
int newPos2 = mySpinner2.getSelectedItemPosition();
// only do something if update is not done yet
if (newPos1 != currentPos1 || newPos2 != currentPos2) {
    currentPos1 = newPos1;
    currentPos2 = newPos2;

    // do whatever has to be done to update things!

}

因为稍后将调用侦听器 - 到那时,currentPos 中记住的位置已经更新 - 不会发生任何事情,也不会发生任何不必要的更新。 当用户在其中一个微调器中选择一个新值时,那么 - 将相应地执行更新!

就是这样! :-)

啊——还有一件事:我的问题的答案是:不。 侦听器不能(很容易)被禁用,并且会在值更改时被调用。

【讨论】:

  • 我在使用 onCheckChanged 时遇到了类似的问题,我解决的方法是使用 buttonView 的 isPressed 方法验证用户输入是否调用了代码。因此,恕我直言,我认为您的方法可能是不明智的,除非我当然误解了这个问题。检查验证它是由用户调用的,而不是你的 onPause() 和 onResume() 方法。
  • 我喜欢 Zordid 的方法,因为无论输入来自何处(用户输入或之前保存的),除非值发生变化,否则在我的应用中做正确的事情是什么。
  • @Zach: isPressed() 在 onItemSelectedListener() 中始终为 false。根据文档, isPressed() 指示视图当前是否处于按下状态。 OnItemSelectedListener 注册一个回调,以便在此 AdapterView 中的项目已被选择时调用。我在 onItemSelectedListener() 里面放了一条调试消息,发现 isPressed() 总是错误的。我挖掘了代码,发现 isSelected() 可能是正确的使用方法。但是不,这种方法在 onLiitemSelectedListener 中也总是错误的。所以 Zordid 的方法只是解决方案,它在我的代码中按预期工作。
【解决方案3】:

我有一个更简单,而且我认为更好的解决方案。由于即使在初始化之后我也必须刷新微调器,这是一种更通用的方法。 请参考接受的答案:

Undesired onItemSelected calls

【讨论】:

  • 非常感谢,听起来不错!当我有时间时,我将不得不更改我的代码......
  • 有趣的是你也回答了你自己的问题 - 就像我之前在我的问题中所做的那样...... :)
  • :D 是的。更有趣的是,在看到你的回答后,我明白了回调的工作原理:)
【解决方案4】:

首先添加用于停止微调器侦听器调用的布尔值

  Boolean check = false;

然后你添加触摸监听器和项目点击监听器就像下面的代码

 holder.filters.setOnTouchListener(new View.OnTouchListener() {
               @Override
               public boolean onTouch(View v, MotionEvent event) {

                   check = true;
                   return false;
               }
           });

           holder.filters.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
           {

               @Override
               public void onItemSelected(AdapterView<?> parent, View arg1, int position, long id)
               {
                   flag = filterids.get(position);

                   if(check)
                   {
                       check = false;
                       new Applyfilters().execute(flag,"3");
                   }else{

                   }

               }

               @Override
               public void onNothingSelected(AdapterView<?> arg0)
               {
                   // TODO Auto-generated method stub
               }
           });

它很简单,可以多次停止服务器调用。

【讨论】:

    【解决方案5】:

    您可以很容易地使用false 调用Spinner.setSelection(int position, boolean animate) 方法,这样听众就不会对更改做出反应。

    【讨论】:

    • 适合我(Api 16)
    • animate 参数决定了微调器动画(选择是否应该直接选择 (false) 或不选择 (true))。它与 onItemSelected 调用无关。
    • 这不是正确的答案。这不适用于我 - Android 4.4.4。
    • 试过 setSelection(spinner, false)。没有变化,触发监听器。安卓 4.4.4
    【解决方案6】:

    Spinner.setSelection(int position, boolean animate) 确实会在 4.3 上触发监听器

    【讨论】:

    • 这不是我的问题的答案,对不起。
    【解决方案7】:

    我创建了一个对所有人都有帮助的库,无需在 Spinner 中调用 item onClick 操作 例如:

    spinner.setSelection(withAction,position);
    

    withAction 是一个布尔标志,用于调用或不调用项目操作

    Github 上的链接: https://github.com/scijoker/spinner2

    【讨论】:

      【解决方案8】:

      我的解决方案很简单。首先初始化一个全局布尔变量。

      boolean shouldWork = true;
      

      然后在你的 onCreate() 方法中使用下面的代码。

      Spinner spinner = findViewById(R.id.spinner);
      spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
          @Override
          public void onItemSelected(AdapterView adapter, View v, int i, long lng) {
              if (shouldWork) {
                     // Do your actions here
              }
              else
                  shouldWork = true;
          }
          public void onNothingSelected(AdapterView<?> parentView)  {
      
          }
      });
      

      现在您可以在任何地方使用 setSelection 方法,而无需通过以下代码调用 onItemSelected() 方法。

      shouldWork = false;
      spinner.setSelection(0);
      

      【讨论】:

        【解决方案9】:

        为每个微调器添加OnItemSelectedListener您在onResume 中设置了任何先前的值。

        【讨论】:

        • 问题是一个正在运行的活动可能会获得一个新的意图,应该将微调器设置为一个新的值——因此活动加上它的所有侦听器肯定会被设置!另外:我尝试在设置值并稍后恢复侦听器之前使用setOnItemSelectedListener(null) 停用侦听器:没有任何效果!侦听器被异步调用! :-(
        【解决方案10】:

        当使用 Spinner.setSelection(position) 时,它总是激活 setOnItemSelectedListener()

        为了避免两次触发代码,我使用了这个解决方案:

        private mIsSpinnerFirstCall=true;
        
        ...
        Spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                //If a new value is selected (avoid activating on setSelection())
                if(!mIsSpinnerFirstCall) {
                    // Your code goes gere
                }
                mIsSpinnerFirstCall = false;
            }
        
            public void onNothingSelected(AdapterView<?> arg0) {
            }
        });
        

        当您确定我们使用了 Spinner.setSelection(position) 时,此解决方案有效。另外,在每次使用 Spinner.setSelection(position) 之前设置 mIsSpinnerFirstCall=true 也很重要

        【讨论】:

          【解决方案11】:

          在我的例子中,由于我以编程方式触发微调器,所以我只需在 spinner.performClick() 之后添加 spinnerSelected 标志,如下所示。

          private var spinnerSelected = false
          
          someView.setOnClickListener {
              spinner.performClick()
              spinnerSelected = true
          }
          
          spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
              override fun onNothingSelected(parent: AdapterView<*>?) {
                  // do nothing
              }
          
              override fun onItemSelected(
                  parent: AdapterView<*>?,
                  view: View?,
                  position: Int,
                  id: Long
              ) {
                  if (spinnerSelected) {
                      //... do something
          
                      spinnerSelected = false
                  }
                }
              }
          

          【讨论】:

            【解决方案12】:
                This following method will help you to stop invoking automatically the selection listener
            
            
                yourspinnerobj.post(new Runnable() {
                            @Override
                            public void run() {
                                yourspinnerobj.setOnItemSelectedListener(yourspinnerlistener);
                            }
                        });
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-05-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多