Comment garder onItemSelected de tirer sur un Spinner nouvellement instancié?

J'ai pensé à des moyens moins qu'élégants pour résoudre ce problème, mais je sais que je dois manquer quelque chose.

Mon onItemSelected déclenche immédiatement et sans aucune interaction avec l'utilisateur, et c'est un comportement indésirable. Je souhaite que l'interface utilisateur attende que l'utilisateur sélectionne quelque chose avant de faire quoi que ce soit.

J'ai même essayé de configurer l'écouteur dans le onResume(), en espérant que cela aiderait, mais ce n'est pas le cas.

Comment puis-je empêcher cela de se déclencher avant que l'utilisateur puisse toucher le contrôle?

public class CMSHome extends Activity { 

private Spinner spinner;

@Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Heres my spinner ///////////////////////////////////////////
    spinner = (Spinner) findViewById(R.id.spinner);
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
            this, R.array.pm_list, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);
    };

public void onResume() {
    super.onResume();
    spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}

    public class MyOnItemSelectedListener implements OnItemSelectedListener {

    public void onItemSelected(AdapterView<?> parent,
        View view, int pos, long id) {

     Intent i = new Intent(CMSHome.this, ListProjects.class);
     i.putExtra("bEmpID", parent.getItemAtPosition(pos).toString());
        startActivity(i);

        Toast.makeText(parent.getContext(), "The pm is " +
          parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
    }

    public void onNothingSelected(AdapterView parent) {
      // Do nothing.
    }
}
}
377
demandé sur CaptJak 2010-04-01 21:18:06

30 réponses

Je m'attendais à ce que votre solution fonctionne - je pense que l'événement de sélection ne se déclencherait pas si vous définissez l'adaptateur avant de configurer l'écouteur.

Cela étant dit, un simple indicateur booléen vous permettrait de détecter l'événement rogue first selection et de l'ignorer.

68
répondu CommonsWare 2010-04-01 17:53:21

L'utilisation de Runnables est complètement incorrecte.

Utiliser setSelection(position, false); dans la sélection initiale avant de setOnItemSelectedListener(listener)

De Cette façon, vous définissez votre sélection avec pas d'animation qui provoque l'élément sélectionné auditeur à être appelé. Mais l'auditeur est nulle donc, rien n'est exécuté. Ensuite, votre auditeur est affecté.

Alors suivez cette séquence exacte:

Spinner s = (Spinner)Util.findViewById(view, R.id.sound, R.id.spinner);
s.setAdapter(adapter);
s.setSelection(position, false);
s.setOnItemSelectedListener(listener);
351
répondu Brad 2013-06-27 07:19:03

Se référant à la réponse de Dan Dyer, essayez d'enregistrer le OnSelectListener dans une méthode post(Runnable):

spinner.post(new Runnable() {
    public void run() {
        spinner.setOnItemSelectedListener(listener);
    }
});

En faisant cela pour moi, le comportement souhaité s'est finalement produit.

Dans ce cas, cela signifie également que l'écouteur ne se déclenche que sur un élément modifié.

184
répondu casaflowa 2013-08-14 09:22:34

J'ai créé une petite méthode utilitaire pour changer la sélection Spinner sans avertir l'utilisateur:

private void setSpinnerSelectionWithoutCallingListener(final Spinner spinner, final int selection) {
    final OnItemSelectedListener l = spinner.getOnItemSelectedListener();
    spinner.setOnItemSelectedListener(null);
    spinner.post(new Runnable() {
        @Override
        public void run() {
            spinner.setSelection(selection);
            spinner.post(new Runnable() {
                @Override
                public void run() {
                    spinner.setOnItemSelectedListener(l);
                }
            });
        }
    });
}

Il désactive l'écouteur, modifie la sélection et réactivera l'écouteur après cela.

L'astuce est que les appels sont asynchrones au thread de L'interface utilisateur, vous devez donc le faire dans des messages de gestionnaire consécutifs.

46
répondu karooolek 2013-03-24 07:53:09

Malheureusement, il semble que les deux solutions les plus couramment suggérées à ce problème, à savoir le comptage des occurrences de rappel et l'affichage D'un Runnable pour définir le rappel ultérieurement, peuvent échouer lorsque, par exemple, les options d'accessibilité sont activées. Voici une classe d'aide qui fonctionne autour de ces problèmes. Une explication supplémentaire est dans le bloc de commentaires.

import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;

/**
 * Spinner Helper class that works around some common issues 
 * with the stock Android Spinner
 * 
 * A Spinner will normally call it's OnItemSelectedListener
 * when you use setSelection(...) in your initialization code. 
 * This is usually unwanted behavior, and a common work-around 
 * is to use spinner.post(...) with a Runnable to assign the 
 * OnItemSelectedListener after layout.
 * 
 * If you do not call setSelection(...) manually, the callback
 * may be called with the first item in the adapter you have 
 * set. The common work-around for that is to count callbacks.
 * 
 * While these workarounds usually *seem* to work, the callback
 * may still be called repeatedly for other reasons while the 
 * selection hasn't actually changed. This will happen for 
 * example, if the user has accessibility options enabled - 
 * which is more common than you might think as several apps 
 * use this for different purposes, like detecting which 
 * notifications are active.
 * 
 * Ideally, your OnItemSelectedListener callback should be
 * coded defensively so that no problem would occur even
 * if the callback was called repeatedly with the same values
 * without any user interaction, so no workarounds are needed.
 * 
 * This class does that for you. It keeps track of the values
 * you have set with the setSelection(...) methods, and 
 * proxies the OnItemSelectedListener callback so your callback
 * only gets called if the selected item's position differs 
 * from the one you have set by code, or the first item if you
 * did not set it.
 * 
 * This also means that if the user actually clicks the item
 * that was previously selected by code (or the first item
 * if you didn't set a selection by code), the callback will 
 * not fire.
 * 
 * To implement, replace current occurrences of:
 * 
 *     Spinner spinner = 
 *         (Spinner)findViewById(R.id.xxx);
 *     
 * with:
 * 
 *     SpinnerHelper spinner = 
 *         new SpinnerHelper(findViewById(R.id.xxx))
 *         
 * SpinnerHelper proxies the (my) most used calls to Spinner
 * but not all of them. Should a method not be available, use: 
 * 
 *      spinner.getSpinner().someMethod(...)
 *
 * Or just add the proxy method yourself :)
 * 
 * (Quickly) Tested on devices from 2.3.6 through 4.2.2
 * 
 * @author Jorrit "Chainfire" Jongma
 * @license WTFPL (do whatever you want with this, nobody cares)
 */
public class SpinnerHelper implements OnItemSelectedListener {
    private final Spinner spinner;

    private int lastPosition = -1;
    private OnItemSelectedListener proxiedItemSelectedListener = null;  

    public SpinnerHelper(Object spinner) {
         this.spinner = (spinner != null) ? (Spinner)spinner : null;        
    }

    public Spinner getSpinner() {
        return spinner;
    }

    public void setSelection(int position) { 
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position);     
    }

    public void setSelection(int position, boolean animate) {
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position, animate);        
    }

    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        proxiedItemSelectedListener = listener;
        spinner.setOnItemSelectedListener(listener == null ? null : this);
    }   

    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (position != lastPosition) {
            lastPosition = position;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onItemSelected(
                        parent, view, position, id
                );
            }
        }
    }

    public void onNothingSelected(AdapterView<?> parent) {
        if (-1 != lastPosition) {
            lastPosition = -1;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onNothingSelected(
                        parent
                );
            }
        }
    }

    public void setAdapter(SpinnerAdapter adapter) {
        if (adapter.getCount() > 0) {
            lastPosition = 0;
        }
        spinner.setAdapter(adapter);
    }

    public SpinnerAdapter getAdapter() { return spinner.getAdapter(); } 
    public int getCount() { return spinner.getCount(); }    
    public Object getItemAtPosition(int position) { return spinner.getItemAtPosition(position); }   
    public long getItemIdAtPosition(int position) { return spinner.getItemIdAtPosition(position); }
    public Object getSelectedItem() { return spinner.getSelectedItem(); }
    public long getSelectedItemId() { return spinner.getSelectedItemId(); }
    public int getSelectedItemPosition() { return spinner.getSelectedItemPosition(); }
    public void setEnabled(boolean enabled) { spinner.setEnabled(enabled); }
    public boolean isEnabled() { return spinner.isEnabled(); }
}
30
répondu Jorrit 2013-06-15 15:40:11

J'ai eu beaucoup de problèmes avec le tir de spinner de Quand Je ne voulais pas, et toutes les réponses ici ne sont pas fiables. Ils travaillent-mais seulement parfois. Vous finirez par rencontrer des scénarios où ils échoueront et introduiront des bogues dans votre code.

Ce qui a fonctionné pour moi était de stocker le dernier index sélectionné dans une variable et de l'évaluer dans l'écouteur. Si c'est la même chose que le nouvel index sélectionné ne rien faire et retourner, sinon continuer avec l'écouteur. Faites ceci:

//Declare a int member variable and initialize to 0 (at the top of your class)
private int mLastSpinnerPosition = 0;

//then evaluate it in your listener
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {

  if(mLastSpinnerPosition == i){
        return; //do nothing
  }

  mLastSpinnerPosition = i;
  //do the rest of your code now

}

Confiance moi quand je dis cela, c'est de loin la solution la plus fiable. Un hack, mais ça fonctionne!

27
répondu Chris 2014-09-08 08:32:52

J'étais dans une situation similaire, et j'ai une solution simple qui fonctionne pour moi.

Il semble que les méthodes setSelection(int position) et setSelected(int position, boolean animate) aient une implémentation interne différente.

Lorsque vous utilisez la deuxième méthode setSelected(int position, boolean animate), avec de faux animer drapeau, vous obtiendrez la sélection sans tirs onItemSelected auditeur.

25
répondu Michal 2013-02-13 06:41:37

Juste pour étoffer des indices à l'aide de l'onTouchListener pour distinguer entre les appels automatiques à setOnItemSelectedListener (qui font partie de l'initialisation de l'activité, etc.) par rapport aux appels déclenchés par l'interaction réelle de l'utilisateur, j'ai fait ce qui suit après avoir essayé d'autres suggestions ici et j'ai constaté que cela fonctionnait bien avec le moins de lignes de code.

Définissez simplement un champ booléen pour votre activité/Fragment comme:

private Boolean spinnerTouched = false;

Puis juste avant de régler votre spinner setOnItemSelectedListener, définit un onTouchListener:

    spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            spinnerTouched = true;
            return false;
        }
    });

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    ...
         if (spinnerTouched){
         //Do the stuff you only want triggered by real user interaction.
        }
        spinnerTouched = false;
16
répondu JASON G PETERSON 2016-02-06 14:53:20
spinner.setSelection(Adapter.NO_SELECTION, false);
12
répondu j2emanue 2014-06-21 21:05:11

Après avoir arraché Mes cheveux pendant longtemps, j'ai créé ma propre classe de Spinner. J'ai ajouté une méthode qui déconnecte et connecte l'écouteur de manière appropriée.

public class SaneSpinner extends Spinner {
    public SaneSpinner(Context context) {
        super(context);
    }

    public SaneSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SaneSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    // set the ceaseFireOnItemClickEvent argument to true to avoid firing an event
    public void setSelection(int position, boolean animate, boolean ceaseFireOnItemClickEvent) {
        OnItemSelectedListener l = getOnItemSelectedListener();
        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(null);
        }

        super.setSelection(position, animate);

        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(l);
        }
    }
}

Utilisez-le dans votre XML comme ceci:

<my.package.name.SaneSpinner
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/mySaneSpinner"
    android:entries="@array/supportedCurrenciesFullName"
    android:layout_weight="2" />

Tout ce que vous avez à faire est de récupérer L'instance de SaneSpinner après l'inflation et d'appeler la sélection set comme ceci:

mMySaneSpinner.setSelection(1, true, true);

, Avec cela, aucun événement est déclenché et l'interaction de l'utilisateur n'est pas interrompu. Cela a beaucoup réduit la complexité de mon code. Cela devrait être inclus en stock Android car il est vraiment un PITA.

8
répondu fusion44 2015-05-31 09:07:26

Aucun événement indésirable de la phase de mise en page si vous reportez l'ajout de l'écouteur jusqu'à ce que la mise en page soit terminée:

spinner.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // Ensure you call it only once works for JELLY_BEAN and later
            spinner.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            // add the listener
            spinner.setOnItemSelectedListener(new OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                    // check if pos has changed
                    // then do your work
                }

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

            });

        }
    });
7
répondu redocoder 2015-07-29 15:22:57

J'ai eu une réponse très simple, 100% sûr que cela fonctionne:

boolean Touched=false; // this a a global variable

public void changetouchvalue()
{
   Touched=true;
}

// this code is written just before onItemSelectedListener

 spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            changetouchvalue();
            return false;
        }
    });

//inside your spinner.SetonItemSelectedListener , you have a function named OnItemSelected iside that function write the following code

if(Touched)
{
 // the code u want to do in touch event
}
4
répondu user6656805 2016-08-17 07:17:28

J'ai trouvé une solution beaucoup plus élégante à cela. Cela implique de compter combien de fois le ArrayAdapter (dans votre cas "adaptateur") a été invoqué. Disons que vous avez 1 spinner et que vous appelez:

int iCountAdapterCalls = 0;

ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
            this, R.array.pm_list, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);

Déclarez un compteur int après la méthode onCreate, puis à l'intérieur de la méthode onItemSelected (), mettez une condition" if " pour vérifier combien de fois l'atapter a été appelé. Dans votre cas, vous l'avez appelé juste une fois donc:

if(iCountAdapterCalls < 1)
{
  iCountAdapterCalls++;
  //This section executes in onCreate, during the initialization
}
else
{
  //This section corresponds to user clicks, after the initialization
}
3
répondu g00dy 2013-02-08 08:22:49

Ma petite contribution est une variation sur certains de ce qui précède qui m'a adapté à quelques reprises.

Déclare une variable entière comme valeur par défaut (ou dernière valeur utilisée enregistrée dans les préférences). Utilisez spinner.setSelection (myDefault) pour définir cette valeur avant l'enregistrement de l'écouteur. Dans onItemSelected vérifiez si la nouvelle valeur de spinner est égale à la valeur que vous avez assignée avant d'exécuter tout autre code.

Cela a l'avantage supplémentaire de ne pas exécuter de code si l'utilisateur sélectionne le même valeur à nouveau.

1
répondu David Walton 2013-06-21 11:09:57

Après avoir eu le même problème, je suis venu à cette solution en utilisant des balises. L'idée derrière est simple: chaque fois que le spinner est changé par programme, assurez-vous que la balise reflète la position sélectionnée. Dans l'écouteur, vous vérifiez si la position sélectionnée est égale à la balise. Si c'est le cas, la sélection du spinner a été modifiée par programme.

Voici ma nouvelle classe" spinner proxy":

package com.samplepackage;

import com.samplepackage.R;
import android.widget.Spinner;

public class SpinnerFixed {

    private Spinner mSpinner;

    public SpinnerFixed(View spinner) {
         mSpinner = (Spinner)spinner;
         mSpinner.setTag(R.id.spinner_pos, -2);
    }

    public boolean isUiTriggered() {
         int tag = ((Integer)mSpinner.getTag(R.id.spinner_pos)).intValue();
         int pos = mSpinner.getSelectedItemPosition();
         mSpinner.setTag(R.id.spinner_pos, pos);
         return (tag != -2 && tag != pos);
    }

    public void setSelection(int position) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position);
    }

    public void setSelection(int position, boolean animate) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position, animate);
    }

    // If you need to proxy more methods, use "Generate Delegate Methods"
    // from the context menu in Eclipse.
}

Vous aurez également besoin d'un fichier XML avec la configuration de la balise dans votre répertoire Values. J'ai nommé mon fichier spinner_tag.xml, mais c'est à vous de voir. Il ressemble à ceci:

<resources xmlns:android="http://schemas.android.com/apk/res/android">
  <item name="spinner_pos" type="id" />
</resources>

Maintenant remplacer

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

Dans votre code avec

SpinnerFixed myspinner;
...
myspinner = new SpinnerFixed(findViewById(R.id.myspinner));

Et faites en sorte que votre gestionnaire ressemble un peu à ceci:

myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (myspinner.isUiTriggered()) {
            // Code you want to execute only on UI selects of the spinner
        }
    }

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

La fonction isUiTriggered() renvoie true si et seulement si le compteur a été changé par l'utilisateur. Notez que cette fonction a un effet secondaire - il définir la balise, donc un deuxième appel dans le même auditeur appel retournera toujours false.

Ce wrapper va également gérer le problème avec l'écouteur appelé lors de la création de la mise en page.

Amusez-vous, Jens.

1
répondu Jens 2014-10-27 20:42:57

Puisque rien n'a fonctionné pour moi, et j'ai plus de 1 spinner à mon avis (et à mon humble avis, tenir une carte bool est un excès) j'utilise la balise pour compter les clics:

spinner.setTag(0);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Integer selections = (Integer) parent.getTag();
            if (selections > 0) {
                // real selection
            }
            parent.setTag(++selections); // (or even just '1')
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        }
    });
1
répondu SagiLow 2015-08-24 17:39:20

Cela se produira si vous effectuez une sélection dans le code as;

   mSpinner.setSelection(0);

Au lieu de l'instruction ci-dessus, utilisez

   mSpinner.setSelection(0,false);//just simply do not animate it.

Edit: cette méthode ne fonctionne pas pour Mi version Android mi UI.

1
répondu Uzair 2017-07-25 11:15:35

Beaucoup de réponses déjà, voici la mienne.

J'étend AppCompatSpinner et ajoute une méthode pgmSetSelection(int pos) qui permet le réglage de la sélection programmatique sans déclencher un rappel de sélection. J'ai codé ceci avec RxJava afin que les événements de sélection soient livrés via un Observable.

package com.controlj.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;

import io.reactivex.Observable;

/**
 * Created by clyde on 22/11/17.
 */

public class FilteredSpinner extends android.support.v7.widget.AppCompatSpinner {
    private int lastSelection = INVALID_POSITION;


    public void pgmSetSelection(int i) {
        lastSelection = i;
        setSelection(i);
    }

    /**
     * Observe item selections within this spinner. Events will not be delivered if they were triggered
     * by a call to setSelection(). Selection of nothing will return an event equal to INVALID_POSITION
     *
     * @return an Observable delivering selection events
     */
    public Observable<Integer> observeSelections() {
        return Observable.create(emitter -> {
            setOnItemSelectedListener(new OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if(i != lastSelection) {
                        lastSelection = i;
                        emitter.onNext(i);
                    }
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {
                    onItemSelected(adapterView, null, INVALID_POSITION, 0);
                }
            });
        });
    }

    public FilteredSpinner(Context context) {
        super(context);
    }

    public FilteredSpinner(Context context, int mode) {
        super(context, mode);
    }

    public FilteredSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
        super(context, attrs, defStyleAttr, mode);
    }
}

Un exemple de son utilisation, appelé dans onCreateView() dans a Fragment par exemple:

    mySpinner = view.findViewById(R.id.history);
    mySpinner.observeSelections()
        .subscribe(this::setSelection);

setSelection() est une méthode dans la vue englobante qui ressemble à ceci, et qui est appelée à la fois à partir des événements de sélection de l'utilisateur via le Observable et aussi ailleurs par programmation, de sorte que la logique de gestion des sélections est commune aux deux méthodes de sélection.

private void setSelection(int position) {
    if(adapter.isEmpty())
        position = INVALID_POSITION;
    else if(position >= adapter.getCount())
        position = adapter.getCount() - 1;
    MyData result = null;
    mySpinner.pgmSetSelection(position);
    if(position != INVALID_POSITION) {
        result = adapter.getItem(position);
    }
    display(result);  // show the selected item somewhere
}
1
répondu Clyde 2017-11-22 22:43:05

J'essaierais d'appeler

spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());

Après avoir appelé setAdapter (). Essayez également d'appeler avant l'adaptateur.

Vous avez toujours la solution pour aller avec le sous-classement, où vous pouvez envelopper un drapeau booléen à votre méthode setadapter overriden pour ignorer l'événement.

0
répondu Pentium10 2010-04-01 18:26:30

La solution avec un drapeau booléen ou un compteur ne m'a pas aidé, car pendant le changement d'orientation onItemSelected () appelle "overflew" le drapeau ou le compteur.

J'AI sous-classé android.widget.Spinner et fait de minuscules ajouts. Les parties pertinentes sont ci-dessous. Cette solution a fonctionné pour moi.

private void setHandleOnItemSelected()
{
  final StackTraceElement [] elements = Thread.currentThread().getStackTrace();

  for (int index = 1; index < elements.length; index++)
  {
     handleOnItemSelected = elements[index].toString().indexOf("PerformClick") != -1; //$NON-NLS-1$

     if (handleOnItemSelected)
     {
        break;
     }
  }
}

@Override
public void setSelection(int position, boolean animate)
{
  super.setSelection(position, animate);

  setHandleOnItemSelected();
}

@Override
public void setSelection(int position)
{
  super.setSelection(position);

  setHandleOnItemSelected();
}

public boolean shouldHandleOnItemSelected()
{
  return handleOnItemSelected;
}
0
répondu RobinBobin 2012-07-14 16:15:18

Ce n'est pas une solution élégante non plus. En fait, C'est plutôt Rube-Goldberg mais cela semble fonctionner. Je m'assure que le spinner a été utilisé au moins une fois en étendant l'adaptateur de tableau et en remplaçant son getDropDownView. Dans la nouvelle méthode getDropDownView, j'ai un indicateur booléen qui est défini pour montrer que le menu déroulant a été utilisé au moins une fois. - Je ignorer les appels à l'auditeur jusqu'à ce que le drapeau est réglé.

MainActivity.onCreate ():

ActionBar ab = getActionBar();
ab.setDisplayShowTitleEnabled(false);
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
ab.setListNavigationCallbacks(null, null);

ArrayList<String> abList = new ArrayList<String>();
abList.add("line 1");
...

ArAd  abAdapt = new ArAd (this
   , android.R.layout.simple_list_item_1
   , android.R.id.text1, abList);
ab.setListNavigationCallbacks(abAdapt, MainActivity.this);

Tableau substitué adaptateur:

private static boolean viewed = false;
private class ArAd extends ArrayAdapter<String> {
    private ArAd(Activity a
            , int layoutId, int resId, ArrayList<String> list) {
        super(a, layoutId, resId, list);
        viewed = false;
    }
    @Override
    public View getDropDownView(int position, View convertView,
            ViewGroup parent) {
        viewed = true;
        return super.getDropDownView(position, convertView, parent);
    }
}

Auditeur Modifié:

@Override
public boolean onNavigationItemSelected(
   int itemPosition, long itemId) {
   if (viewed) {
     ...
   }
   return false;
}
0
répondu steven smith 2015-03-17 21:55:37

Si vous avez besoin de recréer une activité à la volée, par exemple: changer de thème, un simple drapeau/compteur ne fonctionnera pas

Utilisez la fonction onUserInteraction () pour détecter l'activité de l'utilisateur,

Référence : https://stackoverflow.com/a/25070696/4772917

0
répondu dev-gaek 2017-05-23 11:47:24

{j'ai[3]}fait avec la façon la plus simple:

private AdapterView.OnItemSelectedListener listener;
private Spinner spinner;

OnCreate();

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

listener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {

            Log.i("H - Spinner selected position", position);
        }

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

        }
    };

 spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            spinner.setOnItemSelectedListener(listener);
        }

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

        }
    });

Fait

0
répondu Hiren Patel 2015-07-11 16:35:49
if () {        
       spinner.setSelection(0);// No reaction to create spinner !!!
     } else {
        spinner.setSelection(intPosition);
     }


spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

         if (position > 0) {
           // real selection
         }

      }

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

     }
});
0
répondu Gennady Kozlov 2015-09-13 14:39:12

C'est ma solution finale et facile à utiliser:

public class ManualSelectedSpinner extends Spinner {
    //get a reference for the internal listener
    private OnItemSelectedListener mListener;

    public ManualSelectedSpinner(Context context) {
        super(context);
    }

    public ManualSelectedSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ManualSelectedSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
        mListener = listener;
        super.setOnItemSelectedListener(listener);
    }

    public void setSelectionWithoutInformListener(int position){
        super.setOnItemSelectedListener(null);
        super.setSelection(position);
        super.setOnItemSelectedListener(mListener);
    }

    public void setSelectionWithoutInformListener(int position, boolean animate){
        super.setOnItemSelectedListener(null);
        super.setSelection(position, animate);
        super.setOnItemSelectedListener(mListener);
    }
}

Utilisez le setSelection(...) par défaut pour le comportement par défaut ou utilisez setSelectionWithoutInformListener(...) pour sélectionner un élément dans le spinner sans déclencher le rappel OnItemSelectedListener.

0
répondu MatPag 2016-12-20 12:12:08

J'ai besoin d'utiliser mSpinner dans ViewHolder, donc l'indicateur mOldPosition est défini dans la classe interne anonyme.

mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            int mOldPosition = mSpinner.getSelectedItemPosition();

            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long l) {
                if (mOldPosition != position) {
                    mOldPosition = position;
                    //Do something
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
                //Do something
            }
        });
0
répondu Francis Bacon 2017-03-09 08:49:12

Je stockerais l'index initial lors de la création de l'objet onClickListener.

   int thisInitialIndex = 0;//change as needed

   myspinner.setSelection(thisInitialIndex);

   myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

      int initIndex = thisInitialIndex;

      @Override
      public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         if (id != initIndex) { //if selectedIndex is the same as initial value
            // your real onselecteditemchange event
         }
      }

      @Override
      public void onNothingSelected(AdapterView<?> parent) {
      }
  });
0
répondu Ray Lionfang 2017-08-29 08:06:20

Ma solution utilise onTouchListener mais ne restreint pas son utilisation. Il crée un wrapper pour onTouchListener si nécessaire where setup onItemSelectedListener.

public class Spinner extends android.widget.Spinner {
    /* ...constructors... */

    private OnTouchListener onTouchListener;
    private OnItemSelectedListener onItemSelectedListener;

    @Override
    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        onItemSelectedListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    @Override
    public void setOnTouchListener(OnTouchListener listener) {
        onTouchListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    private OnTouchListener wrapTouchListener(final OnTouchListener onTouchListener, final OnItemSelectedListener onItemSelectedListener) {
        return onItemSelectedListener != null ? new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                Spinner.super.setOnItemSelectedListener(onItemSelectedListener);
                return onTouchListener != null && onTouchListener.onTouch(view, motionEvent);
            }
        } : onTouchListener;
    }
}
0
répondu Dem0n13 2017-09-22 11:00:46

Je pourrais répondre trop tard sur le post, mais j'ai réussi à y parvenir en utilisant la bibliothèque de liaison de données Android Android Databinding . J'ai créé une liaison personnalisée pour m'assurer que l'écouteur n'est pas appelé tant que l'élément sélectionné n'est pas modifié, même si l'utilisateur sélectionne la même position encore et encore, l'événement n'est pas déclenché.

Fichier xml de mise en page

    <layout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_vertical_margin"
xmlns:app="http://schemas.android.com/apk/res-auto">


<Spinner
    android:id="@+id/spinner"
    android:layout_width="150dp"
    android:layout_height="wrap_content"
    android:spinnerMode="dropdown"
    android:layout_below="@id/member_img"
    android:layout_marginTop="@dimen/activity_vertical_margin"
    android:background="@drawable/member_btn"
    android:padding="@dimen/activity_horizontal_margin"
    android:layout_marginStart="@dimen/activity_horizontal_margin"
    android:textColor="@color/colorAccent"
    app:position="@{0}"
    />
 </RelativeLayout>
 </layout>

app:position est l'endroit où vous passez la position à sélectionner.

Personnalisé la liaison

  @BindingAdapter(value={ "position"}, requireAll=false)
  public static void setSpinnerAdapter(Spinner spinner, int selected) 
  {

    final int [] selectedposition= new int[1];
    selectedposition[0]=selected;


    // custom adapter or you can set default adapter
        CustomSpinnerAdapter customSpinnerAdapter = new CustomSpinnerAdapter(spinner.getContext(), <arraylist you want to add to spinner>);
        spinner.setAdapter(customSpinnerAdapter);
            spinner.setSelection(selected,false);


    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            String item = parent.getItemAtPosition(position).toString();
        if( position!=selectedposition[0]) {
                        selectedposition[0]=position;
            // do your stuff here
                    }
                }


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

        }
    });
}

Vous pouvez en savoir plus sur la liaison de données personnalisée ici Android Custom Setter

NOTE

  1. N'oubliez pas d'activer la liaison de données dans votre fichier Gradle

       android {
     ....
     dataBinding {
     enabled = true
    }
    }
    
  2. Inclure vos fichiers de mise en <layout> balises

0
répondu N.Moudgil 2017-10-18 06:06:41
mYear.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View arg1, int item, long arg3) {
                if (mYearSpinnerAdapter.isEnabled(item)) {

                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });
-1
répondu Saurabh Malik 2014-12-27 00:53:38