1

I am working on a movie project app for class. The app displays posters that users can click on. Data loads fine, but when I scroll down and rotate the phone, the app will send me back up the top and I will lose my position. What am I missing here?

I have tried implementing the onSavedInstanceState and onRestoreInstanceState to save and restore the position of the user using "scrollPos" but it doesn't seem to work.

public class MainActivity extends AppCompatActivity implements MainActivityInterface {

private static final String LOG_TAG = MainActivity.class.getSimpleName();
private String sortBy;
private MovieAdapter movieAdapter;
private RecyclerView movieGrid;
private GridLayoutManager layoutManager;
private TextView errorMessage;
private TextView noFavoritesView;
private ScrollView mScrollView;
private ProgressBar loadingIndicator;
private static final String SORT_BY_MOST_POPULAR = "http://api.themoviedb.org/3/movie/popular?api_key=" + BuildConfig.THE_MOVIE_DB_API_KEY;
private static final String SORT_BY_HIGHEST_RATED = "http://api.themoviedb.org/3/movie/top_rated?api_key=" + BuildConfig.THE_MOVIE_DB_API_KEY;
private static final String SORT_BY_FAVORITES = "";

private List<Movie> mFavoriteMovies;
final String FAVORITE_TYPE = "favorite";
private String movieType = "normal type";
private ArrayList<Movie> mMovies = new ArrayList<>();

static final String STATE_SORT_TYPE = "sortType";
static final String STATE_SCROLL_POSITION = "scrollPos";
static final String STATE_SORT_INDEX = "sortIndex";
Bundle currInstanceState;


// Stored data for the favorites

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    currInstanceState = savedInstanceState;
    setContentView(R.layout.activity_main);
    //mScrollView = findViewById(R.id.main_scroll_view);
    movieGrid = findViewById(R.id.grid_view);
    layoutManager = new GridLayoutManager(this, 2);
    movieGrid.setLayoutManager(layoutManager);
    movieAdapter = new MovieAdapter(this);
    movieGrid.setAdapter(movieAdapter);
    errorMessage = findViewById(R.id.empty_view);
    loadingIndicator = findViewById(R.id.loading_indicator);
    sortBy = "http://api.themoviedb.org/3/movie/popular?api_key=" + BuildConfig.THE_MOVIE_DB_API_KEY;
    onRestoreInstanceState(savedInstanceState);

    getMovies();
    getFavoritesDisplayed();
}


public String getSortBy() {return sortBy;}

public MovieAdapter getMovieAdapter() {return movieAdapter;}

public TextView getErrorMessage() {return errorMessage;}

public ProgressBar getLoadingIndicator() {return loadingIndicator;}

public RecyclerView getMovieGrid() {return movieGrid;}

public void startDetailActivity (Movie movie) {
    Intent intent = new Intent(this, Details.class);
    boolean isFavorite = compareToFavorite(movie);
    intent.putExtra("movie", movie);
    intent.putExtra("isFavorite", isFavorite);
    startActivity(intent);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if (id == R.id.sort_most_popular) {
        sortBy = SORT_BY_MOST_POPULAR;
        getMovies();
        return true;
    }

    if (id == R.id.sort_highest_rated) {
        sortBy = SORT_BY_HIGHEST_RATED;
        getMovies();
        return true;
    }

    if (id == R.id.sort_favorites) {
        sortBy = SORT_BY_FAVORITES;
        movieType = FAVORITE_TYPE;
        getFavoritesDisplayed();
        return true;
    }

    return super.onOptionsItemSelected(item);
}

    private void getFavoritesDisplayed() {

        // GET LIST FROM MOVIE (Room Database)

            FavoritesViewModel viewModel = ViewModelProviders.of(this).get(FavoritesViewModel.class);
            viewModel.getFavorites().observe(this, new Observer<List<Movie>>() {
                @Override
                public void onChanged(@Nullable List<Movie> movies) {
                    mMovies = (ArrayList<Movie>) movies;
                    mFavoriteMovies = movies;
                    if (movieType.equals(FAVORITE_TYPE)) {
                        Log.d(LOG_TAG, "Updating list of favorite movies from LiveData in ViewModel");
                        movieAdapter.setMovies(mMovies);
                        movieGrid.setAdapter(movieAdapter);
                    }
                }
            });
        }


// CHECK TO SEE IF MOVIE IS IN FAVORITES
private boolean compareToFavorite(Movie movie) {
    if (mFavoriteMovies != null) {
        for (int i = 0; i < mFavoriteMovies.size(); i++) {
            if (movie.getID().equals(mFavoriteMovies.get(i).getID())) {
                return true;
            }
        }
    }
    return false;
}


private void getMovies() {
    movieGrid.setVisibility(View.INVISIBLE);
        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();

        if (info != null && info.isConnectedOrConnecting()){
            errorMessage.setVisibility(View.INVISIBLE);
            loadingIndicator.setVisibility(View.VISIBLE);
            new GetMoviesTask().execute(this);
        } else {
            errorMessage.setVisibility(View.VISIBLE);
            loadingIndicator.setVisibility(View.INVISIBLE);
        }
}


@Override
protected void onSaveInstanceState(Bundle outState) {

    outState.putInt(STATE_SCROLL_POSITION, layoutManager.findFirstCompletelyVisibleItemPosition());

    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    if (savedInstanceState != null) {
        movieGrid.scrollToPosition(savedInstanceState.getInt(STATE_SCROLL_POSITION));
    }
}
}

I expect it to save where I scroll to, so when I rotate the phone it will stay in the same area as I was in before, but when I rotate the phone it sends me back up the top of the app.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Treewallie
  • 346
  • 2
  • 13
  • I thinl you dont need to save the position but to use configChanges on manifest level https://stackoverflow.com/questions/7818717/why-not-use-always-androidconfigchanges-keyboardhiddenorientation – cutiko Dec 26 '18 at 22:14

1 Answers1

1

Don't override onRestoreInstanceState() and don't call it in onCreate(). In onCreate() you're getting Bundle parameter called savedInstanceState. Use it to restore RecyclerView position and when RecyclerView is created, scrooll it to that position.

EDIT 05.01.2019:

Sample code

public class Stackoverflow extends AppCompatActivity {

static final String STATE_SCROLL_POSITION = "scrollPos";

private RecyclerView recyclerView;
private MovieAdapter movieAdapter;
private GridLayoutManager gridLayoutManager;

private int lastPosition = 0;

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

    if(savedInstanceState != null) {
        lastPosition = savedInstanceState.getInt(STATE_SCROLL_POSITION);
        // you can also restore here your list downloaded from web
    }

    recyclerView = findViewById(R.id.recycler_view);
    movieAdapter = new MovieAdapter(this);
    gridLayoutManager = new GridLayoutManager(this, 2);
    recyclerView.setAdapter(movieAdapter);
    recyclerView.setLayoutManager(gridLayoutManager);

    // you can scroll to your last position here if you'll save your
    // list data and restore if from Bundle, if not set this line
    // after you'll get your data again
    recyclerView.smoothScrollToPosition(lastPosition);

    /*getMovies();
    getFavoritesDisplayed();*/
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(STATE_SCROLL_POSITION, gridLayoutManager.findFirstCompletelyVisibleItemPosition());
    // you can also save here your list downloaded from web
}
}
DawidJ
  • 1,245
  • 12
  • 19
  • For anyone wondering, check out this post for the resolution! Happy New Year! https://stackoverflow.com/questions/54015021/how-do-i-store-users-position-from-recyclerview-and-use-scrolltoposition-to-ca/54015137?noredirect=1#comment94868068_54015137 – Treewallie Jan 03 '19 at 19:20
  • If your activity will be destroyed before this 300ms will pass, you'll get crash in your app becasue movieGrid will throw NPE. This solution is bad and poor. – DawidJ Jan 03 '19 at 20:42
  • Thanks for the heads up. Would you be able to help me understand your solution better by writing out an example code please? The other solution works really well, but I also want to learn more about your solution too. – Treewallie Jan 04 '19 at 23:11