【问题标题】:Android Spinner : Avoid onItemSelected calls during initializationAndroid Spinner:在初始化期间避免 onItemSelected 调用
【发布时间】:2012-11-15 12:55:23
【问题描述】:

我使用SpinnerTextView 创建了一个Android 应用程序。我想在 TextView 的 Spinner 下拉列表中显示所选项目。我在onCreate 方法中实现了 Spinner,所以当我运行程序时,它会在TextView 中显示一个值(在从下拉列表中选择一个项目之前)。

我只想在从下拉列表中选择一个项目后在 TextView 中显示该值。我该怎么做?

这是我的代码:

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

public class GPACal01Activity extends Activity implements OnItemSelectedListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Spinner spinner = (Spinner) findViewById(R.id.noOfSubjects);

        // Create an ArrayAdapter using the string array and a default spinner layout
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,R.array.noofsubjects_array, android.R.layout.simple_spinner_item);
        // Specify the layout to use when the list of choices appears
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        // Apply the adapter to the spinner
        spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(this);
    }

    public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
        TextView textView = (TextView) findViewById(R.id.textView1);
        String str = (String) parent.getItemAtPosition(pos);
        textView.setText(str);
    }

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

    }
}

【问题讨论】:

  • 微调器总是有一个默认选择值,如果你想有一个没有默认选择值的微调器,你应该创建你的自定义微调器,或者创建一个带有空条目的自定义微调器,并在 getView () 方法,将原始布局的可见性更改为 GONE
  • 2018年底怎么还存在这么烦人的bug???
  • @user-123 你的意思是 2020 年? ;)
  • @musooff 你的意思是 2021 年?
  • 在这里我找到了 Perfact 的答案。 stackoverflow.com/a/69367698/640913

标签: android android-layout android-spinner


【解决方案1】:
spinner.setOnItemSelectedListener(this); // Will call onItemSelected() Listener.

所以第一次用任何整数值处理这个

示例: 初取int check = 0;

public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
   if(++check > 1) {
      TextView textView = (TextView) findViewById(R.id.textView1);
      String str = (String) parent.getItemAtPosition(pos);
      textView.setText(str);
   }
}

您可以使用布尔值执行此操作,也可以检查当前和以前的位置。 See here

【讨论】:

  • 真棒人..当我第一次遇到这个问题时,我尝试实现自定义微调器..仍然没有用..但是你的解决方案就像一个魅力..谢谢。
  • 你在哪里申报支票?在 getView() 之外?在 viewHolder 里面?在哪里?尝试了您的解决方案,但对我不起作用。
  • 我猜它是一个补丁而不是解决方案。
  • 我也面临同样的问题。这是某种错误吗?另外,如果我重复选择相同的项目,第一次后监听器将无法工作。它仅在选择项更改时才有效。有什么意见,帮忙?
  • 我用同样的问题尝试了这个解决方案,但这只有一个,例如:当我在下拉列表中有 2 个项目时,当我第一次选择任何微调器项目时它会启动,当我第二次尝试它时无法正常工作
【解决方案2】:

只需在设置OnItemSelectedListener之前添加这一行

spinner.setSelection(0,false)

这是因为setSelection(int, boolean) 在内部调用setSelectionInt(),所以当添加监听器时,项目已经被选中。

注意setSelection(int) 不起作用,因为它在内部调用setNextSelectedPositionInt()

【讨论】:

  • 如果您能写出这有什么帮助以及原因,那将是一个更好的答案。
  • 它有帮助,但想怎么做?
  • 这是有效的,因为您首先设置了选择,然后添加了一个侦听器,但不会调用该侦听器,因为您之前已经选择了此选择。只有新的选择会调用监听器。
  • 这是有效的,因为setSelection(int, boolean) 在内部调用setSelectionInt(),并且您需要在调用它之后(而不是之前)设置监听器。请注意setSelection(int) 不起作用,因为它在内部调用setNextSelectedPositionInt(),这就是导致我来到这里的原因。
  • 如果在 onCreateView() 期间或之前声明,这将不起作用。 onItemSelected 将被调用。
【解决方案3】:

从 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。像魅力一样工作。

【讨论】:

  • 好一个比尔..我认为这比被接受为答案的解决方案更好
  • 你从哪里得到 setmPreviousSelectedIndex ?!?!
  • 错字? :) setPreviousSelectedIndex()
  • 这在嵌套片段的情况下不起作用,因为 onUserInteraction 是 Activity 方法。还有其他解决方案吗?
  • @ErikB nvm,想通了。在侦听器中将其设置为 false 效果很好。
【解决方案4】:

这对我有用

Spinner 在 Android 中的初始化有时会出现问题 上面的问题就被这个模式解决了。

Spinner.setAdapter();
Spinner.setSelected(false);  // must
Spinner.setSelection(0,true);  //must
Spinner.setonItemSelectedListener(this);

在初始化微调器时,设置适配器应该是第一部分,onItemSelectedListener(this) 将是最后一部分。通过上面的模式,我的 OnItemSelected() 在微调器初始化期间没有被调用

【讨论】:

    【解决方案5】:

    为了避免在初始化期间调用 spinner.setOnItemSelectedListener()

    spinner.setSelection(Adapter.NO_SELECTION, true); //Add this line before setting listener
    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    
        }
    
        @Override
        public void onNothingSelected(AdapterView<?> parent) {
    
        }
    });
    

    【讨论】:

    • 这在 oncreate 和 resume 中都有效,但在使用 spinner.setSelection(0,false) 时遇到了问题
    【解决方案6】:

    哈哈...我也有同样的问题。 当 initViews() 这样做时。序列是关键,侦听器是最后一个。祝你好运!

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

    【讨论】:

    • 好一个!无论我以前申请过什么,都对我有用,但这个对我来说就像一种魅力。谢谢@TreeSouth
    • 对我来说 spinner.setSelection(position, false) 以相同的方式使用。使用 setSelection(position) 方法,侦听器在初始化期间被调用。
    • @HugoGresse 尝试调用 spinner.setSelection(0,false); .问题是,现在它会忽略这个位置的选择,因为它已经被选择了
    【解决方案7】:

    我的解决方案:

    protected boolean inhibit_spinner = true;
    
    
    @Override
            public void onItemSelected(AdapterView<?> arg0, View arg1,
                    int pos, long arg3) {
    
                if (inhibit_spinner) {
                    inhibit_spinner = false;
                }else {
    
                if (getDataTask != null) getDataTask.cancel(true);
                updateData();
                }
    
            }
    

    【讨论】:

      【解决方案8】:

      你可以这样做:

      AdapterView.OnItemSelectedListener listener = new AdapterView.OnItemSelectedListener() {
              @Override
              public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                  //set the text of TextView
              }
      
              @Override
              public void onNothingSelected(AdapterView<?> adapterView) {
      
              }
          });
      
      yourSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
              @Override
              public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                  yourSpinner.setOnItemSelectedListener(listener);
              }
      
              @Override
              public void onNothingSelected(AdapterView<?> adapterView) {
      
              }
          });
      

      一开始我创建了一个监听器并归因于一个变量回调;然后我创建了第二个匿名监听器,当第一次调用它时,这会改变监听器 =]

      【讨论】:

        【解决方案9】:

        创建一个布尔字段

        private boolean inispinner;
        

        在活动的 oncreate 内部

            spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                    if (!inispinner) {
                        inispinner = true;
                        return;
                    }
                    //do your work here
                }
        
                @Override
                public void onNothingSelected(AdapterView<?> parent) {
        
                }
            });
        

        【讨论】:

          【解决方案10】:

          启用多个微调器的类似简单解决方案是在第一次执行 onItemSelected(...) 时将 AdapterView 放在集合中 - 在活动超类中,然后在执行之前检查 AdapterView 是否在集合中。这在超类中启用了一组方法,并支持多个 AdapterView 和多个微调器。

          超类 ...

          private Collection<AdapterView> AdapterViewCollection = new ArrayList<AdapterView>();
          
             protected boolean firstTimeThrough(AdapterView parent) {
              boolean firstTimeThrough = ! AdapterViewCollection.contains(parent);
              if (firstTimeThrough) {
                 AdapterViewCollection.add(parent);
               }
              return firstTimeThrough;
             }
          

          子类 ...

          public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                if (! firstTimeThrough(parent)) {
                  String value = safeString(parent.getItemAtPosition(pos).toString());
                  String extraMessage = EXTRA_MESSAGE;
                  Intent sharedPreferencesDisplayIntent = new         Intent(SharedPreferencesSelectionActivity.this,SharedPreferencesDisplayActivity.class);
              sharedPreferencesDisplayIntent.putExtra(extraMessage,value);
              startActivity(sharedPreferencesDisplayIntent);
            }
            // don't execute the above code if its the first time through
            // do to onItemSelected being called during view initialization.
          

          }

          【讨论】:

            【解决方案11】:

            然后可以在 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) { 
                        userSelect = false;
                        // Your selection handling code here
                    }
                }
            
            }
            

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

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

            【讨论】:

              【解决方案12】:

              试试这个

              spinner.postDelayed(new Runnable() {
                      @Override
                      public void run() {
                          addListeners();
                      }
                  }, 1000);.o
              

              【讨论】:

                【解决方案13】:

                代码

                spinner.setOnTouchListener(new View.OnTouchListener() { 
                @Override public boolean onTouch(View view, MotionEvent motionEvent) { isSpinnerTouch=true; return false; }});
                
                holder.spinner_from.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                            @Override
                            public void onItemSelected(AdapterView<?> adapterView, View view, int slot_position, long l) {
                                if(isSpinnerTouch)
                                {
                                    Log.d("spinner_from", "spinner_from");
                                    spinnerItemClickListener.onSpinnerItemClickListener(position, slot_position, AppConstant.FROM_SLOT_ONCLICK_CODE);
                                }
                                else {
                
                                }
                            }
                
                            @Override
                            public void onNothingSelected(AdapterView<?> adapterView) {
                
                            }
                        });
                

                【讨论】:

                  【解决方案14】:

                  你可以先通过 setOnTouchListener 再在 onTouch 中通过 setOnItemSelectedListener 来实现

                  @Override
                  public boolean onTouch(final View view, final MotionEvent event) {
                   view.setOnItemSelectedListener(this)
                   return false;
                  }
                  

                  【讨论】:

                  • 我喜欢这个。即使每次用户触摸视图时它都会创建一个新的侦听器。所以我更喜欢缓存第一个创建的监听器并重用它。
                  【解决方案15】:

                  这对我有用:

                      spinner.setSelection(0, false);
                      new Handler().postDelayed(new Runnable() {
                              @Override
                              public void run() {
                                  spinner.setOnItemSelectedListener(listener);
                              }, 500);
                  

                  【讨论】:

                    【解决方案16】:

                    根据 Abhi 的回答,我做了这个简单的监听器

                    class SpinnerListener constructor(private val onItemSelected: (position: Int) -> Unit) : AdapterView.OnItemSelectedListener {
                    
                        private var selectionCount = 0
                    
                        override fun onNothingSelected(parent: AdapterView<*>?) {
                            //no op
                        }
                    
                        override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                            if (selectionCount++ > 1) {
                               onItemSelected(position)
                            }
                        }
                    }
                    

                    【讨论】:

                      【解决方案17】:

                      您可以像这样创建自定义OnItemSelectedListener。我采用了val check=0onItemSelected() 方法,我确实检查了它的计数是否为0?如果 0 表示它在初始化期间被调用。所以干脆忽略它。

                      我还调用了名为onUserItemSelected() 的单独抽象方法,我将此方法称为check &gt; 0。这对我来说非常好。

                      abstract class MySpinnerItemSelectionListener : AdapterView.OnItemSelectedListener {
                      
                      abstract fun onUserItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long)
                      private var check = 0
                      override fun onItemSelected(
                          parent: AdapterView<*>?,
                          view: View,
                          position: Int,
                          id: Long
                      ) {
                          if (++check > 1) {
                              onUserItemSelected(parent, view, position, id)
                          }
                      }
                      
                      override fun onNothingSelected(parent: AdapterView<*>?) {}
                      

                      }

                      然后你可以像这样设置监听器。

                      mySpinner.onItemSelectedListener =  object : MySpinnerItemSelectionListener() {
                              override fun onUserItemSelected(
                                  parent: AdapterView<*>?,
                                  view: View?,
                                  position: Int,
                                  id: Long
                              ) {
                                  //your user selection spinner code goes here
                              }
                      
                          }
                      

                      【讨论】:

                        【解决方案18】:

                        遇到了同样的问题,这对我有用:

                        我有 2 个微调器,我在初始化期间以及与其他控件交互期间或从服务器获取数据后更新它们。

                        这是我的模板:

                        public class MyClass extends <Activity/Fragment/Whatever> implements Spinner.OnItemSelectedListener {
                        
                            private void removeSpinnersListeners() {
                                spn1.setOnItemSelectedListener(null);
                                spn2.setOnItemSelectedListener(null);
                            }
                        
                            private void setSpinnersListeners() {
                                new Handler().postDelayed(new Runnable() {
                                    @Override
                                    public void run() {
                                        spn1.setOnItemSelectedListener(MyClass.this);
                                        spn2.setOnItemSelectedListener(MyClass.this);
                                    }
                                }, 1);
                            }
                        
                            @Override
                            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                                // Your code here
                            }
                        
                            @Override
                            public void onNothingSelected(AdapterView<?> parent) {
                        
                            }
                        }
                        

                        当类初始化时使用 setSpinnersListeners() 而不是直接设置监听器。

                        Runnable 将阻止微调器在您设置它们的值后立即触发 onItemSelected。

                        如果您需要更新微调器(在服务器调用等之后),请在更新行之前使用 removeSpinnersListeners(),并在更新后立即使用 setSpinnersListeners()线。这将防止 onItemSelected 在更新后触发。

                        【讨论】:

                          【解决方案19】:

                          对我来说,Abhi 的解决方案在 Api 级别 27 之前运行良好。

                          但似乎从 Api 级别 28 及更高级别开始,设置侦听器时不会调用 onItemSelected(),这意味着永远不会调用 onItemSelected()。

                          因此,我添加了一个简短的 if 语句来检查 Api 级别:

                          public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
                          
                                      if(Build.VERSION.SDK_INT >= 28){ //onItemSelected() doesn't seem to be called when listener is set on Api 28+
                                          check = 1;
                                      }
                          
                                      if(++check > 1) {
                                          //Do your action here
                                      }
                                  }
                          

                          我认为这很奇怪,我不确定其他人是否也有这个问题,但就我而言,它运行良好。

                          【讨论】:

                            【解决方案20】:

                            我在 Spinner 顶部放置了一个 TextView,大小和背景与 Spinner 相同,这样我就可以在用户点击它之前更好地控制它的外观。有了 TextView,我还可以使用 TextView 来标记用户何时开始交互。

                            我的 Kotlin 代码如下所示:

                            private var mySpinnerHasBeenTapped = false
                            
                            private fun initializeMySpinner() {
                            
                                my_hint_text_view.setOnClickListener {
                                    mySpinnerHasBeenTapped = true //turn flag to true
                                    my_spinner.performClick() //call spinner click
                                }
                            
                                //Basic spinner setup stuff
                                val myList = listOf("Leonardo", "Michelangelo", "Rafael", "Donatello")
                                val dataAdapter: ArrayAdapter<String> = ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, myList)
                                my_spinner.adapter = dataAdapter
                            
                                my_spinner.onItemSelectedListener = object : OnItemSelectedListener {
                            
                                    override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
                            
                                        if (mySpinnerHasBeenTapped) { //code below will only run after the user has clicked
                                            my_hint_text_view.visibility = View.GONE //once an item has been selected, hide the textView
                                            //Perform action here
                                        }
                                    }
                            
                                    override fun onNothingSelected(parent: AdapterView<*>?) {
                                        //Do nothing
                                    }
                                }
                            }
                            

                            布局文件看起来像这样,重要的部分是 Spinner 和 TextView 共享相同的宽度、高度和边距:

                                    <FrameLayout
                                        android:layout_width="match_parent"
                                        android:layout_height="wrap_content">
                            
                                        <Spinner
                                            android:id="@+id/my_spinner"
                                            android:layout_width="match_parent"
                                            android:layout_height="35dp"
                                            android:layout_marginStart="10dp"
                                            android:layout_marginEnd="10dp"
                                            android:background="@drawable/bg_for_spinners"
                            
                                            android:paddingStart="8dp"
                                            android:paddingEnd="30dp"
                                            android:singleLine="true" />
                            
                                        <TextView
                                            android:id="@+id/my_hint_text_view"
                                            android:layout_width="match_parent"
                                            android:layout_height="35dp"                
                                            android:layout_marginStart="10dp"
                                            android:layout_marginEnd="10dp"
                                            android:background="@drawable/bg_for_spinners"
                            
                                            android:paddingStart="8dp"
                                            android:paddingEnd="30dp"
                                            android:singleLine="true"
                                            android:gravity="center_vertical"
                                            android:text="*Select A Turtle"
                                            android:textColor="@color/green_ooze"
                                            android:textSize="16sp" />
                            
                                    </FrameLayout>
                            

                            我确信其他解决方案在您忽略第一个 onItemSelected 调用的情况下有效,但我真的不喜欢假设它总是会被调用的想法。

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2012-03-04
                              • 1970-01-01
                              • 2018-02-17
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多