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.
}
}
}
I need to use
mSpinner
in ViewHolder, so the flagmOldPosition
is set in the anonymous inner class.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.
Use it in your XML like this:
All you have to do is retrieve the instance of SaneSpinner after inflation and call set selection like this:
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.
I created a small utility method for changing
Spinner
selection without notifying the user: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.
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.
Lots of answers already, here's mine.
I extend
AppCompatSpinner
and add a methodpgmSetSelection(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 anObservable
.An example of its usage, called in
onCreateView()
in aFragment
for example:where
setSelection()
is a method in the enclosing view that looks like this, and which is called both from user selection events via theObservable
and also elsewhere programmatically, so the logic for handling selections is common to both selection methods.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:
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: