无法在屏幕旋转时保持RecyclerView位置

时间:2018-07-18 03:22:34

标签: java android android-recyclerview

我正在加载基于对RecyclerView的调用和存储在本地Retrofit数据库中的数据的SQLite

在屏幕旋转时,RecyclerView保持重置状态。另外,当我喜欢电影时(将电影添加到我的本地房间数据库中),在某些情况下,微调器与RecyclerView中要填充的数据不一致。下面是我的代码:

public class MainActivity extends AppCompatActivity {

private RecyclerView mMovieResults;
private MyAdapter mMovieResultsAdapter;
private String query;
private String mBaseURL;
private int lastFirstVisiblePosition;
//TODO: How is someone able to load my API Key if I am storing it


private ArrayList<String> mMovieURLS;
private ArrayList<Movie> mMovies;
private List<Movie> mMovieResultsList;

private static final String SPINNER_SELECTION = "SPINNER_SELECTION";

private boolean mIsFavoriteSelected;

private AppDatabase mDb;

private Spinner mSpinner;

private SharedPreferences mPrefs;
private SharedPreferences.Editor mPrefsEditor;

private static Bundle mBundleRecyclerViewState;


private int saved_selection = -1;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.v("ONCREATE", "ONCREATE");
    if (savedInstanceState != null){
        saved_selection = savedInstanceState.getInt(SPINNER_SELECTION);
    }

    if (isOnline()) {
        setContentView(R.layout.activity_main);
        Stetho.initializeWithDefaults(this);
        mDb = AppDatabase.getInstance(getApplicationContext());
        mMovieResults = findViewById(R.id.main_recyclerview_image_results);
        GridLayoutManager glm = new GridLayoutManager(this, 3);
        glm.setOrientation(LinearLayoutManager.VERTICAL);
        mMovieResults.setLayoutManager(glm);
        mMovieURLS = new ArrayList<>();
        mMovies = new ArrayList<>();
        mMovieResultsList = new ArrayList<>();
        mMovieResultsAdapter = new MyAdapter(mMovieResultsList);
        mMovieResults.setAdapter(mMovieResultsAdapter);
        mDb = AppDatabase.getInstance(getApplicationContext());
        mPrefs = getSharedPreferences("prefs", MODE_PRIVATE);

        mPrefsEditor = mPrefs.edit();
        switch (saved_selection){
                case 0:
                    mBaseURL = "https://api.themoviedb.org/3/movie/popular/";
                    calltoRetrofit(mBaseURL);
                    break;
                case 1:
                    mBaseURL = "https://api.themoviedb.org/3/movie/top_rated/";
                    calltoRetrofit(mBaseURL);
                    break;
                case 2:
                    mIsFavoriteSelected = true;
                    mMovieURLS.clear();
                    retrieveMovies();
                    break;

                default:
                    mBaseURL = "https://api.themoviedb.org/3/movie/popular/";
                    break;
            }


    } else {
        setContentView(R.layout.activity_no_internet);
        return;
    }

}





@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.action_bar_spinner, menu);
    MenuItem item = menu.findItem(R.id.spinner);
    mSpinner = (Spinner) item.getActionView();
    if (isOnline()) {
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.spiner_list_item_array, R.layout.custom_spinner);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mSpinner.setAdapter(adapter);
        if (saved_selection >= 0){
            mSpinner.setSelection(saved_selection);
        }
        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                mPrefsEditor.putInt(SPINNER_SELECTION, i);
                mPrefsEditor.commit();
                switch (i) {
                    case 0:
                        mBaseURL = "https://api.themoviedb.org/3/movie/popular/";
                        calltoRetrofit(mBaseURL);
                        break;
                    case 1:
                        mBaseURL = "https://api.themoviedb.org/3/movie/top_rated/";
                        calltoRetrofit(mBaseURL);
                        break;
                    case 2:
                        mIsFavoriteSelected = true;
                        mMovieURLS.clear();
                        retrieveMovies();
                        break;

                    default:
                        mBaseURL = "https://api.themoviedb.org/3/movie/popular/";
                        break;
                }


            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });
        return true;
    } else {
        return true;
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt(SPINNER_SELECTION, mSpinner.getSelectedItemPosition());
    super.onSaveInstanceState(outState);
}



@Override
public boolean onOptionsItemSelected(MenuItem item) {
    super.onOptionsItemSelected(item);
    int x = item.getItemId();
    Log.v("OPTIONS_ID", String.valueOf(x));
    return true;
}

private void retrieveMovies() {
    //TODO: I am unsure if I am handling correctly. When I favorite and unfavorite a movie, and then go back to the MainActivity, it is not displaying the correct item from the Spinner.
    //TODO: For example, favorites may be listed in the Spinner, however the screen may be displaying "TOP"
    //TODO: How could I tell if I am making unnecessary queries to Room? I know it is a project requirement, but I was not sure how to handle if I am making unnecessary calls
    //TODO: on screen rotation
    MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
    final LiveData<List<Movie>> movies = viewModel.getMovies();
            movies.observe(this, new Observer<List<Movie>>() {
        @Override
        public void onChanged(@Nullable final List<Movie> movies) {
            for (Movie movie : movies) {
                String photoURL = "http://image.tmdb.org/t/p/w185" + movie.getPoster_path();
                Log.v("FAVORITE_URL", photoURL);
                mMovieURLS.add(photoURL);
            }
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mMovieResultsList = movies;
                    mMovieResultsAdapter.notifyDataSetChanged();
                }
            });

        }
    });
}





@Override
protected void onResume() {
    super.onResume();
    ((GridLayoutManager) mMovieResults.getLayoutManager()).scrollToPositionWithOffset(lastFirstVisiblePosition,0);
}

private void calltoRetrofit(String mBaseURL) {
    mMovieURLS.clear();
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
    OkHttpClient client = new OkHttpClient
            .Builder()
            .addInterceptor(interceptor)
            .build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(mBaseURL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    final ApiInterface apiInterface = retrofit.create(ApiInterface.class);

    Log.v("API", API_KEY);
    Call<Movies> call = apiInterface.getImages(API_KEY);
    call.enqueue(new Callback<Movies>() {
        @Override
        public void onResponse(Call<Movies> call, Response<Movies> response) {
            Log.v("RESPONSE", response.body().toString());
            String totalPages = String.valueOf(response.body().getTotal_pages());
            Log.v("TOTAL", totalPages);
            mMovieResultsList = response.body().getResults();
            for (Movie movie : mMovieResultsList) {
                if (movie.getPoster_path() != null) {
                    String photoURL = "http://image.tmdb.org/t/p/w185" + movie.getPoster_path();
                    mMovieURLS.add(photoURL);
                }
                mMovieResultsAdapter.notifyDataSetChanged();
            }
        }

        @Override
        public void onFailure(Call<Movies> call, Throwable t) {
            Log.v("FAILURE", t.toString());
        }
    });
}



@Override
public void onPointerCaptureChanged(boolean hasCapture) {

}

@Override
protected void onPause() {
    super.onPause();
    lastFirstVisiblePosition = ((GridLayoutManager)mMovieResults.getLayoutManager()).findFirstCompletelyVisibleItemPosition();

}

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {


    public class ViewHolder extends RecyclerView.ViewHolder {

        protected ImageView mResultImage;

        public ViewHolder(View itemView) {
            super(itemView);
            mResultImage = itemView.findViewById(R.id.movie_poster_image);
        }
    }


    public MyAdapter(List<Movie> mDataset) {
        mMovieResultsList = mDataset;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.movie_poster_image, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
        final String urlForPhoto = "http://image.tmdb.org/t/p/w185" + mMovieResultsList.get(position).getPoster_path();

        Picasso.get()
                .load(urlForPhoto)
                .into(holder.mResultImage);

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent i = new Intent(getApplicationContext(), IndividualMovieActivity.class);
                i.putExtra("Movie", mMovieResultsList.get(position));
                startActivity(i);
            }
        });

    }

    @Override
    public int getItemCount() {
        return mMovieResultsList.size();
    }

}

public interface ApiInterface {
    @GET("?language=en-US")
    Call<Movies> getImages(@Query("api_key") String api_key);
}

private boolean isOnline() {
    ConnectivityManager cm =
            (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo netInfo = cm.getActiveNetworkInfo();
    return netInfo != null && netInfo.isConnectedOrConnecting();
}

2 个答案:

答案 0 :(得分:5)

RecyclerView仅保存其滚动位置,直到您调用setAdapter为止。通过在没有数据的情况下调用setAdapter,将放弃滚动位置(如果没有位置4,则无法滚动到位置4)。

您应该等到有数据呼叫setAdapter为止。

答案 1 :(得分:3)

旋转设备时,Android会检测到配置更改并默认情况下重新启动活动。这样,您的活动将再次调用适配器,并且recyclerview返回零位置。通过添加以下命令,您的意思是您可以自行处理此配置更改,因此android不会重新启动您的活动。

将此行添加到清单文件中的活动标签

android:configChanges="screenSize|orientation"

像这样:

    <activity
        android:name=".SearchActivity"
        android:configChanges="screenSize|orientation"
        android:label="@string/search"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>