I have a layout with three spinners. They differ in the option presented in the drop-down.
In my onCreateView
I have a method to setup the spinners. Inside that method I have something like this:
mySpinner = (Spinner) view.findViewById(R.id.my_spinner);
ArrayAdapter<String> mySpinner =
new ArrayAdapter<String>(getActivity(), R.layout.background,
new ArrayList<String>(Arrays.asList(getResources().getStringArray(R.array.spinner_one_data))));
mySpinner.setDropDownViewResource(R.layout.spinner_text);
mySpinner.setAdapter(mySpinner);
mySpinner.setOnItemSelectedListener(this);
As I said, my other two spinners are almost the same but with different options.
I know that onItemSelected
is called once for every spinner in a "first setup" so I have a flag to prevent this problem. With this flag solution, my spinners are working as expected.
The problem is when I select in each spinner an option and then rotate the screen. Now, onItemSelected
is called 6 times instead the 3 times that I was expecting (I've set a flag to manage this situation of the 3 times calling).
Why Is it happening and hoe should I handle this?
In general, I've found that there are many different events that can trigger the onItemSelected method, and it is difficult to keep track of all of them. Instead, I found it simpler to use an OnTouchListener to only respond to user-initiated changes.
Create your listener for the spinner:
public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {
boolean userSelect = false;
@Override
public boolean onTouch(View v, MotionEvent event) {
userSelect = true;
return false;
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (userSelect) {
// Your selection handling code here
userSelect = false;
}
}
}
Add the listener to the spinner as both an OnItemSelectedListener and an OnTouchListener:
SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
I've found a solution that is working for me.
I have the 3
spinners so onItemSelected
is called 3
times at the initial spinner setup. To avoid onItemSelected
from firing a method in the initial setup I've created a counter so onItemSelected
only fires the method accordingly the counter value.
I've realized that in my situation, if a rotated the screen, onItemSelected
is fired again the 3
times, plus a time for each spinner that is not in the position 0
.
An example:
I have the 3
spinners and the user changes 2
of them to one of the available option other then position 0
so he ends up with a situation like this:
First spinner - > Item 2 selected
Second spinner -> Item 0 selected (no changes)
Third spinner -> Item 1 selected
Now, wen I rotate the screen, onItemSelected
will be fired 3
times for the initial spinner setup plus 2
times for the spinners that aren't at position 0
.
@Override
public void onSaveInstanceState(Bundle outState) {
int changedSpinners = 0;
if (spinner1.getSelectedItemPosition() != 0) {
changedSpinners += 1;
}
if (spinner2.getSelectedItemPosition() != 0) {
changedSpinners += 1;
}
if (spinner3.getSelectedItemPosition() != 0) {
changedSpinners += 1;
}
outState.putInt("changedSpinners", changedSpinners);
}
I've saved the state in onSaveInstanceState
and then, in onCreateView
I checked if savedInstanceState != null
and if so, extracted changedSpinners
from the bundle and updated my counter to act accordingly.
To expand on Andres Q.'s answer... If you are using Java 8 you can do this with fewer lines of code by making use of lambda expressions. This method also forgoes the need to create a separate class in order to implement onTouchListener
Boolean spinnerTouched; //declare this as an instance or class variable
spinnerTouched = false;
yourSpinner.setOnTouchListener((v,me) -> {spinnerTouched = true; v.performClick(); return false;});
yourSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if(spinnerTouched){
//do your stuff here
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
//nothing selected
}
});