Android Spinner: obtenha o evento de alteração de item selecionado

407

Como você pode definir o ouvinte de evento para um Spinner quando o item selecionado é alterado?

Basicamente, o que estou tentando fazer é algo semelhante a isto:

spinner1.onSelectionChange = handleSelectionChange;

void handleSelectionChange(Object sender){
    //handle event
}
Soni Ali
fonte
Eu tentei essas respostas, mas ninguém foi útil. Uma vez que o componente Spinner não suporta eventos de clique no item. Documentação do Spinner

Respostas:

812

Algumas das respostas anteriores não estão corretas. Eles funcionam para outros widgets e visualizações, mas a documentação para o widget Spinner afirma claramente:

Um girador não suporta eventos de clique no item. Chamar esse método gerará uma exceção.

Melhor usar 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
    }

});

Isso funciona para mim.

Observe que o método onItemSelected também é chamado quando a exibição está sendo construída, para que você possa colocá-lo dentro da onCreate()chamada de método.

znq
fonte
47
o problema é que o método onItemSelected também é chamado quando a exibição está sendo construída. Assim, o código que está escrito lá também é executado na inicialização. Existe uma maneira de executar o código que contém apenas se houver uma seleção de item real invocada pelo usuário?
Kennethvr
39
na verdade, esse problema pode ser resolvido colocando o setOnItemSelectedListener no método OnStart de substituição e não no método onCreate. me estúpido ...
Kennethvr
16
Coloquei o ouvinte no método onStart, mas ele é chamado antes que o usuário veja alguma coisa, assim como onCreate, portanto, no meu caso, um botão "continuar" que deve ficar invisível até que o usuário selecione algo, o botão fica visível na exibição inicial. Você está dizendo que sua experiência é diferente? Se sim, o que você está fazendo de diferente no método onStart que estou perdendo?
Yevgeny Simkin
7
Use outro campo dentro do seu ouvinte anônimo para registrar a primeira seleção e diga ao onItemSelected para não fazer nada, a menos que uma seleção tenha sido encontrada? Apenas um pensamento.
Sam Svenbjorgchristiensensen 21/03
4
Mas e se o usuário selecionar o item "padrão", aquele no topo? Então onItemSelected(...)não é atingido. (Eu sei porque eu só descobriu isso da maneira mais difícil.)
Andrew Wyld
55
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;
    } 
}); 

Nota: Lembre - se de uma coisa.

O OnItemSelectedListenerevento giratório será executado duas vezes:

  1. Inicialização do girador
  2. Usuário selecionado manualmente

Tente diferenciar os dois usando a variável flag.

Santhosh
fonte
3
O meu também é chamado duas vezes! Eu não consigo descobrir como você diferencia os dois?
MAC
18
Basta definir um booleano global como o booleano initialDisplay = true; E, em seu onSelect, veja se é verdade e se for, não faça o que mais você faria ao selecionar, mas defina o sinalizador como false, para a próxima vez que for chamado (quando o usuário realmente selecionar).
Yevgeny Simkin
Melhor explicação sobre a execução do OnclickListener.
Pankaj Kumar
6
Pessoalmente, estou chocado que algo tão simples - um widget tão básico da interface do usuário - seja tão difícil de implementar ... sério - quão difícil seria construir uma propriedade 'item de exibição padrão' e construir a propriedade de sinalizador booleano no objeto própria classe ?? Não sou fã do Objetivo C, mas direi que a implementação do widget iOS leva cerca de 1/10 do tempo no Android.
Bennett Von Bennett,
4
Eu também concordo. O Spinner é um widget estragado. É muito difícil saber quando o pop-up ou drop é aberto ou fechado. Teria sido tão difícil adicionar um evento para isso? A solução acima pode QUASE dizer quando a lista está aberta ou fechada, mas tem três problemas: (1) não há evento para selecionar o item já selecionado (e a lista fecha) e (2) não há evento para abortar (toque em off-list para fechá-lo) e (3) onNothingSelected nunca parece disparar para mim.
Batdude
19

Você pode implementar a AdapterView.OnItemSelectedListenerclasse em sua atividade.

E então use a linha abaixo dentro onCreate()

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

Em seguida, substitua esses dois métodos:

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

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

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

Você pode evitar que o OnItemSelectedListener () seja chamado com uma verificação simples: armazene o índice de seleção atual em uma variável inteira e verifique dentro do onItemSelected (..) antes de fazer qualquer coisa.

Por exemplo:

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

De causa, o iCurrentSelectiondeve estar no escopo do objeto para que isso funcione!

Sampath
fonte
1
Você não pode usar uma variável não final dentro de uma classe interna anônima. Se a iCurrentSelectionvariável for declarada nessa classe anônima, ela funcionará bem. Você pode inicializá-lo para -1 para que o código seja executado na primeira chamada.
Dahvyd 19/05
2
@dahvyd estava correto se você usar isso, o int deve ser final. De qualquer forma, funciona muito bem. Eu estava desabilitando um campo EditText se a posição 0 não estava selecionada e se ela mudou, reative-a. Obrigado por isso.
Natur3
8

Encontre o nome do seu girador e encontre o ID e implemente esse método.

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
    }
});
droidster.me
fonte
8

Não importa se você definir OnItemSelectedListener em onCreate ou onStart - ainda será chamado durante a criação da atividade ou o início (respectivamente).
Assim, podemos configurá-lo em onCreate (e NÃO em onStart!).
Basta adicionar um sinalizador para descobrir a primeira inicialização:

private Spinner mSpinner;
private boolean mSpinnerInitialized;

em onCreate (ou onCreateView), basta:

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;
            }
        });
Leo Droidcoder
fonte
1
Obrigado por usar esta bandeira.
Javan R.
7

Os documentos para o widget giratório dizem

Um girador não suporta eventos de clique no item.

Você deve usar setOnItemSelectedListenerpara lidar com o seu problema.

johndotnet
fonte
6
spinner1.setOnItemSelectedListener(
    new AdapterView.OnItemSelectedListener() {
        //add some code here
    }
);
Chiwai Chan
fonte
1
Isso não resolve o problema desse retorno de chamada quando o spinner é iniciado pela primeira vez (provocando uma resposta que nada tem a ver com um item realmente selecionado).
Yevgeny Simkin
4

pegue uma variável global para a seleção atual do 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>
indrajeet
fonte
4

Se você deseja um verdadeiro onChangedListener (). Armazene o valor inicial no manipulador e verifique se ele foi alterado. É simples e não requer uma variável global. Funciona se você tiver mais de um botão giratório na página.

String initialValue = // get from Database or your object
mySpinner.setOnItemSelectedListener(new SpinnerSelectedListener(initialValue));

...

protected class SpinnerSelectedListener implements AdapterView.OnItemSelectedListener {

        private SpinnerSelectedListener() {
            super();
        }

        public SpinnerSelectedListener(String initialValue) {
            this();
            this.initialValue = initialValue;
        }

        private String initialValue;

        // getter and setter removed.  

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            final String newValue = (String) spinHeight.getItemAtPosition(position);
            if (newValue.equals(initialValue) == false) {
               // Add your code here.  The spinner has changed value. 

               // Maybe useful.   
               // initialValue = newValue;
            }

        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
               // Maybe useful.   
               // initialValue = null; 
        }
    }

Os objetos são seus amigos, use-os.

fishjd
fonte
3
spinner.setOnItemSelectedListener(
            new AdapterView.OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> arg0, View arg1,
                        int arg2, long arg3) {

                    // TODO Auto-generated method stub
                }

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

                }
                //add some code here
            }
        );
kaub0st3r
fonte
1

Isso funcionará para inicializar o girador e o findviewbyid e, usando isso, ele funcionará

    Spinner schemeStatusSpinner;

  schemeStatusSpinner = (Spinner) dialog.findViewById(R.id.spinner);

schemeStatusSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
            // your code here
            if(schemeStatusSpinner.getSelectedItemId()==4){
                reasonll.setVisibility(View.VISIBLE);
            }
            else {
                reasonll.setVisibility(View.GONE);
            }
        }

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

    });
Sanjay
fonte
1

A melhor maneira que eu acho que seria ter um flagitemselected = 0;in onCreate(). E no item selecionado, o evento incrementa esse sinalizador, ie flagitemselected++; e depois verifique

if(flagitemselected!=1)
{
// do your work here
}

Isso vai ajudar, eu acho.

Pinakin
fonte
0

Um truque que encontrei foi colocar seus setOnItemSelectedListeners em onWindowFocusChanged em vez de em onCreate. Ainda não encontrei nenhum efeito colateral ruim dessa maneira. Basicamente, configure os ouvintes depois que a janela for fechada. Não sei ao certo quantas vezes o onWindowFocusChanged é executado, mas é fácil o suficiente criar uma variável de bloqueio se você a encontrar executando com muita frequência.

Eu acho que o Android pode estar usando um sistema de processamento baseado em mensagens e, se você colocar tudo no onCreate, pode se deparar com situações nas quais o botão giratório é preenchido depois de ser desenhado. Portanto, seu ouvinte será acionado depois que você definir o local do item. Este é um palpite, é claro, mas fique à vontade para me corrigir.

Joe Plante
fonte
0

Por padrão, você obterá o primeiro item da matriz giratória através de

value = spinner.getSelectedItem().toString();

sempre que você selecionou o valor no botão giratório, isso fornecerá o valor selecionado

se você deseja a posição do item selecionado, faça-o assim

pos = spinner.getSelectedItemPosition();

as duas respostas acima são para sem aplicar o ouvinte

Adam Noor
fonte