The other day the QA team reported us a bug about a blinking effect in one of our screens, specifically in the item detail screen.
It was just open that screen and I saw perfectly what they were talking about, but that wasn't happening before, what had changed?
The thing that had changed was that in that screen, now the user is able to modify the item's category, to be able to do that we added a
Spinner
to select the category from a given list, once the user select a category we redraw the screen with the new data related to that category.
The problem wasn't easy to be identified, it was happening that we were doing a "bad use" of
Spinner.setSelection()
, basically we weren't calling to Spinner.setSelection()
in the same method where the categories were added to the adapter.
I'm sure that with a piece of code it will be clearer:
This code will call to
OnItemSelectedListener.onItemSelected
saying that the item 0 was selected.
spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(this);
adapter = new ArrayAdapter<String>(this, R.layout.simple_spinner_item, items);
spinner.setAdapter(adapter);
But we didn't expect that thing to happen! what we expect is
onItemSelected
to be called when the user select a item from the Spinner
, or maybe we could expect it to be called when spinner.setSelection(position);
saying that the item position
is selected.
In our app, the user could have selected a category previously, then at some point after the data is loaded from the database we do
spinner.setSelection(position);
, "et voilà", onItemSelected
is called twice, the first call is done saying that the item 0 was selected and the second one with the position
item was selected.
At this point I decided to search in internet to see how other people had fixed that problem and I found some interesting approaches (and crazy ones):
- To keep counters for each
Spinner
withOnItemSelectedListener
that are in the screen to skip the first call toonItemSelected
. - To create a custom adapter and to add
spinner.setOnTouchListener()
to manage when the user has touched the screen previously. - To create a custom view and override the method
onClick
and callCustomOnItemClickListener.onItemClick
within it.
None of those options convinced me (I found some more that didn't either), so I decided to investigate a little more and I found that you cannot skip
onItemSelected
to be called when the adapter
is added to the Spinner
if the adapter
has already items, but we can make it called with the correct element selected if spinnerView.setSelection(position)
is called immediately after the items are added to the view/adapter
. I've thought in two ways to do that:First approach
We create the
adapter
with the items in it and we add it to the Spinner
once that we know the item that has to be selected.
adapter = new ArrayAdapter<>(this, R.layout.simple_spinner_item, items);
spinner.setAdapter(adapter);
spinner.setSelection(position);
Second approach
We create the
adapter
without the items in it and we add it to the Spinner
.
adapter = new ArrayAdapter<>(this, R.layout.simple_spinner_item);
spinner.setAdapter(adapter);
And once that we know the item that has to be selected.
spinner.setSelection(10);
adapter.addAll(items);
In conclusion
It would be nice to set
AdapterView.OnItemSelectedListener
just after add the items to the adapter
and to not see onItemSelected
be called, but it isn't, so at least we can do this that is a smart approach.Complete code for approach 1
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_layout);
final Spinner spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(this);
final ArrayAdapter<String> adapter =
new ArrayAdapter<>(this, R.layout.simple_spinner_item, items);
spinner.setAdapter(adapter);
spinner.setSelection(10);
}
Complete code for approach 2
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_layout);
final Spinner spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(this);
final ArrayAdapter<String> adapter =
new ArrayAdapter<>(this, R.layout.simple_spinner_item);
spinner.setAdapter(adapter);
findViewById(R.id.select_item).setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
spinner.setSelection(10);
adapter.addAll(items);
}
});
}
No hay comentarios:
Publicar un comentario