How to keep onItemSelected from firing off on a newly instantiated Spinner?
Asked Answered
I

33

441

I've thought of some less than elegant ways to solve this, but I know I must be missing something.

My onItemSelected fires off immediately without any interaction with the user, and this is undesired behavior. I wish for the UI to wait until the user selects something before it does anything.

I even tried setting up the listener in the onResume(), hoping that would help, but it doesn't.

How can I stop this from firing off before the user can touch the control?

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.
    }
}
}
Intendant answered 1/4, 2010 at 17:18 Comment(3)
You can look at this solution, it is easy and practical. https://mcmap.net/q/81768/-spinner-onitemselected-called-erroneously-without-user-actionHimeji
A simple solution would be to make the first item in Spinner empty and inside onItemSelected you can detect if the String is not empty then startActivity!Biennial
This pattern works properly #13398433Guillermo
S
82

I would have expected your solution to work -- I though the selection event would not fire if you set the adapter before setting up the listener.

That being said, a simple boolean flag would allow you to detect the rogue first selection event and ignore it.

Skinnydip answered 1/4, 2010 at 17:53 Comment(17)
ugh, yeah. Thats what I meant by an inelegant solution. Seems like there must be a better way. Thank you though.Intendant
This thread on the Dev ml has more insight about this: groups.google.com/group/android-developers/browse_thread/thread/… - Unfortunately no solution is given...Mustard
The process of laying out the components fires the selection listener. You'd therefore have to add the listener after the layout has been done. I have been unable to find a suitable, straightforward place to do this as the layout seems to happen at some point after onResume() and onPostResume(), so all of the normal hooks have completed by the time the layout happens.Hohenzollern
I would stay away from this boolean flag - as if behavior changes in the future it could cause a bug. A more bullet-proof solution would be to keep a variable with the "current selected index", initialized to the first item selected. Then on selection event - check if it equals the new position - return and do nothing. Of course update the variable on selection.Atli
This does not work. Answer by @casanova works. That should be the accepted answer.Sessler
It is correct but you can do one more thing. Set the adapter. include mSpinner.setSelection(0, false); and set up the listener. I hope it works.Lit
you gotta have one flag for each spinner wich is not usefull. Besides, if your spinner is in fragment, you leave your app and ramBooster cleans up, then you return to app and your spinner will fire up twiceSebbie
This does not provide an answer to this highly popular question. It should be a comment, not an answer, and especially not the accepted answer. The real working reliable answer is provided by karooolek: https://mcmap.net/q/80527/-how-to-keep-onitemselected-from-firing-off-on-a-newly-instantiated-spinnerCelestinecelestite
@VioletGiraffe: "This does not provide an answer to this highly popular question" -- sure it does: "a simple boolean flag would allow you to detect the rogue first selection event and ignore it". You may not like that answer. Similarly, I do not like the answer that you highlighted, as it makes unfounded assumptions about the order and timing of events, assumptions that may not hold up over Android versions and manufacturer tweaks to Android. However, I would never claim that your chosen answer is not an answer. It's an answer, just one that I disagree with.Skinnydip
@CommonsWare: I understand, you have a point. I've made a mistake of treating the question as being more broad than it actually is: I need to prevent the OnItemSelected callback every time it's fired off in response to setSelection, and this question only talks about the first time that's during the initial UI layout process. For this particular purpose, your solution is better. The one I pointed is more universal, but possibly unreliable in the future, although I expect it to last very long. However, a hint at an answer is still not really an answer if you ask me.Celestinecelestite
Is it guaranteed that there will always be one and only one "rogue first selection event"? If so, how do we know that? It doesn't seem clear from the documentation. Without that guarantee, it seems that this solution also makes unfounded assumptions that may not hold up over Android versions and manufacturer tweaks to Android.Iniquitous
@LarsH: By that argument, you have no guarantees of getting any selection events. Or you could get 2,000 callbacks per selection. I readily agree that my solution is simplistic. It's also over nine years old and comes from a time when we could count the number of Android device models on one hand, with fingers left over. IMHO, Spinner is not a particularly well-designed widget with respect to this particular issue. If you need control over the implementation, look into third-party replacements.Skinnydip
"By that argument, you have no guarantees of getting any selection events." How so? The documentation says that setOnItemSelectedListener() will "Register a callback to be invoked when an item in this AdapterView has been selected." developer.android.com/guide/topics/ui/controls/spinner?hl=en also says "When the user selects an item from the drop-down, the Spinner object receives an on-item-selected event."Iniquitous
@LarsH: Part of your concern is with manufacturer tweaks. Manufacturers are not legally obligated to honor the documentation. So long as they pass the CTS and comply with the CDD, they can change what they want. That could include breaking expectations around Spinner. I would not expect them to break it, just as I would not expect them to break the assumption that my answer makes. But, stuff happens.Skinnydip
Sure, mfrs can tweak things. But assuming that OS implementers will honor documented behavior is a much safer assumption than assuming they will continue to honor undocumented behavior. In other words, it feels like you're drawing a false equivalence.Iniquitous
I don't have a problem with your answer per se; sometimes the best available course is to make do with relying on undocumented behavior that seems to happen consistently. I just don't understand why you say you don't like the solution Violet pointed to (relative to your own) because it makes unfounded assumptions, when yours does also. I say that because you've earned my respect for your Android knowledge and competence.Iniquitous
@LarsH: My issue with Violet's comments were the claims that this is not an answer. And given a choice between Violet's answer and mine, I'll argue that mine is safer with respect to their respective assumptions, though I agree that they both make assumptions. I have not evaluated the other answers on this question to know which of them also make assumptions. And if you want to avoid these sorts of assumptions, my assumption is that Spinner isn't a good choice of widget. :-)Skinnydip
I
399

The use of Runnables is completely incorrect.

Use setSelection(position, false); in the initial selection before setOnItemSelectedListener(listener)

This way you set your selection with no animation which causes the on item selected listener to be called. But the listener is null so nothing is run. Then your listener is assigned.

So follow this exact sequence:

Spinner s = (Spinner)Util.findViewById(view, R.id.sound, R.id.spinner);
s.setAdapter(adapter);
s.setSelection(position, false);
s.setOnItemSelectedListener(listener);
Insectile answered 27/6, 2013 at 7:19 Comment(24)
+1 Hidden gem! Passing false as "animate" parameter does not call the listener callback. Awesome!Lovmilla
+1 Weird but elegant solution :) Luckily, I already had to call setSelection anyway...Furie
The listener will still fire when the Spinner UI element is assembled, so it will fire regardless which doesn't prevent the unwanted behavior described by the OP. This works great if not declared during or before onCreateView(), but that's not what they asked for.Sexcentenary
Ohh thats the better solution without any hack !!Tracitracie
It solved exactly my problem. Thanks. @Rudi are you sure? I build my layout in onCreate and with this my toast doesnt fire, like it did before this line of code. I have toast fired when user selects the first spinner entry and before the toast always fired when starting the activity. Now with htis line the toast only fires when user selects the first entry, not when starting the activity.Mullet
Michal had same answer before.Richelle
Useful, but solves a different problem than OP presented. OP refers to a selection event that (unfortunately) automatically fires when the view first appears even though the programmer did not do setSelection.Pivoting
What should be the value of position ?Triatomic
position should be the value that comes from [your listener](developer.android.com/reference/android/widget/…?>, android.view.View, int, long))Insectile
How to do this while using ButterKnife? I'm stuck!Melamine
awesome answer...hence upvote, but you should update it to add information that 'position' will be already selected item's position in list. It took 5 minutes for myself also to understand what position should be.Soso
The "false" value parameter in setSelection(..) method was the solution for me. ty!Mineral
setSelection(position); doesn't work either,, you must use setSelection(position, false); to make it workTutty
"The use of Runnables is completely incorrect". I agree with that. This a way better optimal. Thanks!.Blenheim
This should be the accepted answer it is the most general one, using a boolean will not always work if you are initializing the spinner from different parts of the code with different expected resultsBroadminded
Easiest fix that i have found. But keep in mind that for any strange reason it won't work if -1 is assigned for position. Use Adapter.NO_SELECTION for default value.Cheery
For me this prevents the spinner from firing when selecting the first element. Other elements work fine.Ufa
followed the same sequence, still fires the click listener!Sephira
This does not work with databinding an adapter. It still fires the click listener even if I attached it after setting the selection. If I set the selection without attaching the onItemSelectedListener, nothing fires (as expected).Plasty
Amazing, this should be the accepted answer s.setSelection(position, false); Setting animate value to false does the trick. No hack! Clean solution.Neogene
Also s.setSelection(position, false); should be called after setting the adapter.Sophia
Everyone seems to be discussing several different things. I ended up here because OnItemSelected fires when you set the adapter for the spinner. That's what bugs me. I'm getting data async and populating the spinner after I get the data, and then the view fires OnItemSelected. Well an item was NOT selected. This is a bug IMHO.Henig
@Henig same issue - I feel your pain. Frankly, I think it's absolutely ridiculous that this "bug" even exists. Who would think to invoke the listener on initialization? Does anyone know in what context would that be beneficial for?Domenech
Why can't the web search point to this solution directly! Thank you so much for finding the correct way and not the hacky ways that made me think 'Android APIs cant be this dumb'.Voe
E
208

Referring to the answer of Dan Dyer, try to register the OnSelectListener in a post(Runnable) method:

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

By doing that for me the wished behavior finally occurred.

In this case it also means that the listener only fires on a changed item.

Excommunicative answered 21/2, 2012 at 10:9 Comment(8)
I get an error saying: The method setOnItemSelectedListener(AdapterView.OnItemSelectedListener) in the type AdapterView<SpinnerAdapter> is not applicable for the arguments (new Runnable(){}) why is that?Totten
Isn't this essentially setting up a race condition between the Runnable and the UI Thread?Tema
@theFunkyEngineer - This code should be run from one of the main thread methods e.g. onCreate(), onResume() etc. In that case, its a fantastic trick, with no danger of race condition. I normally use this trick in onCreate() just after the layout code.Epithelium
@praha -- Works fine on CM11Baronial
This a great solution and definitely not a hack! This sort of functionality is how things are done deep in the framework. It's a shame Spinner doesn't do this internally. However, this the cleanest way to have some code guaranteed to run after Activity creation. This works because the listener is not set yet on the Spinner when the Activity tries to notify them.Snowdrop
Little note: If you are using a spinner in a ListView (RecyclerView) don't forget to call spinner.setOnItemSelectedListener(null) before resetting the adapter in your viewHolder to avoid calling the "old" listener.Cutlip
This is an acceptable solution. not a blind shot. other solution are more prone to behaviour change problem in future.Rase
This worked for me. The onItemSelectedListneer was first initlized before setting the adapter. When the adapter was set the listerner fired. But initilizing the listener with the Spinner.post method, caused the listener to not fire when setting the adapter. Thanks!Berchtesgaden
S
82

I would have expected your solution to work -- I though the selection event would not fire if you set the adapter before setting up the listener.

That being said, a simple boolean flag would allow you to detect the rogue first selection event and ignore it.

Skinnydip answered 1/4, 2010 at 17:53 Comment(17)
ugh, yeah. Thats what I meant by an inelegant solution. Seems like there must be a better way. Thank you though.Intendant
This thread on the Dev ml has more insight about this: groups.google.com/group/android-developers/browse_thread/thread/… - Unfortunately no solution is given...Mustard
The process of laying out the components fires the selection listener. You'd therefore have to add the listener after the layout has been done. I have been unable to find a suitable, straightforward place to do this as the layout seems to happen at some point after onResume() and onPostResume(), so all of the normal hooks have completed by the time the layout happens.Hohenzollern
I would stay away from this boolean flag - as if behavior changes in the future it could cause a bug. A more bullet-proof solution would be to keep a variable with the "current selected index", initialized to the first item selected. Then on selection event - check if it equals the new position - return and do nothing. Of course update the variable on selection.Atli
This does not work. Answer by @casanova works. That should be the accepted answer.Sessler
It is correct but you can do one more thing. Set the adapter. include mSpinner.setSelection(0, false); and set up the listener. I hope it works.Lit
you gotta have one flag for each spinner wich is not usefull. Besides, if your spinner is in fragment, you leave your app and ramBooster cleans up, then you return to app and your spinner will fire up twiceSebbie
This does not provide an answer to this highly popular question. It should be a comment, not an answer, and especially not the accepted answer. The real working reliable answer is provided by karooolek: https://mcmap.net/q/80527/-how-to-keep-onitemselected-from-firing-off-on-a-newly-instantiated-spinnerCelestinecelestite
@VioletGiraffe: "This does not provide an answer to this highly popular question" -- sure it does: "a simple boolean flag would allow you to detect the rogue first selection event and ignore it". You may not like that answer. Similarly, I do not like the answer that you highlighted, as it makes unfounded assumptions about the order and timing of events, assumptions that may not hold up over Android versions and manufacturer tweaks to Android. However, I would never claim that your chosen answer is not an answer. It's an answer, just one that I disagree with.Skinnydip
@CommonsWare: I understand, you have a point. I've made a mistake of treating the question as being more broad than it actually is: I need to prevent the OnItemSelected callback every time it's fired off in response to setSelection, and this question only talks about the first time that's during the initial UI layout process. For this particular purpose, your solution is better. The one I pointed is more universal, but possibly unreliable in the future, although I expect it to last very long. However, a hint at an answer is still not really an answer if you ask me.Celestinecelestite
Is it guaranteed that there will always be one and only one "rogue first selection event"? If so, how do we know that? It doesn't seem clear from the documentation. Without that guarantee, it seems that this solution also makes unfounded assumptions that may not hold up over Android versions and manufacturer tweaks to Android.Iniquitous
@LarsH: By that argument, you have no guarantees of getting any selection events. Or you could get 2,000 callbacks per selection. I readily agree that my solution is simplistic. It's also over nine years old and comes from a time when we could count the number of Android device models on one hand, with fingers left over. IMHO, Spinner is not a particularly well-designed widget with respect to this particular issue. If you need control over the implementation, look into third-party replacements.Skinnydip
"By that argument, you have no guarantees of getting any selection events." How so? The documentation says that setOnItemSelectedListener() will "Register a callback to be invoked when an item in this AdapterView has been selected." developer.android.com/guide/topics/ui/controls/spinner?hl=en also says "When the user selects an item from the drop-down, the Spinner object receives an on-item-selected event."Iniquitous
@LarsH: Part of your concern is with manufacturer tweaks. Manufacturers are not legally obligated to honor the documentation. So long as they pass the CTS and comply with the CDD, they can change what they want. That could include breaking expectations around Spinner. I would not expect them to break it, just as I would not expect them to break the assumption that my answer makes. But, stuff happens.Skinnydip
Sure, mfrs can tweak things. But assuming that OS implementers will honor documented behavior is a much safer assumption than assuming they will continue to honor undocumented behavior. In other words, it feels like you're drawing a false equivalence.Iniquitous
I don't have a problem with your answer per se; sometimes the best available course is to make do with relying on undocumented behavior that seems to happen consistently. I just don't understand why you say you don't like the solution Violet pointed to (relative to your own) because it makes unfounded assumptions, when yours does also. I say that because you've earned my respect for your Android knowledge and competence.Iniquitous
@LarsH: My issue with Violet's comments were the claims that this is not an answer. And given a choice between Violet's answer and mine, I'll argue that mine is safer with respect to their respective assumptions, though I agree that they both make assumptions. I have not evaluated the other answers on this question to know which of them also make assumptions. And if you want to avoid these sorts of assumptions, my assumption is that Spinner isn't a good choice of widget. :-)Skinnydip
W
55

I created a small utility method for changing Spinner selection without notifying the user:

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

It disables the listener, changes the selection, and re-enables the listener after that.

The trick is that calls are asynchronous to the UI thread, so you have to do it in consecutive handler posts.

Wu answered 23/11, 2012 at 11:51 Comment(2)
Awesome. I had multiple spinners and tried setting all of their listeners to null before setting their values, then I set all of them back to what they were supposed to be, but for some reason that didn't work. tried this function instead and it worked. I don't know why mine didn't work, but this works so i don't care :DSuppurative
Of note: if you call setSpinnerSelectionWithoutCallingListener twice rapidly, so that the second call is made while the first one has already set the listener to null, your spinner will be stuck with a null listener forever. I propose the following fix: add if (listener == null) return; after spinner.setSelection(selection).Celestinecelestite
I
35

Unfortunately it seems that the two most commonly suggested solutions to this issue, namely counting callback occurrences and posting a Runnable to set the callback at a later time can both fail when for example accessibility options are enabled. Here's a helper class that works around these issues. Further explenation is in the comment block.

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(); }
}
Inocenciainoculable answered 15/6, 2013 at 15:40 Comment(2)
This should be the highest voted answer. It's simple yet brilliant. It allows you to keep all your current implementation the same, except the one line where you initialize. Definitely made retro-fitting older projects quite easy. On top of that, I killed two birds with one stone by implementing the OnTouchLisener interface to close keyboard when spinner opens. Now all my spinners behave exactly the way I want.Materse
Beautiful answer. It's still triggering to the 0th element when I addAll() to the adapter but my 0th element is an ellipsis for neutral (do nothing) behavior.Rutkowski
B
32

I have had LOTS of issues with the spinner firing of when I didn't want to, and all the answers here are unreliable. They work - but only sometimes. You will eventually run into scenarios where they will fail and introduce bugs into your code.

What worked for me was to store the last selected index in a variable and evaluate it in the listener. If it is the same as the new selected index do nothing and return, else continue with the listener. Do this:

//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

}

Trust me when I say this, this is by far the most reliable solution. A hack, but it works!

Brigitte answered 5/8, 2014 at 15:39 Comment(6)
Will this even work if you are trying to change the value? In my case I am trying to set the value to something like 3 when it is actually 0 without triggering the change listeners. Are you saying that int i only returns a different value if the user is selecting it?Suppurative
Hi JStephen, I'm not 100% sure what you mean. But int i will be the position of the spinner whenever onItemSelected is triggered. The problem is that onItemSelected is triggered whenever the spinner is first loaded, without any actual user interaction, leading to unwanted behaviour in this case. int i will be equal to 0 at this initial point as this is the default starting index when the spinner is first loaded. So my solution checks to make sure that an actual different item is selected instead of the currently selected item being reselected... Does this answer your question?Brigitte
Hi Chris, I have a page that pulls information from the database for the user to edit. when the page opens up i populate the spinners, and then set their positions to the values that were in the database. So if i set their position to 3 for example, this causes the onItemSelected to trigger with i set to 3 which is different from the initial. I was thinking you were saying i is only set if the user actually changed it themselves.Suppurative
What if the user selects position 0? They'll be ignored.Prevot
I dont think the last position way is good idea. I init spinners by loading position from SharedPreferences and using setSelection. Very often values in SharedPrefs are not the same as default values when the spinners are created, so the onItemSelected will be triggered at initiation.Whenas
If you always start the UI with the spinner position at zero, this is the simpler solution. It also works with the double call of onItemSelected I verified recently, every time you change the selection in the spinnerSheba
R
27

Just to flesh out hints at using the onTouchListener to distinguish between automatic calls to the setOnItemSelectedListener (which are part of Activity initialization, etc.) vs. calls to it triggered by actual user interaction, I did the following after trying some other suggestions here and found that it worked well with the fewest lines of code.

Just set an Boolean field for your Activity/Fragment like:

private Boolean spinnerTouched = false;

Then just before you set your spinner's setOnItemSelectedListener, set an 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;
Renn answered 6/2, 2016 at 14:53 Comment(5)
That works great, and since Android 6+, that's the only method that works. BUT, you also have to do the same with setOnKeyListener(), or it doesn't work when the user navigates with the keyboard.Seagraves
Works great, all other solutions has some issue with different phones.Secretory
This is simple and absolutely perfect! No extra nonsense needed, just keep the logic in mind. Im glad i scrolled all the way down till here!Fougere
Instead of setOnKeyListener(), you can subclass spinner and set flag spinnerTouched = true in overridden preformClick() method, which is called in both cases (touch/key). Rest is the same.Flita
Just wanted to mention this appears to resolve the same bug with DropDownPreferences I recently posted here: #61867618 I can't f*cking believe it tbh :DCalabro
R
26

I was in similar situation, and I have a simple solution working for me.

It seems like methods setSelection(int position) and setSelected(int position, boolean animate) have different internal implementation.

When you use the second method setSelected(int position, boolean animate) with false animate flag, you get the selection without firing onItemSelected listener.

Renick answered 7/12, 2012 at 12:5 Comment(5)
The better approach is to not worry about the extra calls to onItemSelected, but to make sure that it shows the right selection. So, calling spinner.setSelection(selectedIndex) before adding listener made it work consistently for me.Backdate
there is no setSelected(int position, boolean animate) method for spinnerSquinteyed
The actual call you need is setSelection(int position, boolean animate);Insectile
+1 for you. This solve a more general problem when code modify more times Spinner content & selection keeping onItemSelected only for user interactionDanford
sadly false animation flag still calls onItemSelected in API23Mott
A
13
spinner.setSelection(Adapter.NO_SELECTION, false);
Aec answered 21/6, 2014 at 21:5 Comment(1)
The code might speak for itself, but a little bit explanation goes a long way :)Achromatin
O
11

This will happen if you are making selection in code as;

   mSpinner.setSelection(0);

Instead of above statement use

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

Edit: This method doesn't work for Mi Android Version Mi UI.

Ootid answered 20/4, 2017 at 7:28 Comment(1)
This definitely solved the problem for me. I read the documentation about Spinner widget.. it's absolutely tricky to understand the difference: setSelection(int position, boolean animate) -> Jump directly to a specific item in the adapter data. setSelection(int position) -> Sets the currently selected item.Fanatic
A
8

After pulling my hair out for a long time now I've created my own Spinner class. I've added a method to it which disconnects and connects the listener appropriately.

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

Use it in your XML like this:

<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" />

All you have to do is retrieve the instance of SaneSpinner after inflation and call set selection like this:

mMySaneSpinner.setSelection(1, true, true);

With this, no event is fired and user interaction is not interrupted. This reduced my code complexity a lot. This should be included in stock Android since it really is a PITA.

Arrowworm answered 31/5, 2015 at 8:42 Comment(2)
This doesnt work for me, it still triggers onItemSelected.Whenas
Arthez please double check if you're really passing true to the third argument. If yes something else is wrong here. If possible post your code.Arrowworm
O
8

No unwanted events from the layout phase if you defer adding the listener till the layout is finished:

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

            });

        }
    });
Obaza answered 29/7, 2015 at 15:22 Comment(1)
This works, and IMO it is the cleanest solution to the OP's specific issue. I want to note that you can remove the ViewTreeObserver.OnGlobalLayoutListener on versions below J by calling ViewTreeObserver.removeGlobalOnLayoutListener, which is deprecated and has a similar name to the method this answer uses.Pleasing
F
6

I got a very simple answer , 100% sure it works:

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
}
Fluxmeter answered 17/8, 2016 at 7:17 Comment(0)
F
3

I've found much more elegant solution to this. It involves counting how many times the ArrayAdapter (in your case "adapter")has been invoked. Let's say you have 1 spinner and you call:

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

Declare an int counter after the onCreate and then inside onItemSelected() method put an "if" condition to check how many times the atapter has been called. In your case you have it called just once so:

if(iCountAdapterCalls < 1)
{
  iCountAdapterCalls++;
  //This section executes in onCreate, during the initialization
}
else
{
  //This section corresponds to user clicks, after the initialization
}
Fraught answered 8/2, 2013 at 8:22 Comment(0)
L
3

Since nothing worked for me, and I have more than 1 spinner in my view (and IMHO holding a bool map is an overkill) I use the tag to count the clicks :

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) {
        }
    });
Livery answered 24/8, 2015 at 17:39 Comment(0)
D
2

My small contribution is a variation on some of the above that has suited me a few times.

Declare an integer variable as a default value (or last used value saved in preferences). Use spinner.setSelection(myDefault) to set that value before the listener is registered. In the onItemSelected check whether the new spinner value equals the value you assigned before running any further code.

This has the added advantage of not running code if the user selects the same value again.

Dexterdexterity answered 21/6, 2013 at 11:9 Comment(0)
P
1

After having had the same problem, I came to this solutions using tags. The idea behind it is simple: Whenever the spinner is changed programatically, make sure the tag reflects the selected position. In the listener then you check if the selected position equals the tag. If it does, the spinner selection was changed programatically.

Below is my new "spinner proxy" class:

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.
}

You will also need an XML file with the tag setup in your Values directory. I named my file spinner_tag.xml, but that's up to you. It looks like this:

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

Now replace

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

in your code with

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

And make your handler somewhat look like this:

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

The function isUiTriggered() will return true if and only if the spinner has been changed by the user. Note that this function has a side effect - it will set the tag, so a second call in the same listener call will always return false.

This wrapper will also handle the problem with the listener being called during layout creation.

Have fun, Jens.

Prurigo answered 27/10, 2014 at 20:42 Comment(0)
H
1

Lots of answers already, here's mine.

I extend AppCompatSpinner and add a method pgmSetSelection(int pos) that allows programmatic selection setting without triggering a selection callback. I've coded this with RxJava so that the selection events are delivered via an 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);
    }
}

An example of its usage, called in onCreateView() in a Fragment for example:

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

where setSelection() is a method in the enclosing view that looks like this, and which is called both from user selection events via the Observable and also elsewhere programmatically, so the logic for handling selections is common to both selection methods.

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
}
Heroics answered 22/11, 2017 at 22:43 Comment(0)
P
0

I would try to call

spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());

after you call setAdapter(). Also try out calling before the adapter.

You always have the solution to go with subclassing, where you can wrap a boolean flag to your overriden setAdapter method to skip the event.

Peripatetic answered 1/4, 2010 at 18:26 Comment(0)
J
0

The solution with a boolean flag or a counter didn't help me, 'cause during orientation change onItemSelected() calls "overflew" the flag or the counter.

I subclassed android.widget.Spinner and made tiny additions. The relevant parts are below. This solution worked for me.

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;
}
Jolly answered 14/7, 2012 at 16:15 Comment(0)
B
0

This is not an elegant solution either. In fact it's rather Rube-Goldberg but it seems to work. I make sure the spinner has been used at least once by extending the array adapter and overriding its getDropDownView. In the new getDropDownView method I have a boolean flag that is set to show the dropdown menu has been used at least once. I ignore calls to the listener until the flag is set.

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

overriden array adapter:

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

modified listener:

@Override
public boolean onNavigationItemSelected(
   int itemPosition, long itemId) {
   if (viewed) {
     ...
   }
   return false;
}
Baelbeer answered 17/3, 2015 at 21:55 Comment(0)
A
0

if you need to recreate activity on the fly eg: changing themes , a simple flag/counter wont work

use onUserInteraction() function to detect user activity,

reference : https://mcmap.net/q/81768/-spinner-onitemselected-called-erroneously-without-user-action

Aarau answered 10/4, 2015 at 9:18 Comment(0)
U
0

I have done with simplest way:

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

        }
    });

Done

Ulrick answered 13/4, 2015 at 9:45 Comment(1)
Its an interesting solution. Could use more explanation. Basically its intentionally ignoring the first onItemSelected event. They may work well in some cases but not others such as when accessibility options are enabled (see Jorrit's explanation).Adapt
O
0
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) {

     }
});
Osmious answered 13/9, 2015 at 14:39 Comment(0)
H
0

That's my final and easy to use solution :

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

Use the default setSelection(...) for default behaviour or use setSelectionWithoutInformListener(...) for selecting an item in the spinner without triggering OnItemSelectedListener callback.

Hankhanke answered 20/12, 2016 at 12:12 Comment(0)
E
0

I need to use mSpinner in ViewHolder, so the flag mOldPosition is set in the anonymous inner class.

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
            }
        });
Emmaemmalee answered 9/3, 2017 at 8:49 Comment(0)
S
0

I would store the initial index during creation of the onClickListener object.

   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) {
      }
  });
Sweeting answered 29/8, 2017 at 8:6 Comment(0)
B
0

My solution uses onTouchListener but doesn't restricts from its use. It creates a wrapper for onTouchListener if necessary 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;
    }
}
Bribe answered 22/9, 2017 at 10:5 Comment(0)
D
0

I might be answering too late over the post, however I managed to achieve this using Android Data binding library Android Databinding . I created a custom binding to make sure listener is not called until selected item is changed so even if user is selecting same position over and over again event is not fired.

Layout xml file

    <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 is where you are passing position to be selected.

Custom binding

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

        }
    });
}

You can read more about custom data binding here Android Custom Setter

NOTE

  1. Don't forget to enable databinding in your Gradle file

       android {
     ....
     dataBinding {
     enabled = true
    }
    }
    
  2. Include your layout files in <layout> tags

Dagley answered 18/10, 2017 at 6:6 Comment(0)
G
-1
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) {
            }
        });
Gunner answered 26/12, 2014 at 20:25 Comment(1)
1) Please properly format your code. 2) An explanation on what your code is doing would also be appreciated. Not all code snippets are understood immediately when reading the code.Crespo
F
-1

It's not a particularly elegant solution, but I find it works reliably. Simply set the listeners on a delayed thread from inside onResume. You're then free to do any initialisation you like, and the UI will then make any layout changes, and then set the listeners.

Thread t = new Thread(){
            public void run() {
                try{                
                    Thread.sleep(1000);
                    getActivity().runOnUiThread(new Runnable() {                        
                        @Override
                        public void run() {
                            setSpinnerListeners();

                        }
                    });
                }
                catch(Exception e){
                    e.printStackTrace();
                }
            };
        };
        t.start();
Fayefayette answered 8/7, 2015 at 17:56 Comment(1)
why downvote? this works. the downvote should be done to the android support framework :-DLynelllynelle
D
-1

Design a spinner for common use, just data input and get user's choosen, advantages :

1. Keep spinner style the same for app.
2. Start spinner anywhere.
3. Easy to handle linked spinner(start ReuseSpinner again with different data).

My demo example : ReuseSpinner
pass data to ReuseSpinner :

Intent intent = new Intent(MainActivity.this, SpinnerActivity.class);
intent.putExtra(SpinnerActivity.Extra_Resource, arrayList);
startActivityForResult(intent, mRequestCode_select_country_prompt);

get user's choosen :

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode == mRequestCode_select_country && resultCode == RESULT_OK){
            if(data != null){
                Map.Entry<String,String> entry = (Map.Entry<String,String>) data.getSerializableExtra(SpinnerActivity.Result_Data);
                if(entry != null){
                    Log.i(TAG, String.format("get result -> key:%s , value:%s", entry.getKey(), entry.getValue()));

                }
            }
        }
    }
Dwelling answered 4/8, 2016 at 9:50 Comment(0)
C
-1

Not a prefect solution but if your happy with having the starting string as just a place holder you can add place holder spring value ("Day_of_Work_Out")

  @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            String name = (String) parent.getItemAtPosition(position);
            if (name.equals("Day_of_Work_Out")) {

            }else {

                workOutD = name;
                Intent intent = new Intent();
                workOutNam = workOutName.getText().toString();

                if (workOutNam == null) {

                    startActivity(intent);
                    Log.i("NewWorkOutActivity","Name is null");

                }else {
                    Log.i("NewWorkOutActivity","Name Not null");
                    Toast.makeText(NewWorkOutActivity.this, "Please Select a Day", Toast.LENGTH_LONG).show();
                }


            }
        }
Carissacarita answered 26/4, 2017 at 22:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.