I'm using 'Fragment' to obtain a layout with two 'fragments' so my class extends 'Fragment'.
I'm loading a 'ListView' on the left 'fragment' and on the right 'fragment' I have another modified 'ListView'. Here, I have a 'Spinner' that I have to change it's color.
I have this code:
private void loadSpinner(int value) {
//Not relevant code
adapter = new ArrayAdapter<CharSequence>(getActivity().getApplicationContext(), android.R.layout.simple_spinner_item, data);
adapter.setDropDownViewResource(R.layout.spinner);
spinner.setAdapter(adapter);
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String value = parent.getItemAtPosition(position).toString();
((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.black));
//Some code
}
The code above is working as expected UNTIL I rotate the screen of my device. Here, I'm getting a nullpointerexception
at ((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.black));
.
If I comment the above line, since I'm saving the state before rotating the screen, after I rotate the screen, my data is restored and all works as expected EXCEPT the spinner that is on light color thus being poorly visible.
I understand that I can create my own 'spinner' layout and solve this matter but I would like to know how can I solve this.
After screen rotation, your Spinner
view has no children views attached yet (you can verify it by calling parent.getChildCount()
), thus your parent.getChildAt(0)
returns null, resulting in NPE
.
After activity creation, Android displays its content by calling onMeasure()
followed by onLayout()
for each view in the layout hierarchy.
Spinner
will have its children views created, populated with data from adapter, and attached during call to onLayout()
, as you can see here.
So, normally for your Spinner you will have sequence : onMeasure
-> onLayout
-> onItemSelected
and since onLayout
is called before onItemSelected
- everything works fine for you during initial activity startup.
Now, lets have a look what happens when you rotate the screen:
Before Activity is destroyed its onSavedInstanceState
gets called which propagates the call to Spinner
's onSavedInstanceState
that will save current selected position.
After rotation, your activity is re-created and then its onRestoreInstanceState
is called.
when Activity#onRestoreInstanceState
is called it will eventually call to Spinner's onRestoreInstanceState, which has the following code :
SavedState ss = (SavedState) state;
//...some code
if (ss.selectedId >= 0) {
mDataChanged = true;
//...some more code
}
As you can see, it will always set mDataChanged flag if previous state of the spinner was saved with onSavedInstanceState.
Then, onMeasure() is called, which has the following piece of code :
if (mDataChanged) {
handleDataChanged();
}
Implementation of handleDataChanged() will eventually call fireOnSelected(), triggering Spinner's onItemSelected
.
Thus onItemSelected
gets called during onMeasure
, before onLayout
.
And as explained above, Spinner
has no children before its onLayout
is called.
Hope the problem is clear now.
To confirm that behaviour and resolve NPE you could simply provide empty implementation for onSavedInstanceState
/onRestoreInstanceState
in your activity (without call to super implementation) :
@Override
protected void onSaveInstanceState(Bundle outState) { /* do nothing */ }
This would prevent setting mDataChanged
and onMeasure
will not trigger fireOnSelected
.
Not calling Activity super.
implementation when overriding onSavedInstanceState
/onRestoreInstanceState
is definitely not recommended, as a minimum you will not be able to automatically save states of your internal views (including your spinner).
I encourage you to only do that to verify the behavior explained above and to see that NPE
is gone.
The preferable solution is to define TextView
with desired color for you spinner item in a .xml layout and use it for constructing ArrayAdapter
for the spinner.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With