Search code examples
androidandroid-arrayadapterandroid-spinneronsaveinstancestate

How to save a Spinner and it's adapter using OnsavedInstance and restore it after?


I have a problem with handling onSavedInstance and onRestoreInstance for my two spinners in app.

I have two SpinnerAdapter that can be dynamically fill with pair of ArrayAdapter<String>- adapter1/adapter2 and adapter3/adapter4.

In onCreate method app create 4 adapters for them, when user press button1 it fill spinnerFrom and spinnerTo with adapter1 and adapter2,when button2 fill spinners with adapter3 and adapter 4 respectively.

As i understant lifecycle - when screen rotates, android destroys app and then restore it from the beginning .

So in onSavedInstancei put my spinner into bundle

savedInstanceState.putInt("from",spinnerFrom.getSelectedItemPosition);
savedInstanceState.putInt("to",spinnertTo.getSelectedItemPosition());

on restore i retrieve them from bundle:

spinnerFrom.setSelection(savedInstanceState.getInt("from"));
spinnertTo.setSelection(savedInstanceState.getInt("to"));

The problem is: when app restore it lose my adapters and they get null value.

To check it i created technical button and next code:

 protected void checkAdapter () {
       //get Adapter from spinners
        SpinnerAdapter valueAdapter = spinnerFrom.getAdapter();
        SpinnerAdapter resultAdapter = spinnertTo.getAdapter();

    if (valueAdapter == null | resultAdapter== null) {
                Toast.makeText(this, “adapter null” , Toast.LENGTH_SHORT).show();
    }
    else{
    Toast.makeText(this, “adapters not null” , Toast.LENGTH_SHORT).show();
    }
    }

Question: How properly put AdroidSpinner and it's adapter into OnSavedInstance bundle and then restore it onRestoreInstance in case when it populates not in onCreate but in onClick methods? Thanks in advance!

App java code:

      import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.ArrayAdapter;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.RadioGroup;
    import android.widget.Spinner;
    import android.widget.SpinnerAdapter;
    import android.widget.TextView;
    import android.widget.Toast;

    import butterknife.BindView;
    import butterknife.ButterKnife;
    import butterknife.OnClick;


    public class MainActivity extends AppCompatActivity  {
        protected double getEnteredValue;
        protected SpinnerAdapter checkAdapter;
        protected int pos;
   protected ArrayAdapter<String> adapter 1, adapter2, adapter3,
                adapter4;

//butterknife bindings
@BindView(R.id.unit_From_spinner)Spinner spinnerFrom;
@BindView(R.id.unit_To_spinner)Spinner spinnertTo;
@BindView(R.id.radio_result_group) RadioGroup resultGroup;
@BindView(R.id.radio_value_gropu) RadioGroup valueGroup;
@BindView(R.id.valueEditTextView) EditText valueEdit;
@BindView(R.id.resultView) TextView resultView;
@BindView(R.id.conver_button) Button  convertButton;

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
         savedInstanceState.putString("result",resultView.getText().toString());          savedInstanceState.putString("value",valueEdit.getText().toString());         savedInstanceState.putInt("from",spinnerFrom.getSelectedItemPosition);
savedInstanceState.putInt("to",spinnertTo.getSelectedItemPosition());
savedInstanceState.putInt("radGroup1",resultGroup.getCheckedRadioButtonId());
savedInstanceState.putInt("radGroup2", valueGroup.getCheckedRadioButtonId());
super.onSaveInstanceState(savedInstanceState);

        }


@Override
protected void onCreate(Bundle savedInstanceState)  {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.unifiedlayout);
            ButterKnife.bind(this);

//adapters for spinners 
adapter1 = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_spinner_item,getResources().getStringArray( R.array.adapter_1) );
adapter2 = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_spinner_item,getResources().getStringArray(R.array. adapter_2));
adapter3 = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_spinner_item,getResources().getStringArray( R.array. adapter_3) );
adapter4 = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_spinner_item,getResources().getStringArray(R.array. adapter_4));}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
            resultView.setText(savedInstanceState.getString("result"));
            valueEdit.setText(savedInstanceState.getString("value"));
            spinnerFrom.setSelection(savedInstanceState.getInt("from"));
            spinnertTo.setSelection(savedInstanceState.getInt("to"));


            super.onSaveInstanceState(savedInstanceState);
        }

//OnClick
@OnClick({R.id.button1, R.id.button2,R.id.button3,R.id.button4} )
public void setViewOnClickEvent(View view){
    switch(view.getId()){
        case R.id. button1:
        fillSpinner(adapter1, adapter2); // fill spinner with values
                break;
            case R.id. button2:
                fillSpinner(adapter3,
                        adapter4);
                break;
        //button for check if any adapter active
         case R.id.buttonAdapter:
        checkAdapter () ;   
               break;   
       }
    }

//fill spinners when user checks radiobutton in radiogroups
protected void fillSpinner (final ArrayAdapter<String> spinnerToAdapter,
                                final ArrayAdapter<String> spinnerFromAdapter){

        valueGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup valueGroup, int checkedId) {
                pos = valueGroup.indexOfChild(findViewById(checkedId));
                switch (pos) {
                    case 0:
                        spinnerFrom.setAdapter(spinnerToAdapter);
                        break;
                    case 1:
                        spinnerFrom.setAdapter(spinnerFromAdapter);
                        break;
                }
            }
        });
        resultGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup resultGroup, int checkedId) {
                pos = resultGroup.indexOfChild(findViewById(checkedId));
                switch (pos) {
                    case 0:
                        spinnertTo.setAdapter(spinnerToAdapter);
                        break;
                    case 1:
                        spinnertTo.setAdapter(spinnerFromAdapter);
                        break;
                }
            }
        });
    }

//simple method for check if both spinners has null value
protected void checkAdapter () {
   //get Adapter from spinners
    SpinnerAdapter valueAdapter = spinnerFrom.getAdapter();
    SpinnerAdapter resultAdapter = spinnertTo.getAdapter();

if (valueAdapter == null | resultAdapter== null) {
            Toast.makeText(this, “adapter null” , Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(this, “adapters not null” , Toast.LENGTH_SHORT).show();
}

}

EDIT: Answer isn't perfect it don't works how i want yet, but this way give me possibility to save adapter: 1.Create method to retrieve string array from adapter that want' to be saved :

public static String[] getStringArray(SpinnerAdapter adapter) {
        String[] a = new String[adapter.getCount()];

        for (int i = 0; i < a.length; i++)
            a[i] = adapter.getItem(i).toString();

        return a;
}

2.In onSavedInstanceState put next code:

// here i saving arraylist that ussualy i get from resources.
 savedInstanceState.putStringArray(ADAPTER_TO,getStringArray(spinnerFrom.getAdapter()));
            savedInstanceState.putStringArray(ADAPTER_FROM,getStringArray(spinnerFrom.getAdapter()));

3.In restoreInstance create new adapter that will fill yours spinner with values and use savedInstanceState.getStringArray(ADAPTER_TO) to populate with saved values :

toAdapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_spinner_item, savedInstanceState.getStringArray(ADAPTER_TO));
fromAdapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_spinner_item, savedInstanceState.getStringArray(ADAPTER_FROM));
     //use spinner.setAdapter to set created adapter.
    spinnerFrom.setAdapter (fromAdapter)
    spinnerTo.setAdapter (toAdapter)

This still not work properly as i want, but if you want to save spinner state on rotation screen you can try use this logic.


Solution

  • You can achieve this using two methods. 1) (recommended) Using ViewModels (architecture components) 2) using save and restore state.

    1). You can create a viewModel and whenever the user selects something, call the ViewModel with the specified value. By observing the ViewModel, you can get the latest changes to that value (even after rotation changes) because ViewModel will stay alive.

    3) Hold user's selection in a local variable inside your activity, then in save and restore, put and get your value like following:

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
      super.onSaveInstanceState(savedInstanceState);
      savedInstanceState.putInt("firstSpinnerPosition", variableThatHasTheChangedPosition);
      savedInstanceState.putInt("secondSpinnerPosition", variableThatHasTheChangedPositionForSecond);
      // other stuff.
    }
    
    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
      super.onRestoreInstanceState(savedInstanceState);
      variableThatHasTheChangedPosition = savedInstanceState.getInt("firstSpinnerPosition");
      variableThatHasTheChangedPositionForSecond = savedInstanceState.getInt("secondSpinnerPosition");
    //notify your adapter of the changes
    
    }