Android下拉菜单:获取所选项目更改事件

454

当Spinner选择的项目发生变化时,如何设置事件监听器?

基本上我想做的是类似于这样的事情:

spinner1.onSelectionChange = handleSelectionChange;

void handleSelectionChange(Object sender){
    //handle event
}

我尝试过这些答案,但没有一个有用。Spinner组件不支持项目点击事件。 Spinner文档 - user1470285
17个回答

901

之前某些答案是不正确的。它们可以用在其他部件和视图上,但是 Spinner 部件的文档明确规定:

Spinner 不支持item点击事件。调用此方法将会引发异常。

最好使用 OnItemSelectedListener () 替代:

spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
        // your code here
    }

    @Override
    public void onNothingSelected(AdapterView<?> parentView) {
        // your code here
    }

});

这对我有用。

请注意,在构建视图时也会调用onItemSelected方法,因此您可以考虑将其放在onCreate()方法调用中。


53
问题在于当视图构建时,onItemSelected方法也会被调用。因此,在其中编写的代码也会在启动时执行。是否有一种方式只在用户进行真正的项目选择时才执行包含的代码? - Kennethvr
40
实际上,这个问题可以通过将setOnItemSelectedListener放在覆盖OnStart方法中而不是在onCreate方法中解决。我太蠢了... - Kennethvr
17
我已经将监听器放在onStart方法中,但它会在用户看到任何内容之前被调用,就像onCreate一样。因此,在我的情况下,当用户选择某些内容时,一个"proceed"按钮应该是不可见的,但在初始显示时,该按钮是可见的。您是说您的经验与此不同吗?如果是这样,请问您在onStart方法中做了什么不同的事情,我可能忽略了什么? - Yevgeny Simkin
7
在您的匿名监听器中使用另一个字段记录第一次选择,告诉onItemSelected除非已经遇到选择,否则不要执行任何操作?这只是一个想法。 - Sam Svenbjorgchristiensensen
4
如果用户选择了位于顶部的“默认”选项,那么 onItemSelected(...) 方法就不会被触发。(我知道这是因为我刚刚吃了这个亏。) - Andrew Wyld
显示剩余7条评论

64
Spinner spnLocale = (Spinner)findViewById(R.id.spnLocale);

spnLocale.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { 
        // Your code here
    } 

    public void onNothingSelected(AdapterView<?> adapterView) {
        return;
    } 
}); 

注意:记住一件事。

Spinner的OnItemSelectedListener事件会执行两次:

  1. Spinner初始化
  2. 用户手动选择

尝试使用标志变量区分这两种情况。


3
我的也被调用了两次!我无法弄清楚你是如何区分这两个调用的? - MAC
22
只需设置一个全局布尔变量,比如Boolean initialDisplay = true; 然后在你的 onSelect 方法中检查它是否为 true ,如果是,则不执行你原本打算在选择时执行的任何其他操作,而是将标志设置为 false,以便下次调用时(即用户实际选择时)可以执行操作。 - Yevgeny Simkin
8
我个人感到非常震惊,一个如此简单的基础用户界面小部件居然如此难以实现...说真的,构建一个“默认显示物品”属性并将布尔标志属性内置到对象类本身中有多难呢?我不是Objective C的粉丝,但我会说iOS小部件实现所需时间约为Android的十分之一。 - Bennett Von Bennett
6
我也同意。Spinner 控件有些问题,很难知道弹出框或下拉框是打开还是关闭。添加一个事件难道就这么难吗?上述解决方法几乎可以告诉你列表是打开还是关闭,但有三个问题:(1) 没有事件可用于选择已选项目(并关闭列表),(2) 没有中止事件(按列表外部关闭它),以及 (3) onNothingSelected 似乎从来没有触发过。 - Batdude
Genia S.的方法并不是非常有效,因为第一次将在onCreate方法中触发onselect事件,您可以通过全局变量排除此事件,但如果我需要选择已经选择的项目(默认项目,第一个),它将不会被触发。我尝试过放置一个Spinner标签并将其排除,但它将在打开的对话框上可见。我同意@Batdude的看法。 Spinner是一个混乱的小部件。 - jedi
显示剩余4条评论

25

您可以在Activity中实现AdapterView.OnItemSelectedListener类。

然后在onCreate()内使用以下行:

Spinner spin = (Spinner) findViewById(R.id.spinner);
spin.setOnItemSelectedListener(this);

然后重写这两个方法:

public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
    selection.setText(items[position]);
}

public void onNothingSelected(AdapterView<?> parent) {
    selection.setText("");
}

我怎样获取“选择”? - bro
原始来源:https://commonsware.com/Android/excerpt.pdf#page=10 - undefined

21

https://stackoverflow.com/q/1714426/811625

您可以通过简单的检查来避免调用OnItemSelectedListener():将当前选择的索引存储在整数变量中,在执行任何操作之前在onItemSelected(..)内进行检查。

E.g:

Spinner spnLocale;

spnLocale = (Spinner)findViewById(R.id.spnLocale);

int iCurrentSelection = spnLocale.getSelectedItemPosition();

spnLocale.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { 
    if (iCurrentSelection != i){
            // Your code here
    }
    iCurrentSelection = i;
    } 

    public void onNothingSelected(AdapterView<?> adapterView) {
        return;
    } 
}); 

当然,为了使这个方法起作用,iCurrentSelection 应该在对象范围内!

1
你不能在匿名内部类中使用非 final 变量。如果 iCurrentSelection 变量在这个匿名类中声明,它将正常工作。你可以将其初始化为 -1,这样代码会在第一次调用时执行。 - dahvyd
2
@dahvyd 是正确的,如果你使用这个,int 必须是 final 的。无论如何,它非常有效。如果位置 0 没有被选中,我会禁用 EditText 字段,如果它改变了,就重新启用它。感谢您的帮助。 - natur3

12
无论您在onCreate或onStart中设置OnItemSelectedListener,它都将在Activity创建或启动期间调用(分别)。因此,我们可以在onCreate中设置它(而不是在onStart中!)只需添加一个标志以找出第一次初始化即可。
所以,我们可以在onCreate中设置它(而不是在onStart中!),只需添加一个标志以找出第一次初始化。
private Spinner mSpinner;
private boolean mSpinnerInitialized;

然后在 onCreate(或 onCreateView)中只需:

mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                if (!mSpinnerInitialized) {
                    mSpinnerInitialized = true;
                    return;
                }

                // do stuff
            }

            public void onNothingSelected(AdapterView<?> adapterView) {
                return;
            }
        });

1
感谢您使用此标志。 - Javan R.

9
找到你的spinner名称和ID,然后实现此方法。
spinnername.setOnItemSelectedListener(new OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
        // your code here
    }

    @Override
    public void onNothingSelected(AdapterView<?> parentView) {
        // your code here
    }
});

8
spinner1.setOnItemSelectedListener(
    new AdapterView.OnItemSelectedListener() {
        //add some code here
    }
);

3
这并没有解决当下拉菜单首次初始化时调用此回调函数的问题(从而引发与实际选择项无关的响应)。 - Yevgeny Simkin

8

旋转选择小部件的文档说

旋转选择小部件不支持项目点击事件。

您应该使用 setOnItemSelectedListener 来解决您的问题。


8

对于Kotlin,您可以使用:

spinner.onItemSelectedListener =  object : AdapterView.OnItemSelectedListener {
    override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        
    }

    override fun onNothingSelected(p0: AdapterView<*>?) {
        
    }
}

注意:对于onItemSelected方法的参数,我使用自定义变量名。

4

为当前Spinner选择采用全局变量:

int currentItem = 0;

spinner_counter = (Spinner)findViewById(R.id.spinner_counter);
String[] value={"20","40","60","80","100","All"};
aa=new ArrayAdapter<String>(this,R.layout.spinner_item_profile,value);
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner_counter.setAdapter(aa);

spinner_counter.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if(currentItem == position){
                return; //do nothing
            }
            else
            {
                 TextView spinner_item_text = (TextView) view;
                 //write your code here
            }
            currentItem = position;
        }

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

        }
    });

//R.layout.spinner_item_profile
<?xml version="1.0" encoding="utf-8"?>

<TextView  android:id="@+id/spinner_item_text"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" 
android:layout_height="wrap_content"
android:background="@drawable/border_close_profile"
android:gravity="start"  
android:textColor="@color/black"         
android:paddingLeft="5dip"
android:paddingStart="5dip"
android:paddingTop="12dip"
android:paddingBottom="12dip"
/>

//drawable/border_close_profile
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
   <shape android:shape="rectangle">
    <solid android:color="#e2e3d7" />
   </shape>
 </item>
<item android:left="1dp"
android:right="1dp"
android:top="1dp"
android:bottom="1dp">
<shape android:shape="rectangle">
    <solid android:color="@color/white_text" />
</shape>
</item>
</layer-list>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接