0

I am building an app where I am using SQLBrite sql wrapper for all my database operations. Also, I am using Auto Value to handle all my getters and setters automatically using Google AutoValue library.

Now, my issue is to handle the device rotation and to handle that my putParcelable() method expects two parameters where the first is the KEY and the second is the value of Parcelable type. As I said earlier my Recipe class (code below) is a SQLBrite implemented class and doesn't implement Parcelable. I would like some help to convert this to implement the Parcelable interface.

@AutoValue
public abstract class Recipe {
    public abstract int id();
    public abstract String name();
    public abstract List<Ingredients> ingredients();
    public abstract List<Step> steps();
    public abstract int servings();
    public abstract String image();

    public static Builder builder() {
        return new AutoValue_Recipe.Builder();
    }

    public static TypeAdapter<Recipe> typeAdapter(Gson gson) {
        return new AutoValue_Recipe.GsonTypeAdapter(gson);
    }

    @AutoValue.Builder
    public abstract static class Builder {
        public abstract Builder id(int id);
        public abstract Builder name(String name);
        public abstract Builder ingredients(List<Ingredients> ingredients);
        public abstract Builder steps(List<Step> steps);
        public abstract Builder servings(int servings);
        public abstract Builder image(String image);

        public abstract Recipe build();
    }
}

Now, I tried converting the above code to implement the Parcelable interface, however, now my code is not working. Please help me convert this class to implement Parcelable interface.

@AutoValue
public abstract class Recipe implements Parcelable {
    public static final String ID = "id";
    public static final String NAME = "name";
    public static final String INGREDIENTS = "ingredients";
    public static final String STEPS = "steps";
    public static final String SERVINGS = "servings";
    public static final String IMAGE = "image";

    private int id;
    private String name;
    private List<Ingredients> ingredients;
    private List<Step> steps;
    private int servings;
    private String image;

    public abstract int id();
    public abstract String name();
    public abstract List<Ingredients> ingredients();
    public abstract List<Step> steps();
    public abstract int servings();
    public abstract String image();

    public static Recipe.Builder builder() {
        return new AutoValue_Recipe.Builder();
    }

    public static TypeAdapter<Recipe> typeAdapter(Gson gson) {
        return new AutoValue_Recipe.GsonTypeAdapter(gson);
    }

    public static final class Builder {
        private final ContentValues values = new ContentValues();
        List<Ingredients> ingredient;
        List<Step> step;

        public Recipe.Builder id(int id) {
            values.put(ID, id);
            return this;
        }

        public Builder name(String name) {
            values.put(NAME, name);
            return this;
        }

        public Builder ingredients(List<Ingredients> ingredients) {
        *//*for(int i=0; i<ingredients.size(); i++) {
            values.put(INGREDIENTS, ingredients.get(i).ingredient());
        }*//*
            this.ingredient = ingredients;
            return this;
        }

        public Builder steps(List<Step> steps) {
            *//*for(int i=0; i<steps.size(); i++) {
                values.put(STEPS, steps.get(i));
            }*//*
            this.step = steps;
            return this;
        }

        public Builder servings(int servings) {
            values.put(SERVINGS, servings);
            return this;
        } 

        public Builder image(String image) {
            values.put(IMAGE, image);
            return this;
        }

        public Recipe build() {
            Recipe recipe = null;
            recipe.id = (int) values.get(ID);
            recipe.name = (String) values.get(NAME);
            recipe.ingredients = ingredient;
            recipe.steps = step;
            recipe.servings = (int) values.get(SERVINGS);
            recipe.image = (String) values.get(IMAGE);

            return recipe;
        }
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
        dest.writeList(ingredients);
        dest.writeList(steps);
        dest.writeInt(servings);
        dest.writeString(image);
    }
}

Edit: I will using the modified class to handle my device rotation to save scroll state of my recycler view inside a fragment. My onSaveInstanceState() method looks something like this,

private static final String BUNDLE_RECYCLER_LAYOUT = "BUNDLE_RECYCLER_LAYOUT";
ArrayList<Recipe> mRecipeList = new ArrayList<>(0);

@Override
public void onSaveInstanceState(Bundle bundle) {
    super.onSaveInstanceState(bundle);
   bundle.putParcelable(BUNDLE_RECYCLER_LAYOUT,
   mRecipeListRecyclerView.getLayoutManager().onSaveInstanceState());

   bundle.putParcelableArrayList(BUNDLE_RECYCLER_RECIPE_DATA, mRecipeList);
}

The second parameter inside the putParcelable is of Parcelable type.

My onCreateView() method is as shown:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup 
container, @Nullable Bundle savedInstanceState) {

     View view = inflater.inflate(R.layout.fragment_recipe_list, container, false);
    unbinder = ButterKnife.bind(this, view);

    if(savedInstanceState != null) {
        Parcelable savedRecyclerViewState = savedInstanceState.getParcelable(BUNDLE_RECYCLER_LAYOUT);
        mRecipeListRecyclerView.getLayoutManager().onRestoreInstanceState(savedRecyclerViewState);
        mRecipeList = savedInstanceState.getParcelableArrayList(BUNDLE_RECYCLER_RECIPE_DATA);
        mRecipeListAdapter.refreshRecipes(mRecipeList);
    } else {
        mRecipeList = getArguments().getParcelableArrayList(BUNDLE_RECYCLER_RECIPE_DATA);
    }

    mRecipeListAdapter = new RecipeListAdapter(getContext(), mRecipeList, recipeId
            -> mRecipeListPresenter.loadRecipeDetails(recipeId));
    mRecipeListAdapter.setHasStableIds(true);

    gridLayoutManager = new GridLayoutManager(getContext(), mGridColumnCount);
    mRecipeListRecyclerView.setLayoutManager(gridLayoutManager);
    mRecipeListRecyclerView.setHasFixedSize(true);
    mRecipeListRecyclerView.setAdapter(mRecipeListAdapter);

    return view;
}

Any help would be great as I am stuck while converting the class to implement the Parcelable interface.

Nithin Prasad
  • 554
  • 1
  • 9
  • 22
  • What have you tried while implement Parcelable? What are you stuck on exactly? – Code-Apprentice Jul 06 '17 at 16:41
  • I am trying to implementing this class to implement Parcelable to handle my device rotation to save scroll state in my RecyclerView in a fragment. So my code inside onSaveInstanceState looks something like this: @Override public void onSaveInstanceState(Bundle bundle) { super.onSaveInstanceState(bundle); bundle.putParcelable(BUNDLE_RECYCLER_LAYOUT, mRecipeListRecyclerView.getLayoutManager().onSaveInstanceState()); bundle.putParcelableArrayList(BUNDLE_RECYCLER_RECIPE_DATA, mRecipeList); } – Nithin Prasad Jul 06 '17 at 16:44
  • Yes, you said that in your question. I suggest that you read the Parcelable documentation and do what it says. If you already did, what happened? We need more details in order to help you. – Code-Apprentice Jul 06 '17 at 16:45
  • p.s., you should edit your question because you cannot format code in a comment. – Code-Apprentice Jul 06 '17 at 16:45
  • Note that you need to do `class Recipe **implements Parcelable**`. – Code-Apprentice Jul 06 '17 at 16:47
  • Hi I have edited the question little further. Please check it once. I have also given the code which I tried editing using class Recipe **implements Parcelable** If you see the second class that is what I have tried to do by implementing the Parcelable interface – Nithin Prasad Jul 06 '17 at 16:49
  • The second class doesn't have `implements Parcelable`. If that is not the same as your actual code, please edit. Also please explain what happened with this second example and how the actual results differ from what you want. – Code-Apprentice Jul 06 '17 at 16:52
  • "The second parameter inside the putParcelable is of Parcelable type." I do not understand what the purpose of this sentence is. Is this an error message? If so, which line causes it? Is this really the **exact** error message? – Code-Apprentice Jul 06 '17 at 16:54
  • `bundle.putParcelable(BUNDLE_RECYCLER_LAYOUT, mRecipeListRecyclerView.getLayoutManager().onSaveInstanceState());` You should not call `onSaveInstanceState()` directly. Besides, this method returns `void` and is intended to be called by the Android system. What exactly are you trying to do here? – Code-Apprentice Jul 06 '17 at 16:55
  • Sorry, I will update the question properly. Please help me out then.. – Nithin Prasad Jul 06 '17 at 17:05
  • I will do what I can. First I am trying to get a better understanding of what you are asking. If I am unable to help, hopefully any clarifications you provide will allow someone else to help you instead. – Code-Apprentice Jul 06 '17 at 17:05
  • Sure @Code-Apprentice. 2 mins. I will update the proper error. I am new here so please bare with me. – Nithin Prasad Jul 06 '17 at 17:08
  • My main aim to save the scroll state of my device on rotation. Now, to achieve that I thought of implementing my model class to implement the Parcelable as my putParcelable method wants me to have the second parameter to implement Parcelable. But, after doing so in my model class my app crashes on rotation. Now I am using SQLBrite wrapper for my database operations. Take a look once In my github: https://github.com/nithin2889/BakingApp – Nithin Prasad Jul 06 '17 at 17:11
  • Let's back up. What data do you need to store to represent the scroll state? – Code-Apprentice Jul 06 '17 at 17:15
  • I need the whole layout to be shown as it and that is the reason I used mRecyclerView.getLayoutManager().onSaveInstanceSta‌​te(). For example, I would scroll on the list in portrait mode, then on landscape mode, I want the same scroll position to be shown in my list with out any change. Hope I am clear. – Nithin Prasad Jul 06 '17 at 17:18
  • To get the gist of what I am doing is to check the following files is my repo: RecipeListFragment.java RecipeLocalDataSource.java Recipe.java DbUtils.java – Nithin Prasad Jul 06 '17 at 17:35
  • If I understand correctly, you want to store the scroll position in order to restore it after an orientation change. This can be done by storing a single `int`. You do not need to store an entire object. – Code-Apprentice Jul 06 '17 at 17:46
  • So you are saying no need to use Parcelable interface? Well, currently this is my code inside my onSaveInstance method: bundle.putParcelable(BUNDLE_RECYCLER_LAYOUT, mRecipeListRecyclerView.getLayoutManager().onSaveInstanceState()); which you said is not correct. What is the proper way to handle it then? – Nithin Prasad Jul 06 '17 at 17:48

1 Answers1

0
        mRecipeListRecyclerView.getLayoutManager().onRestoreInstanceState(savedRecyclerViewState);

You do not need to call onRestoreInstanceState(). This is already called automatically by the Android system. Additionally, you do not need to save and restore the ArrayList yourself. RecyclerView already takes care of these details for you.

As for saving the scroll position, you should do a little more research. For example, I found ListView jumps to the top on screen orientation change with a quick search. This link has a clear example of how to do what you want.

This answer is about ScrollView but the example code is very similar to what you need.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268