2

I am having issues trying to restore state of Android Spinners in my application. Currently there are multiple Spinners in my Activity's ListView header that depend on one another, as the selection of one Spinner loads data for the subsequent Spinner.

The problem I am experiencing is, restoring state doesn't seem to work when I manually set selections on the Spinners. I have tried in both onRestoreInstanceState and onResume. It appears setting the selections is asynchronous when looking at the LogCat output. How can I reliably restore state of these Spinners when I have to wait for one to be selected before the other can populated and then set?

EDIT: Added code

Activity's onCreate():

mSecondSpinner = mMyListHeader.findViewById(R.id.second_spinner);
mSecondSpinnerArrayAdapter = new SecondArrayAdapter(MyActivity.this, R.layout.second_spinner_item, new ArrayList<MySecondDto>());
mSecondSpinner.setAdapter(mSecondSpinnerArrayAdapter);
mSecondSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

   @Override
   public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

      MySecondDto selectedMySecondDto = (MySecondDto) parent.getItemAtPosition(position);
      List<MyThirdDto> myThirdDtos = selectedMySecondDto.getMyThirdDtos();

      // Load third spinner with dtos....
   }

   @Override
   public void onNothingSelected(AdapterView<?> parent) {}}
);

mFirstSpinner = mMyListHeader.findViewById(R.id.first_spinner);
mFirstSpinnerAdapter= new FirstArrayAdapter(MyActivity.this, R.layout.first_spinner, mResponse.getAllDtos());
mFirstSpinner.setAdapter(mFirstSpinnerArrayAdapter);
mFirstSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

   @Override
   public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

      mSecondSpinner.setAdapter(null);

      MyFirstDto selectedMyFirstDto = (MyFirstDto ) parent.getItemAtPosition(position);
      List<MySecondDto> mySecondDtos = selectedMyFirstDto .getMySecondDtos();

      mSecondSpinnerArrayAdapter.clearAndReplaceAll(mySecondDtos);
      mSecondSpinner.setAdapter(mSecondSpinnerArrayAdapter);

      // If there is only one second dto, disable the spinner
      if (mySecondDtos== null || mySecondDtos.size() <= 1)
      {
          disableSpinner(mSecondSpinner);
      }
      else
      {
          // Enable second spinner, select the hint element
          enableSpinner(mSecondSpinner);
          mSecondSpinner.setSelection(mSecondSpinnerArrayAdapter .getHintIndex());
      }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {}
});

Activity's onRestoreInstanceState():

 mFirstSpinner.setSelection(mFirstAdapterPosition);
 mFirstSpinnerArrayAdapter.notifyDataSetChanged();

 mSecondSpinner.setSelection(mSecondAdapterPosition);
 mSecondSpinnerArrayAdapter.notifyDataSetChanged();
JPM
  • 1,482
  • 5
  • 18
  • 25

2 Answers2

0

Have u tried this

mySpinner.post(new Runnable() { @Override public void run() { mySpinner.setSelection(position); } });
lib4backer
  • 3,337
  • 3
  • 18
  • 16
  • Hi. I am not sure what you mean. Would this be done in onRestoreInstanceState, and how would I "wait" until the selection is done in the first spinner, before doing the same in the second? – JPM Dec 29 '17 at 02:06
0

This might work for you. There could be a bug in it somewhere, so be careful. I have a similar situation to you but I had 3 spinners and they get populated depending on the selection of the previous one.

The idea is to store the Indexes/Positions of the Spinners in IndexVariables. Theses Variables have default value of -1.

Store current values in onSaveInstanceState,

Restore values in onActivityCreated.

In onItemSelected check if selected Item = null, check if the IndexVariable was set (i.e. !-= -1)

If so use it to set Spinner then set IndexVariable back to -1;

Here's the class

(I used NothingSelectedSpinnerAdapter from How to make an Android Spinner with initial text "Select One". Not really important but just giving a shout out to the guy/girl where I got that code.)

public class SpinnerTestFragment  extends Fragment {

private MainActivity activity;

private static final String SELECTED_THEME_IDX_STORAGE_KEY = "mSelectedTheme_IDX_StorageKey";
private static final String SELECTED_AIM_IDX_STORAGE_KEY = "mSelectedAim_IDX_StorageKey";
private static final String SELECTED_GOAL_IDX_STORAGE_KEY = "mSelectedGoal_IDX_StorageKey";

private static String TAG = "SpinnerTestFragment";

private Spinner spnrThemes;
private Spinner spnrAims;
private Spinner spnrGoals;

private String mSelectedTheme;
private String mSelectedAim;
private String mSelectedGoal;

private int mSelectedAimIdx = -1;
private int mSelectedThemeIdx = -1;
private int mSelectedGoalIdx = -1;


//----------------------------------------------------------------------------------------//


public SpinnerTestFragment() {
}//ctor

//----------------------------------------------------------------------------------------//

@Override
public View onCreateView(LayoutInflater inflater,
                         ViewGroup container,
                         Bundle savedInstanceState) {
    Log.d(TAG, "onCreateView");

    activity = (MainActivity) getActivity();


    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_photo, container, false);


    spnrThemes = view.findViewById(R.id.spnrThemes);
    spnrAims = view.findViewById(R.id.spnrAims);
    spnrGoals = view.findViewById(R.id.spnrGoals);


    setSpinner(spnrThemes, "Select Theme", ThemesAimsGoals.getThemes());

    spnrThemes.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Object selectedItem = parent.getItemAtPosition(position);

            if (selectedItem != null) {
                mSelectedTheme = selectedItem.toString();
                setSpinner(spnrAims, "Select Aim", ThemesAimsGoals.getAims(mSelectedTheme));

            } else if(mSelectedThemeIdx != -1){
                selectedItem = parent.getItemAtPosition(mSelectedThemeIdx);
                mSelectedTheme = selectedItem.toString();
                setSpinner(spnrAims, "Select Aim", ThemesAimsGoals.getAims(mSelectedTheme));
                parent.setSelection(mSelectedThemeIdx);
                mSelectedThemeIdx = -1;
            }//Else
        }//onItemSelected

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        }
    });


    spnrAims.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Object selectedItem = parent.getItemAtPosition(position);

            if (selectedItem != null) {
                mSelectedAim = selectedItem.toString();
                setSpinner(spnrGoals, "Select Goal", ThemesAimsGoals.getGoals(mSelectedTheme, mSelectedAim));

            } else if(mSelectedAimIdx != -1){
                selectedItem = parent.getItemAtPosition(mSelectedAimIdx);
                mSelectedAim = selectedItem.toString();
                setSpinner(spnrGoals, "Select Goal", ThemesAimsGoals.getGoals(mSelectedTheme, mSelectedAim));
                parent.setSelection(mSelectedAimIdx);
                mSelectedAimIdx = -1;
            }//Else
        }//onItemSelected

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        }
    });

    spnrGoals.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Object selectedItem = parent.getItemAtPosition(position);
            if (selectedItem != null) {
                mSelectedGoal = selectedItem.toString();

            }else if(mSelectedGoalIdx != -1){
                selectedItem = parent.getItemAtPosition(mSelectedGoalIdx);
                mSelectedGoal = selectedItem.toString();
                parent.setSelection(mSelectedGoalIdx);
                mSelectedGoalIdx = -1;
            }//Else
        }//onItemSelected

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        }
    });

    return view;
}//onCreateView

//----------------------------------------------------------------------------------------//

/**
 * Populate Spinner
 * @param spnr Spinner to populate
 * @param prompt What to show at the start
 * @param array Items in the spinner
 */
private void setSpinner(Spinner spnr, String prompt, String[] array) {
    spnr.setPrompt(prompt);
    ArrayAdapter<CharSequence> adapter = new ArrayAdapter(activity, android.R.layout.simple_spinner_item, array);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spnr.setAdapter(
            new NothingSelectedSpinnerAdapter(
                    adapter,
                    R.layout.contact_spinner_row_nothing_selected,
                    activity,
                    prompt));
}//setSpinner

//----------------------------------------------------------------------------------------//

/**
 * Some lifecycle callbacks so that the image can survive orientation chang
 *
 * @param outState current state of fragment
 */
@Override
public void onSaveInstanceState(Bundle outState) {
    Log.d(TAG, "onSaveInstanceState");
    super.onSaveInstanceState(outState);

    outState.putInt(SELECTED_THEME_IDX_STORAGE_KEY, spnrThemes.getSelectedItemPosition());
    outState.putInt(SELECTED_AIM_IDX_STORAGE_KEY, spnrAims.getSelectedItemPosition());
    outState.putInt(SELECTED_GOAL_IDX_STORAGE_KEY, spnrGoals.getSelectedItemPosition());

}//onSaveInstanceState

//----------------------------------------------------------------------------------------//

/**
 * Rebuilds the Activity/Fragment in the image of the last one.
 *
 * @param savedInstanceState Info from last session or rotation
 */
@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    Log.d(TAG, "onActivityCreated");

    if (savedInstanceState == null)
        return;

    mSelectedThemeIdx = savedInstanceState.getInt(SELECTED_THEME_IDX_STORAGE_KEY);
    mSelectedAimIdx = savedInstanceState.getInt(SELECTED_AIM_IDX_STORAGE_KEY);
    mSelectedGoalIdx = savedInstanceState.getInt(SELECTED_GOAL_IDX_STORAGE_KEY);


}//onActivityCreated

}//Cls
ShanieMoonlight
  • 1,623
  • 3
  • 17
  • 28
  • Thanks for this Shanie but I am trying to recreate this but from my log statements, it appears onCreateView is called before onActivityCreated is called so I am not sure how this would work. The Spinner has already been initialized before even attempting to restore the values. – JPM Mar 19 '18 at 21:11
  • @JPM I've changed my mind. The real answer is here https://stackoverflow.com/questions/27745948/android-spinner-onitemselected-called-multiple-times-after-screen-rotation?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa – ShanieMoonlight May 12 '18 at 13:18