Android Spinner:初始化期间避免onItemSelected调用


156

我使用Spinner和创建了一个Android应用程序TextView。我想从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

    }
}

Spinners始终具有默认的选定值,如果您希望拥有一个没有默认选定值的Spinner,则应创建自定义的Spinner或创建一个空条目的自定义Spinner,并在getView()方法中更改以下项的可见性GONE的原始布局
Houcine 2012年

4
为何在2018年底仍然存在这种烦人的bug ????
user-123

Answers:


174
spinner.setOnItemSelectedListener(this); // Will call onItemSelected() Listener.

所以第一次用任何Integer值处理

示例:最初服用 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);
   }
}

您可以使用布尔值,也可以通过检查当前和以前的位置来做到这一点。看这里


2
很棒的人..当我第一次遇到这个问题时,我尝试实现自定义微调器。
Sash_KP 2014年

1
您在哪里申报支票?在getView()之外?在viewHolder内部?哪里?尝试了您的解决方案,但对我不起作用。
user3718908 '18

9
它是一个补丁,我猜不是解决方案。
萨克斯姆(Saksham)

1
我正面临着同样的问题。是某种错误吗?另外,如果我反复选择相同的项目,则第一次收听后听众将无法使用。仅当选择项更改时,它才有效。有任何意见,帮助吗?
阿米塔瓦

1
我用同样的问题尝试了此解决方案,但是这只能使人正常工作,例如:当我在下拉菜单中有2个项目时,当我第一次选择任何微调项时,它都会工作,而当我第二次尝试其无法正常工作时
Waseem

116

只需在设置OnItemSelectedListener之前放置此行

spinner.setSelection(0,false)

7
如果您要写出这有什么帮助以及原因,那将是一个更好的答案。
user3533716

1
它有帮助,但想如何?
AEMLoviji '16

14
之所以有效,是因为您首先设置了选择,然后添加了一个侦听器,但是由于您之前已经选择了此选择,因此不会调用该侦听器。只有新的选择才会调用侦听器。
Android开发人员

4
之所以有效,是因为在内部进行了setSelection(int, boolean)调用setSelectionInt(),因此您需要在调用此方法之后(而不是之前)设置侦听器。当心,这是setSelection(int)行不通的,因为它是setNextSelectedPositionInt()内部调用的,这就是导致我来到这里的原因。
张海

3
如果在onCreateView()之前或之前声明,则此方法不起作用。onItemSelected将被调用。
Arash

62

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

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

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

作为活动的一个字段,我有:

 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。奇迹般有效。


3
好法案。.我认为这比被接受的答案更好的解决方案
Ritesh Gune '16

您在哪里得到setmPreviousSelectedIndex?!?!
TheQ

错别字?:) setPreviousSelectedIndex()
比尔·莫特

4
如果嵌套片段是无效的,因为onUserInteraction是Activity方法。还有其他解决方案吗?
Chitrang

1
@ErikB nvm,弄清楚了。在监听器中将其设置为false可以正常工作。
西坎德

20

这对我有用

Android中Spinner的初始化存在问题,有时上述问题已通过此模式解决。

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

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


11

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

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

好人!以前我申请过的东西对我没有任何帮助,但是这对我来说就像是魅​​力。感谢@TreeSouth
Deepika Lalra 2015年

11
对我来说,以相同的方式使用spinner.setSelection(position,false)。使用setSelection(position)方法,可在初始化期间调用侦听器。
马里奥·库特列夫

2
@HugoGresse尝试调用spinner.setSelection(0,false); 。事情是,现在它将忽略此位置的选择,因为它已被选择
android开发人员

8

我的解决方案:

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

        }

7

为了避免在初始化期间调用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) {

    }
});

6

您可以通过以下方式执行此操作:

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) {

        }
    });

首先,我创建一个侦听器,并将其分配给变量回调。然后我创建第二个匿名监听器,并且在第一次调用时,将改变监听器=]


3

然后,可以在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
        }
    }

}

将侦听器同时添加为微调器OnItemSelectedListener和an OnTouchListener

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

2

允许多个微调器使用的类似简单解决方案是,在首次执行onItemSelected(...)时,将AdapterView放入Activity超类中的集合中,然后在执行之前检查AdapterView是否在集合中。这将启用超类中的一组方法,并支持多个AdapterViews及其多个微调器。

超类...

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.

}


2

创建一个布尔字段

private boolean inispinner;

在活动的内部创建

    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) {

        }
    });


1

您可以先通过setOnTouchListener实现,然后在onTouch中通过setOnItemSelectedListener实现

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

我喜欢这个。即使每次用户触摸视图都会创建一个新的侦听器。所以我更喜欢缓存第一个创建的侦听器并重用它。
弗拉德(Vlad)'18

1

这对我有用:

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

1

根据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)
        }
    }
}

0

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

我有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在更新后触发。


0

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) {

            }
        });

0

对我而言,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
            }
        }

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


0

我在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调用的情况下使用,但是我真的不喜欢假设它总是被调用的想法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.