5

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.

Akshay
  • 2,506
  • 4
  • 34
  • 55
Favolas
  • 6,963
  • 29
  • 75
  • 127
  • Where do you call `loadSpinner` from? – Szymon Dec 29 '13 at 22:21
  • @Szymon Hi. At `onCreateView` I'm doing this `spinner = (Spinner) itemDataLayout.findViewById(R.id.availableValues); spinner.setOnItemSelectedListener(this);` and the spinner is loaded from , imagine, `functionX`. When I click on a item on the left `listview` the function `functionX` is called. If I come from a `savedrestoredstate`, `functionX` is called from `onStart()` – Favolas Dec 29 '13 at 22:30
  • 1
    you probably have `onItemSelected` triggered before `loadSpinner` is called, which sets the adapter. You could verify the order by putting breakpoints in `onItemSelected` and on line `spinner.setAdapter()` and see what is called first after the rotation – kiruwka Dec 29 '13 at 22:36
  • @Favolas if what I wrote in previous comment is the case, you will always have NPE in the `onItemSelected` due to spinner has no children do display yet. So, you will have two options to fix this, but first you need to check if that is the case. – kiruwka Dec 29 '13 at 22:44
  • @kiruwka Nop. `loadSpinner` comes first – Favolas Dec 29 '13 at 22:44
  • @Favolas did you verify the order with the debugger ? do both calls come on Main thread ? – kiruwka Dec 29 '13 at 22:46
  • Yep. Checked with breakpoits – Favolas Dec 29 '13 at 22:47
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/44120/discussion-between-kiruwka-and-favolas) – kiruwka Dec 29 '13 at 22:48
  • @Favolas I debugged a bit and found exact cause of the issue. Please, have a look at my answer. – kiruwka Dec 31 '13 at 16:58
  • it give me a null point objection in textview – Adnan haider Nov 20 '20 at 08:00

1 Answers1

9

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.

Explanations

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.

Workaround

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.

Extra note

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.

kiruwka
  • 9,250
  • 4
  • 30
  • 41
  • Many thanks for this explanation. You are right. Gonna implement my custom spinner. – Favolas Jan 01 '14 at 12:51
  • @Favolas Glad it helped. Please consider accepting. And happy new year! – kiruwka Jan 01 '14 at 14:00
  • Hey..i am having this same issue..i implemented my custom spinner..still getting the same `nullpointerexception`.Did you solve this problem using a custom spinner or something else? – Sash_KP Aug 26 '14 at 12:54
  • I solved it [using this solution](http://stackoverflow.com/questions/13397933/android-spinner-avoid-onitemselected-calls-during-initialization?rq=1) – Sash_KP Aug 26 '14 at 13:17
  • 1
    @Favolas, did your solution define the color in XML? I have a similar question, but I cannot rely on defining the color in XML because the color is known only at runtime. My question is here: http://stackoverflow.com/questions/33747884/android-nullpointerexception-spinner-onitemselected-view-parameter-is-null-a – Rock Lee Nov 17 '15 at 06:49
  • @kiruwka Your workaround worked for me, thank you very much! But my text color is only known at runtime. Do you know any other solution? For more info, please see my question here: http://stackoverflow.com/questions/33747884/android-nullpointerexception-spinner-onitemselected-view-parameter-is-null-a – Rock Lee Nov 17 '15 at 06:56