简体   繁体   中英

How to properly restore view state on rotation?

I know this question relates to this question RecyclerView store / restore state between activities but it is specific to my own problem, maybe you can help. I won't post all the code as there is a LOT of it.

In the MainActivity below I want follow what what is suggested in that answer, the only difference is that I use SharePreference to distinguish between what view was last on the screen. For some reason it still loads the items for layoutManager on rotation. Not only this, but it does not even save the state of that view, it just resets. I posted all the code from this Activity in the hopes that someone can spot where I went wrong. I can also post the github link if needed. Whole app can not be used unless you have API key described in the github. Please let me know if you need the AndroidManifest or anything else.

public class MainActivity extends AppCompatActivity implements PosterAdapter.PosterItemClickHandler, FavoritesAdapter.FavoritesClickHandler {

private PosterAdapter posterAdapter;
private FavoritesAdapter favoritesAdapter;
private GridLayoutManager layoutManager;
private GridLayoutManager layoutManager1;
private MovieDataBase mDb;
private SharedPreferences prefs;
private SharedPreferences.Editor editor;
private final String FavoriteViewState = "favorites-view-state";
private boolean VIEWSTATE1;
private Parcelable favListState;
private final String FavListKey = "favorites-key";
private Bundle mBundleRecyclerViewState;



@BindView(R.id.rv_posters)RecyclerView mRecyclerViews;
@BindView(R.id.tv_error_message1) TextView mErrorMessage1;
@BindView(R.id.tv_error_message2) TextView mErrorMessage2;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ButterKnife.bind(this);
    mDb = MovieDataBase.getInstance(getApplicationContext());

    setRecyclerViews();
    updateUI(popularSortLink);
    loadFavoritesData();

    prefs = PreferenceManager.getDefaultSharedPreferences(this);

    if(savedInstanceState != null){
        boolean favoritesState = prefs.getBoolean(FavoriteViewState, VIEWSTATE1);
        if(favoritesState) {
            favListState = savedInstanceState.getParcelableArrayList(FavListKey);
            //List<MovieEntry> movies = new ArrayList<>(favListState);
            if(favListState != null) {
                favoritesAdapter = new FavoritesAdapter(favListState, MainActivity.this);
                //favoritesAdapter.setFavorites(favListState);
                mRecyclerViews.setLayoutManager(layoutManager1);
                mRecyclerViews.setHasFixedSize(false);
                mRecyclerViews.setAdapter(favoritesAdapter);
            }
        }
    }
}


// This method sets the recycler views for the main screen
public void setRecyclerViews(){
    // Create the grid layout and apply it to the poster recycler view
    layoutManager = new GridLayoutManager(this,3);
    layoutManager1 = new GridLayoutManager(this,1);
    mRecyclerViews.setLayoutManager(layoutManager);
    mRecyclerViews.setHasFixedSize(true);

}

// This method updates the UI based on whether there is a network connection or not.
public void updateUI(String movieLink){
    if(!isOnline()){
        mErrorMessage1.setVisibility(View.VISIBLE);
        mRecyclerViews.setVisibility(View.INVISIBLE);
    }else{
        mErrorMessage1.setVisibility(View.INVISIBLE);
        mRecyclerViews.setVisibility(View.VISIBLE);
        startApp(movieLink);
    }
}

//Information sourced from https://developer.android.com/training/volley/requestqueue
//09/11/18
// This method makes a network request using Androids Volley mechanisms to retrieve the json data
private void startApp(String movieLink){
    RequestQueue mRequestQueue;
    // Instantiate the cache
    Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap
    // Set up the network to use HttpURLConnection as the HTTP client.
    Network network = new BasicNetwork(new HurlStack());
    // Instantiate the RequestQueue with the cache and network.
    mRequestQueue = new RequestQueue(cache, network);
    // Start the queue
    mRequestQueue.start();

    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
            (Request.Method.GET, movieLink, null, new Response.Listener<JSONObject>() {

                @Override
                public void onResponse(JSONObject response) {

                    setRecyclerViews();
                    mErrorMessage2.setVisibility(View.INVISIBLE);
                    mRecyclerViews.setVisibility(View.VISIBLE);
                    VIEWSTATE1 = false;
                    editor = prefs.edit();
                    editor.putBoolean(FavoriteViewState, VIEWSTATE1);
                    editor.apply();

                    //Parse the JSON string and store in a list of Movie objects
                    List<MovieDetails> movieDetailsList = JsonUtility.parseMovieDetailsJson(response);

                    // display the data
                    loadMovieData(movieDetailsList);
                }
            }, new Response.ErrorListener() {

                @Override
                public void onErrorResponse(VolleyError error) {
                    // TODO: Handle error
                    Log.i("TAG", error.toString());
                    mErrorMessage2.setVisibility(View.VISIBLE);
                    mRecyclerViews.setVisibility(View.INVISIBLE);
                    VIEWSTATE1 = false;
                    editor = prefs.edit();
                    editor.putBoolean(FavoriteViewState, VIEWSTATE1);
                    editor.apply();
                }
            });
    mRequestQueue.add(jsonObjectRequest);
}

public void loadMovieData(List movieDetailsList){
    //Create the adapter using the MovieDetails lists and apply the adapter to the recycler view
    posterAdapter = new PosterAdapter(movieDetailsList, this);
    mRecyclerViews.setAdapter(posterAdapter);
}

// This method views changes in the favorites database and updates it's cooresponding recycler view
public void loadFavoritesData(){
    MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
    viewModel.getMovies().observe(this, new Observer<List<MovieEntry>>() {
        @Override
        public void onChanged(@Nullable List<MovieEntry> movieEntries) {
            Log.d("TAG", "UPDATE FROM THE DATABASE using livedata in viewmodel");
            favoritesAdapter = new FavoritesAdapter(movieEntries, MainActivity.this);
        }
    });

}


//This method updates the main view to show the favorites list
public void showFavsList(){


    mRecyclerViews.setLayoutManager(layoutManager1);
    mRecyclerViews.setHasFixedSize(false);
    mRecyclerViews.setAdapter(favoritesAdapter);
    mErrorMessage1.setVisibility(View.INVISIBLE);
    VIEWSTATE1 = true;
    editor = prefs.edit();
    editor.putBoolean(FavoriteViewState, VIEWSTATE1);
    editor.apply();
}

@Override //This method opens the next activity and loads data based on the index passed through from the adapter onClick method
public void onPosterItemClick(MovieDetails movieDetails) {

    String parcelData = MainActivity.this.getString(R.string.parcel_data);
    Intent intent = new Intent(this, DetailActivity.class);
    intent.putExtra(parcelData, movieDetails);
    startActivity(intent);
} 

@Override //Override this method to inflate the menu resource
public boolean onCreateOptionsMenu(Menu menu) {

    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.sort_by, menu);
    return true;
}

@Override //Handle clicks on certain menu items. In this case it handles the sorting methods.
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if(id == R.id.popular_sort){
        updateUI(popularSortLink);
        return true;
    }else if(id == R.id.rating_sort){
        updateUI(topRatingSortLink);
        return true;
    }else if(id == R.id.favorites_sort){
        // display the favorites list
        showFavsList();
        return true;
    }
    return super.onOptionsItemSelected(item);
}


@Override   
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    favoritesState = prefs.getBoolean(FavoriteViewState, VIEWSTATE1);

    //Log.d("TEST", String.valueOf(favoritesState));
    if(favoritesState){
        outState.putParcelableArrayList(FavListKey,favListState);
    }
}
}
  1. Use onSaveInstanceState(Bundle bundle) instead of onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) .
  2. Don't override onRestoreInstanceState(Bundle savedInstanceState) , you're getting Bundle object in onCreate() . Restore your state in there.
  3. Instead of using SharedPreferences to save state in key FavoriteViewState use onSaveInstanceState() and onCreate() . SharedPreferences is overkill in here.
  4. In setRecyclerViews() you're always setting layoutManger as your manager. It may be a problem, it's hard to say without debuging your code.

EDIT 05.01.2019:

layoutManager.onSaveInstanceState() which returns Parcelable object dont work like that. You're not supposed to call it in your class, forget about it, it is called internally. In order to restore your view properly, in onSaveInstanceState() you need to save your data list which will be restored later in onCreate() . You can also save there your last clicked position and in your case, information about which layout manager should be set after view will be recreate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM