I'm having trouble getting my recyclerview to save state on orientation change. Have used onSaveInstanceState in my fragment but obviously not correctly as it is not working. Any advice and pointers on how to fix?
SecondActivity
public class SecondActivity extends AppCompatActivity {
private boolean mTwoPane;
ArrayList<Recipe> myList;
List<Steps> steps;
android.support.v7.app.ActionBar actionBar;
StepsFragment fragment;
//Eventbus
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_two);
actionBar = getSupportActionBar();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
myList = getIntent().getParcelableArrayListExtra("list");
String id = getIntent().getStringExtra("intent");
int result = Integer.parseInt(id);
steps = (ArrayList) myList.get(result - 1).getSteps();
String nameString = myList.get(result - 1).getName();
getSupportActionBar().setTitle(nameString);
if (findViewById(R.id.android_me_linear_layout) != null) {
mTwoPane = true;
if (savedInstanceState == null) {
Bundle arguments = new Bundle();
arguments.putParcelableArrayList("myList", myList);
arguments.putInt("result", result);
arguments.putBoolean("twoPane", mTwoPane);
StepsFragment fragment = new StepsFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
} else {
// We're in single-pane mode and displaying fragments on a phone in separate activities
mTwoPane = false;
Bundle arguments = new Bundle();
arguments.putParcelableArrayList("myList", myList);
arguments.putInt("result", result);
arguments.putBoolean("twoPane", mTwoPane);
StepsFragment fragment = new StepsFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
super.onBackPressed();
if(mTwoPane) {
startActivity(new Intent(SecondActivity.this, MainActivity.class));
finish();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(Message event) {
if (event.getMessage() == 2) {
Fragment newFragment = new DetailFragment();
newFragment.setArguments(event.getBundle());
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.detail_container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
Log.d(LOG_TAG, "Eventbus worked");
}
else if ( event.getMessage() == 3){
Fragment newFragment = new DetailFragment();
newFragment.setArguments(event.getBundle());
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
}
StepsFragment
public class StepsFragment extends android.support.v4.app.Fragment {
ArrayList<Recipe> myList;
List<Steps> myStepsList;
List <Ingredients> ingredients;
private RecyclerView recyclerView;
StepItemRecyclerViewAdapter stepItemRecyclerViewAdapter;
private RecyclerView.LayoutManager mLayoutManager;
int result;
List<String> ingredientsString;
String string;
Boolean mTwoPane;
Parcelable listState;
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("myArrayListTwo",(ArrayList) myStepsList);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null || !savedInstanceState.containsKey("myArrayListTwo")){
myStepsList = new ArrayList<>();
}
else{
myStepsList = savedInstanceState.getParcelableArrayList("myArrayListTwo");
}
mTwoPane = getArguments().getBoolean("twoPane");
myList = getArguments().getParcelableArrayList("myList");
result = getArguments().getInt("result") - 1;
myStepsList = myList.get(result).getSteps();
ingredients = myList.get(result).getIngredients();
//Create a String array for all ingredients strings
ingredientsString = new ArrayList<>();
ingredientsString.add("Ingredients" + "\n");
for(int x = 0; x < ingredients.size(); x++){
ingredientsString.add(ingredients.get(x).getIngredient() + " " + ingredients.get(x).getQuantity() + " " + ingredients.get(x).getMeasure());
Log.d(LOG_TAG, ingredientsString.get(x).toString());
}
StringBuilder stringBuilder = new StringBuilder();
for(String s : ingredientsString){
stringBuilder.append(s + "\n");
}
string = stringBuilder.toString();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_steps, container, false);
recyclerView = (RecyclerView) rootView
.findViewById(R.id.my_recycler_view);
mLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
stepItemRecyclerViewAdapter = new StepItemRecyclerViewAdapter(myStepsList);
recyclerView.setAdapter(stepItemRecyclerViewAdapter);
TextView textView = (TextView) rootView.findViewById(R.id.text_view);
textView.setText(string);
return rootView;
}
public class StepItemRecyclerViewAdapter
extends RecyclerView.Adapter<StepItemRecyclerViewAdapter.ViewHolder> {
private final List<Steps> mValues;
public StepItemRecyclerViewAdapter(List<Steps> items) {
mValues = items;
}
@Override
public StepItemRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_list_content, parent, false);
return new StepItemRecyclerViewAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(final StepItemRecyclerViewAdapter.ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.mContentView.setText(mValues.get(position).getShortDescription());
holder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mTwoPane){
Bundle args = new Bundle();
args.putParcelableArrayList("myList", myList);
args.putInt("result", result);
args.putString("int", holder.mItem.getId());
EventBus.getDefault().postSticky(new Message(2, args));
Log.d(LOG_TAG, "You clicked a button" + holder.mItem.toString());
} else if( mTwoPane == false){
Bundle args = new Bundle();
args.putParcelableArrayList("myList", myList);
args.putInt("result", result);
args.putString("int", holder.mItem.getId());
EventBus.getDefault().postSticky(new Message(3, args));
Log.d(LOG_TAG, "You clicked a button" + holder.mItem.toString());
}
}
});
}
@Override
public int getItemCount() {
return mValues.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public View mView;
public TextView mContentView;
public Steps mItem;
public ViewHolder(View view) {
super(view);
mView = view;
mContentView = (TextView) view.findViewById(R.id.content);
}
@Override
public String toString() {
return super.toString() + " '" + mContentView.getText() + "'";
}
}
}
}
activity_main_two.xml https://github.com/RJRogers/TwoPaneUITest/blob/master/app/src/main/res/layout/activity_main_two.xml
fragment_steps.xml https://github.com/RJRogers/TwoPaneUITest/blob/master/app/src/main/res/layout/fragment_steps.xml
Something that jumps out at me from your posted code is this (in your activity):
if (findViewById(R.id.android_me_linear_layout) != null) { if (savedInstanceState == null) { ... getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, fragment) .commit(); } } else { ... getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, fragment) .commit(); }
In general, you should only have to add the fragment once, when savedInstanceState
is null, regardless of orientation. The fragment manager will automatically save and restore your fragment even when your activity's layout changes (as long as both layouts have a container with the same id). It is possible that this is causing a new fragment to appear, instead of one that has had its state saved and restored.
Another thing that stands out is this (in your fragment):
if(savedInstanceState == null || !savedInstanceState.containsKey("myArrayListTwo")){ myStepsList = new ArrayList<>(); } else{ myStepsList = savedInstanceState.getParcelableArrayList("myArrayListTwo"); } ... myStepsList = myList.get(result).getSteps();
This looks like you're accidentally overwriting the saved list with a new one based on the fragment's arguments bundle. I can't exactly follow precisely what this fragment/activity are trying to do, but this seems like a plausible spot for your problem to be hiding.