The OnItemSelectedListener event handler gets called both when a spinner
selection is changed programmatically, and when a user physically clicks the spinner control.
Is is possible to determine if an event was triggered by a user
selection somehow?
Or is there another way to handle spinner user selections?
To workaround you need to remember the last selected position. Then inside of your spinner listener compare the last selected position with the new one. If they are different, then process the event and also update the last selected position with new position value, else just skip the event processing.
If somewhere within the code you are going to programatically change spinner selected position and you don't want the listener to process the event, then just reset the last selected position to the one you're going to set.
Yes, Spinner in Android is painful. I'd even say pain starts from its name - "Spinner". Isn't it a bit misleading? :) As far as we're talking about it you should also be aware there's a bug - Spinner may not restore (not always) its state (on device rotation), so make sure you handle Spinner's state manually.
Hard to believe that a year and a half later, the problem still exists and continues to boggle people...
Thought I'd share the workaround I came up with after reading Arhimed's most useful post (thanks, and I agree about spinners being painful!). What I've been doing to avoid these false positives is to use a simple wrapper class:
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
public class OnItemSelectedListenerWrapper implements OnItemSelectedListener {
private int lastPosition;
private OnItemSelectedListener listener;
public OnItemSelectedListenerWrapper(OnItemSelectedListener aListener) {
lastPosition = 0;
listener = aListener;
}
#Override
public void onItemSelected(AdapterView<?> aParentView, View aView, int aPosition, long anId) {
if (lastPosition == aPosition) {
Log.d(getClass().getName(), "Ignoring onItemSelected for same position: " + aPosition);
} else {
Log.d(getClass().getName(), "Passing on onItemSelected for different position: " + aPosition);
listener.onItemSelected(aParentView, aView, aPosition, anId);
}
lastPosition = aPosition;
}
#Override
public void onNothingSelected(AdapterView<?> aParentView) {
listener.onNothingSelected(aParentView);
}
}
All it does is trap item selected events for the same position that was already selected (e.g. the initial automatically triggered selection for position 0), and pass on other events to the wrapped listener. To use it, all you have to do is modify the line in your code that calls the listener to include the wrapper (and add the closing bracket of course), so instead of, say:
mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
...
});
you'd have this:
mySpinner.setOnItemSelectedListener(new OnItemSelectedListenerWrapper(new OnItemSelectedListener() {
...
}));
Obviously once you've tested it, you could get rid of the Log calls, and you could add the ability to reset the last position if required (you'd have to keep a reference to the instance, of course, rather than declaring on-the-fly) as Arhimed said.
Hope this can help someone from being driven crazy by this strange behaviour ;-)
In the past I've done things like this to distinguish
internal++; // 'internal' is an integer field initialized to 0
textBox.setValue("...."); // listener should not act on this internal setting
internal--;
Then in textBox's listener
if (internal == 0) {
// ... Act on user change action
}
I use ++ and -- rather than setting a boolean value to 'true' so that there is no worry when methods nest other methods that might also set the internal change indicator.
I had this situation lately when using spinners and the internet didn't came up with a suitable solution.
My application scenario:
X spinners (dynamically, 2 for each cpu, min & max) for setting & viewing the CPU-Frequency. They are filled when the application starts and they also get the current max/min freq of the cpu set. A thread runs in the background and checks for changes every second and updates the spinners accordingly. If a new frequency inside the spinner is set by the user the new frequency is set.
The issue was that the thread accessed setSelection to update the current frequency which in turn called my listener and I had no way of knowing if it was the user or the thread that changed the value. If it was the thread I didn't want the listener to be called since there would have been no need to change the frequency.
I came up with a solution that suits my needs perfectly and works around the listener on your call :) (and I think this solution gives you maximal control)
I extended Spinner:
import android.content.Context;
import android.widget.Spinner;
public class MySpinner extends Spinner {
private boolean call_listener = true;
public MySpinner(Context context) {
super(context);
}
public boolean getCallListener() {
return call_listener;
}
public void setCallListener(boolean b) {
call_listener = b;
}
#Override
public void setSelection(int position, boolean lswitch) {
super.setSelection(position);
call_listener = lswitch;
}
}
and created my own OnItemSelectedListener:
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
public class SpinnerOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
MySpinner spin = (MySpinner) parent.findViewById(parent.getId());
if (!spin.getCallListener()) {
Log.w("yourapptaghere", "Machine call!");
spin.setCallListener(true);
} else {
Log.w("yourapptaghere", "UserCall!");
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
}
If you now create a MySpinner you can use this to set the selection:
setSelection(position, callListener);
Where callListener is either true or false. True will call the listener and is default, which is why user interactions are getting identified, false will also call the listener but uses code you want for this special case, exempli gratia in my case: Nothing.
I hope that someone else finds this useful and is spared a long journey to look if something like this already exists :)
I also looked for a good solution on the internet but didn't find any that satisfied my needs.
So I've written this extension on the Spinner class so you can set a simple OnItemClickListener, which has the same behaviour as a ListView.
Only when an item gets 'selected', the onItemClickListener is called.
Have fun with it!
public class MySpinner extends Spinner
{
private OnItemClickListener onItemClickListener;
public MySpinner(Context context)
{
super(context);
}
public MySpinner(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public MySpinner(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override
public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener inOnItemClickListener)
{
this.onItemClickListener = inOnItemClickListener;
}
#Override
public void onClick(DialogInterface dialog, int which)
{
super.onClick(dialog, which);
if (this.onItemClickListener != null)
{
this.onItemClickListener.onItemClick(this, this.getSelectedView(), which, this.getSelectedItemId());
}
}
}
Just to expand on aaamos's post above, since I don't have the 50 rep points to comment, I am creating a new answer here.
Basically, his code works for the case when the initial Spinner selection is 0. But to generalize it, I amended his code the following way:
#Override
public void setOnItemSelectedListener(final OnItemSelectedListener listener)
{
if (listener != null)
super.setOnItemSelectedListener(new OnItemSelectedListener()
{
private static final int NO_POSITION = -1;
private int lastPosition = NO_POSITION;
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
if ((lastPosition != NO_POSITION) && (lastPosition != position))
listener.onItemSelected(parent, view, position, id);
lastPosition = position;
}
#Override
public void onNothingSelected(AdapterView<?> parent)
{
listener.onNothingSelected(parent);
}
});
else
super.setOnItemSelectedListener(null);
}
Basically, this code will ignore the very first firing of onItemSelected(), and then all subsequent "same position" calls.
Of course, the requirement here is that the selection is set programatically, but that should be the case anyhow if the default position is not 0.
I did some logging and discovered that it only ever gets called on initialize, which is annoying. Can't see the need for all this code, I just created an instance variable that was initialised to a guard value, and then set it after the method had been called the first time.
I logged when the onItemSelected method was being called it was otherwise only being called once.
I had a problem where it was creating two of something and realised it was because I was calling add() on my custom adapter, which already had a reference to the list I was referencing and had added to outside the adapter. After I realised this and removed the add method the problem went away.
Are you guys sure you need all this code?
I know this is pretty late, but I came up with a very simple solution to this. It is based on Arhimed's answer, it's exactly the same. It's very easy to implement too. Refer the accepted answer:
Undesired onItemSelected calls
Related
I have 36 spinners that I have initialized with some values. I have used onItemSelectedListener with them. As usual, the user can interact with these spinners, firing the onItemSeected function.
One problem is that the call is made during init, but I found solutions to it here and avoided that using a global variable "count" and checking if count > 36 before executing code inside onItemSelected.
My problem is this:
The user has the option to click on a button called "Previous", upon which I have to reset SOME of the spinner values.
I tried changing the value of count to 0 before resetting the spinners, and then changing it back to 37 after resetting, but I have come to understand that the onItemSelected is called only after every other function is done executing, so it is called AFTER count is changed back to 37 even though the spinner values are set as soon as they are selected by user.
I need to repeatedly refresh some spinners WITHOUT firing off the onItemSelected function. Can anyone please help me find a solution? Thanks.
I found a simple and, I think, elegant solution.
Using tags.
I first created a new XML file called 'tags' and put in the following code:
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item name="pos" type="id" />
</resources>
Whenever I myself use spin.setSelection(pos), I also do spin.setTag(R.id.pos, pos), so I am setting the current position as a tag.
Then, in onItemSelected, I am executing code only if(spin.getTag(R.id.pos) != position), where position is the position variable supplied by the function.
In this way, my code is executed only when the user is making a selection.
Since the user has made a selection, the tag has not been updated, so after the processing is done, I update the tag as spin.setTag(R.id.pos, position).
NOTE: It is important to use the same adapter throughout, or the "position" variable might point to different elements.
EDIT: As kaciula pointed out, if you're not using multiple tags, you can use the simpler version, that is spin.setTag(pos) and spin.getTag() WITHOUT the need for an XML file.
When Spinner.setSelection(position) is used, it always activates setOnItemSelectedListener()
To avoid firing the code twice I use this solution:
private Boolean mIsSpinnerFirstCall = true;
...
Spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//If a new value is selected (avoid activating on setSelection())
if(!mIsSpinnerFirstCall) {
// Your code goes gere
}
mIsSpinnerFirstCall = false;
}
public void onNothingSelected(AdapterView<?> arg0) {
}
});
I don't know if this solution is as foolproof as the chosen one here, but it works well for me and seems even simpler:
boolean executeOnItemSelected = false;
spinner.setSelection(pos)
And then in the OnItemSelectedListener
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if(executeOnItemSelected){
//Perform desired action
} else {
executeOnItemSelected = true;
}
}
The way I solved this is by saving the OnItemSelectedListener first. Then set the OnItemSelectedListener of the Spinner to the null value. After setting the item in the Spinner by code, restore the OnItemSelectedListener again. This worked for me.
See code below:
// disable the onItemClickListener before changing the selection by code. Set it back again afterwards
AdapterView.OnItemSelectedListener onItemSelectedListener = historyPeriodSpinner.getOnItemSelectedListener();
historyPeriodSpinner.setOnItemSelectedListener(null);
historyPeriodSpinner.setSelection(0);
historyPeriodSpinner.setOnItemSelectedListener(onItemSelectedListener);
Here's my solution to this problem. 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
}
I've a Spinner with onItemSelected interation that works, but how the Api specification says:
This callback is invoked only when the newly selected position is
different from the previously selected position or if there was no
selected item.
I need to remove this limitation and i want that the callback is invoked also if the user select the same element.
How to do that? I read a suggestion about extending Spinner class and set the position to INVALID_POSITION, but i've not understood/able to do that.
Anyone did the same thing?
I also needed a solution to this problem. What I wanted to do was have a Spinner that has date ranges with a custom range option. The rows would look something like this:
Apr 10 - May 10
Mar 10 - Apr 10
Feb 10 - Mar 10
Custom Range
The problem is that if the user selects a custom range and then wants to change their custom range, they have to select a different range and then select the custom range option again. I wanted the user to just be able to select "Custom Range" again so that the custom range dialog could be shown again.
I sub-classed Spinner and created my own listener. The code switches the selection, but then immediately switches it so that nothing is selected. In my listener I just ignore any position that is less than zero.
The Spinner just displays the last selected item. I created my own custom adapter and specify what to display for each view, but that shouldn't be necessary. Here is how I sub-classed Spinner.
package com.example.widget;
import android.content.Context;
import android.widget.Spinner;
public class DateRangeSpinner extends Spinner {
private ItemSelectionListener listener;
public DateRangeSpinner(Context context) {
super(context);
}
/**
* This listener will be fired every time an item is selected,
* regardless of whether it has already been selected or not.
*
* #param l
*/
public void setOnItemSelectedListener(ItemSelectionListener l) {
listener = l;
}
public void removeOnItemSelectedListener() {
listener = null;
}
#Override
public void setSelection(int position) {
setSelection(position, true);
}
#Override
public void setSelection(int position, boolean animate) {
if (listener != null) {
listener.onItemSelected(position);
}
super.setSelection(position, animate);
super.setSelection(-1, animate);
}
public interface ItemSelectionListener {
public void onItemSelected(int position);
}
}
I hope this helps!
You can do this by a custom adapter, like create a layout of your desire views, then inflate this in custom adapter then on onItemClick function you can get the view by this function.
To distinguish each view you must to set the tag of each row.
It probably works in your condition.
Let me know any issue if you have
why you have select the selected item again. Just give a refresh button if you want to perform that task again.
This is the spinner Element with custom dialog mode and whitout promt:
<Spinner
android:id="#+id/spinner_metatag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="#array/search_adv"
/>
The Array element where the default value is putted at position 0:
<string-array name="search_adv">
<item>#string/search_adv_prompt</item>
<item>#string/search_adv_title</item>
<item>#string/search_adv_desc</item>
<item>#string/search_adv_autore</item>
....
</string-array>
The String elements for the array with the default value:
<string name="search_adv_prompt">Scegli un metatag</string> <!-- Default value-->
<string name="search_adv_title">Titolo</string>
<string name="search_adv_desc">Descrizione</string>
<string name="search_adv_autore">Autore</string>
...
And here the code to prevent the event fired on onCreateMethod and the work around to permit to select the same element already selected:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_adv_main);
spinner = (Spinner) findViewById(R.id.spinner_metatag);
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
//prevent onCreate event fire and the loop
if(pos==0)
return;
//HERE YOUR CODE
//at the end move to the default element the spinner
spinner.setSelection(0);
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {}
});
}
Hope helps. The idea come from the second solution from TreKing's answer
Use OnItemClickListener instead of on itemSelectedListener.That will work for every click whether it is same or different.
I guess you must be storing the value in a variable, initialize the vairable with -1. And change the value as the user selects the item spinner, if the value is -1 ask the user to reselect or whatever u want.
#i want that the callback is invoked also if the user select the same element.
Android will do it for u as this is the default behavior of android.
i have created two spinners both of different class.i want to set content of one spinner based on selected value of other spinner which is having in other class.can any one help me.thanks in advance.
There is a solution,you might feel it lengthy to implement but it would perfectly work for you,i think!
Lets say you have spinner_one in your ActivityOne.class and spinner_two in your ActivityTwo.class. And you want to populate spinner_2 based on what is selected in spinner_1.
Then,
spinner_1.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
// save your selected item into a SharedPreference Variable
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
Now,for spinner_2:
String spinner1_value=get value from SharedPreference;
if(spinner1_value.equals("something_1"))
{
//populate spinner_2 accordingly
}
else if(spinner1_value.equals("something_2"))
{
//populate spinner_2 accordingly
}
else
{
//populate spinner_2 accordingly
}
And don't forget to put this code to populate spinner_2 in onResume() of your ActivityTwo.class,so that it will reflect the changes user made in spinner_1 value.
This will allow you to change spinner_2 content according to spinner_1 value as and when user changes it.
The short answer is that you can do this programatically - either by assigning a new ArrayAdapter to the Spinner, or by removing/adding items from the existing ArrayAdapter.
The longer answer is that you have to be very careful in relying on OnItemSelectedListener.onItemSelected(), since it will not get called if the old selection and a new selection happen to be in the same position, and Spinner.getSelectedItemPosition(), since it can actually return positions greater than the number of items in the ArrayAdapter if you're removing items on the fly.
I have a system of three cascaded Spinners, which in turn can drive the contents of other Buttons and Texts. I was able to get 95% solutions until I recognized the above and changed my view of what was authoritative and what was not - by making the Spinner subservient to the logic that determines content, rather than the other way around. So rather than calling setSelection() and relying on the onItemSelected() callback, do all your cascading logic outside the handler, and only then call setSelection() and return back up the chain.
Adapted from my working code:
class Spinster extends Activity {
...
private void setSpinnerOne( int pos ) {
// 1. Do our own chores, etc
doSomeStuff();
mSomeText.setText( "Blah!" );
mSomeButton.setEnabled( some_condition );
// 2. Populate dependent Spinner based on pos
populateSpinnerTwoAdapter( pos );
// 3. Cascade by calling SpinnerTwo logic (not the Spinner itself)
lSpinnerTwoPos = someNiceFunction();
setSpinnerTwo( lSpinnerTwoPos );
// 4. Now set SpinnerOne
mSpinnerTwo.setSelection( pos );
}
private void setSpinnerTwo( int pos ) {
// Follows the same pattern as setSpinnerOne(), but cascades to SpinnerThree
}
private void setSpinnerThree( int pos ) {
// Follows the same pattern as setSpinnerOne(), but need not cascade
}
...
private OnItemSelectedListener item_select = new OnItemSelectedListener() {
public void onItemSelected( AdapterView parent, View v, int position, long id )
{
...
int lId = parent.getId();
if ( lId == R.id.spinner_one ) {
setSpinnerOne( position );
} else if ( lId == R.id.spinner_two ) {
setSpinnerTwo( position );
} else if ( lId == R.id.spinner_three ) {
setSpinnerThree( position );
}
}
}
}
I've left out a small detail here, that of setting and clearing a guard variable in the setSpinner*() methods so that they don't bother doing all their work again when onSelectedItemListener() calls them back. I think the basic logic is easier to show by leaving it out. (And they don't strictly need to be there - onItemSelected() will not be called the second time round.)
I could post a real, working example if this is deemed to abstract.
It appears that android's Spinner class (and possibly ListView in general, although I don't know for sure) calls your OnItemSelectedListener's onItemSelected() method after you call setAdapter(), even if the user hasn't explicitly selected anything yet.
I can see how this would be useful in many situations, but there are times when I only want onItemSelected() to be called when an item is actually specifically selected.
Is there a way to control this behaviour and have Spinner NOT call onItemSelected() after setting the adapter?
I haven't used this solution for very long yet so I'm not totally confident that it works as expected, but I've had luck so far with this workaround:
spinner.setOnItemSelectedListener( new OnItemSelectedListener() {
protected Adapter initializedAdapter = null;
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// Always ignore the initial selection performed after setAdapter
if( initializedAdapter !=parent.getAdapter() ) {
initializedAdapter = parent.getAdapter();
return;
}
...
}
}
Is there a better way?
Add listener to spinner like below:
spinner.post(new Runnable(){
public void run()
{
spinner.setOnItemSelectedListener( new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
...
}
}
}
});
I've used the setTag and getTag methods, and created a resource id called "spinnerstate".
Then whenever I programmatically set the adapter, I set the "spinnerstate" tag to "init", and in the fired event, set it to "ready" and ignore the event. (note my code is Mono for Android se it will look different):
Set Adapter:
profileSpn.SetTag (Resource.Id.spinnerstate, "init");
profileSpn.Adapter = new ArrayAdapter (this, Android.Resource.Layout.SimpleSpinnerItem, items.ToArray ());
Item Selected event:
string state = (string)((Spinner)sender).GetTag (Resource.Id.spinnerstate);
if (state == "init") {
((Spinner)sender).SetTag (Resource.Id.spinnerstate, "ready");
return;
}
I agree that this is not desired behaviour in almost 100% of cases, and I don't think it's good design on the part of Google, but there you go.
I did similar things before, I used count value. Using parent adapter object is incomplete because it can be a problem when view is refreshed or getView() called again.
Therefore, I recommend that using array of counter.
At first, define array of count in adapter globally.
private int isInitializedView[];
And then initialize it on getView().
isInitializedView[position] = 0;
In the selection listener, do something that you want if it already initialized.
holder.mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
isInitializedView[position]++;
if(isInitializedView[position] > 1) {
// do someting that you want
}
}
#Override
public void onNothingSelected(AdapterView<?> parentView) {}
});
(Note that isInitializedView[position]++; can be come after if() routine, and only trigger event when this value is >0 . It's your choice.)
I had three spinner in my activity and all spinner adapter data has been filled at runtime(from web-service data after call from onCreate method). So it automatically call onItemSelected(AdapterView<?> parent, View view, int position, long id) method of spinner.
I solved this issue by using onUserInteraction() method of activity
check this method that user is interacting with spinner or not. if yes then perform the action else not
Declare isUserIntract boolean variable globally
in onItemSelected method use following procedure
If(isUserIntract)
{
//perform Action
}
else{
//not perform action
}
use below code in activity
#Override
public void onUserInteraction() {
super.onUserInteraction();
isUserIntract = true;
}
I'm creating a spinner and I've added an OnItemSelectedListener to it.
However I've noticed that it fires on create.
Now I was wondering if there was a way to ignore/discard it.
I know I could use a boolean value, but that's a bit "dirty".
Here is my solution.
I need to ignore the first item selection event because there is a dependency between the Route Grade Spinner and the Route Checkbox.
And all my controls are setup based on a previous visit to the activity.
// Used to count the number of times the onItemSelected gets fired
private int mGradeSelectionCount = 0;
private void attachHandlers() {
OnItemSelectedListener gradeRangeSelectionMadeListener;
gradeRangeSelectionMadeListener = new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapter, View view, int position, long id) {
// If the counter is 0 then we can assume that it is android firing the event
if (mGradeSelectionCount++ < 1) {
return;
}
if (mCmbGradeFrom.getSelectedItemPosition() == 0) {
// Uncheck the Route checkbox
mChkTypeRoute.setChecked(false);
} else {
// Check the Route checkbox
mChkTypeRoute.setChecked(true);
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// Dont care, keep the same values as before
}
};
mCmbGradeFrom.setOnItemSelectedListener(gradeRangeSelectionMadeListener);
mChkTypeRoute.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (!isChecked) {
mCmbGradeFrom.setSelection(0);
mCmbGradeTo.setSelection(0);
}
}
});
}
This may help you.
#Override
public void onItemSelected( AdapterView<?> parent, View view, int position, long id)
{
if(view!=null && view.getId()!=0){
//do your code here to avoid callback twice
}
}
You should not attempt to prevent the call to the OnItemSelectedListener.
By default, Android Spinners select the first item returned by the Adapter, and therefore the OnItemSelectedListener is called to trigger some action on that item.
I would advise that the first item in your Spinner Adapter be a blank item, and your OnItemSelectedListener can ignore that blank item based on its id.
If anyone else comes across this question, it may be worth having a look at a related question I asked a while ago, which has several answers with good ideas on how to work around this issue.
Well I think I found nice solution for me, I had it in mind from start but...
I have custom wrapper class based on android Handler , that is called DoLater, and also there is custom Adapter based on Listener so you cant copy paste this but you will get idea. Dangerous thing is just that somehow delay 500 can be to long and View can be already destroyed (when user do some wired stuff quickly or phone gets slow...) so DoLater cares of that so it is not called when activity is not resumed. But this way OnItemSelectedListener is not fired on create.
public void onResume() {
super.onResume();
new DoLater(this, 500) {
public void run() {
new OnSpinnerSelectedAdapter(getBowSpinner()) {
protected void onItemSelected(int position) {
onBowSelected(position);
}
};
}
};
}