0

In my app the main activity is a recyclerview that contains a list of custom objects (recipes in this case). Whenever I create a new recipe (my object) and save it, the first object I created seems to be deleted and replaced with this new entry. Here is the code from the main activity:

package groceryproject.jacob.com.recipelist;

import android.content.Intent;
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.view.View.OnClickListener;
import android.content.Intent;
import android.widget.RelativeLayout;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class RecipeList extends AppCompatActivity{
    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    ArrayList<Recipe> recipes = new ArrayList<>();
    Recipe passedRecipe = new Recipe();

    //TODO: Fix bug: Newly created recipes replace each other
    protected void onCreate(Bundle savedInstanceState) {

        /*
        for (int i = 0; i < 1; i++){
            Recipe recipe = new Recipe();
            recipe.setRecipeName("Recipe #" + i);
            recipe.setPrepTime(i);
            recipe.setCookTime(i);
            recipe.setServingSize(i + " slices");
            ArrayList<String> ingredientsTest = new ArrayList<String>();
            ArrayList<String> directionsTest = new ArrayList<String>();
            for(int j = 0; j < 5; j++){
                ingredientsTest.add("Test Ingredient: " + j);
                directionsTest.add("Test Direction: " + j);
            }
            recipe.setIngredients(ingredientsTest);
            recipe.setDirections(directionsTest);
            recipes.add(recipe);
        }
        */

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recipe_list);
        mRecyclerView = (RecyclerView) findViewById(R.id.list_recycler_view);

        if(mLayoutManager == null) {
            mLayoutManager = new LinearLayoutManager(this);
            mRecyclerView.setLayoutManager(mLayoutManager);
        }

        if (mAdapter == null) {
            mAdapter = new MyAdapter(recipes);
            mRecyclerView.setAdapter(mAdapter);
        }

        if (getIntent().getExtras() != null){
            if (getIntent().getExtras().containsKey("recipe_key")){
                passedRecipe = (Recipe) getIntent().getSerializableExtra("recipe_key");
                recipes.add(passedRecipe);
                mAdapter.notifyDataSetChanged();

            }
        }

        //This button creates a new empty Recipe object and passes it to the EditRecipe class
        //The Recipe object is passed as a serializable
        Button mCreateRecipeButton = (Button) findViewById(R.id.create_new_recipe_button);
        mCreateRecipeButton.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                //Create a new empty recipe to be passed to the EditRecipe class
                Recipe passedRecipe = new Recipe();
                Intent i = new Intent(RecipeList.this, EditRecipe.class);
                i.putExtra("passed_recipe_key", (Serializable) passedRecipe);
                RecipeList.this.startActivity(i);
            }
        });

    }
}

Code for the adapter:

package groceryproject.jacob.com.recipelist;

/**
 * Created by Jacob on 9/27/2016.
 */
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

import org.w3c.dom.Text;

//TODO: Rename this to something more relevant instead of MyAdapter
//TODO: Create a delete button
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{

    private List<Recipe> mRecipeSet;

    public MyAdapter(List<Recipe> recipes){
        mRecipeSet = recipes;
    }

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        //This is what will handle what happens when you click a recipe in the recycler view
        @Override
        public void onClick(View v){
            int position = getAdapterPosition();
            Intent i = new Intent(v.getContext(), RecipeTextView.class);
            Recipe selectedRecipe = mRecipeSet.get(position);
            i.putExtra("view_recipe_key", (Serializable) selectedRecipe);
            //Every view has a context, and to start the activity I must get that context
            v.getContext().startActivity(i);
        }

        public TextView mRecipeName;
        public TextView mPrepTime;
        public TextView mCookTime;
        public TextView mServingSize;
        public RelativeLayout mRecipeTextSection;


        public ViewHolder(View v) {
            super(v);
            mRecipeName = (TextView) v.findViewById(R.id.recipe_list_recycler_view_recipe_name);
            mServingSize = (TextView) v.findViewById(R.id.recipe_list_recycler_view_serving_size);
            mPrepTime = (TextView) v.findViewById(R.id.recipe_list_recycler_view_prep_time);
            mCookTime = (TextView) v.findViewById(R.id.recipe_list_recycler_view_cook_time);
            mRecipeTextSection = (RelativeLayout) v.findViewById(R.id.recycled_item_section_view);

            mRecipeTextSection.setOnClickListener(this);

        }
    }


    public void add(int position, Recipe item) {
        mRecipeSet.add(position, item);
        notifyItemInserted(position);
    }

    public void remove(Recipe item) {
        int position = mRecipeSet.indexOf(item);
        mRecipeSet.remove(position);
        notifyItemRemoved(position);
    }


    public MyAdapter(ArrayList<Recipe> myRecipeset) {
        mRecipeSet = myRecipeset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recipe_item_recycled, parent, false);
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    //This is the code that controls what goes into the view.
    //TODO: Edit this code to control the placement of each element of a recipe in the recycler.
    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Recipe recipe = mRecipeSet.get(position);
        String recipeName = recipe.getRecipeName();
        String prepTime = "Prep Time: " + String.valueOf(recipe.getPrepTime()) + " minutes";
        String cookTime = "Cook Time: " + String.valueOf(recipe.getCookTime()) + " minutes";
        String servingSize = "Serves: " + String.valueOf(recipe.getServings());

        holder.mRecipeName.setText(recipeName);
        holder.mServingSize.setText(servingSize);
        holder.mPrepTime.setText(prepTime);
        holder.mCookTime.setText(cookTime);


    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mRecipeSet.size();
    }



}

And finally code for which the object is edited:

package groceryproject.jacob.com.recipelist;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.Selection;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

//This class is used to edit a recipe. Editing includes both creating a new recipe and editing an existing one
//The edit and create button are in RecipeTextView (Edit) and RecipeList (Create)
public class EditRecipe extends AppCompatActivity {
    private final String TAG = "myApp";
    private EditText mRecipeName;
    private EditText mServings;
    private EditText mPrepTime;
    private EditText mIngredients;
    private EditText mDirections;
    private Button mSaveButton;
    private EditText mCookTime;

    //These are declared here so that I can use them within each edit text listener, and then set them to the values
    //of the recipe object when the save button is pushed.

    private String recipeName;
    private String prepTime;
    private String cookTime;
    private String servings;
    //For directions and ingredients, we will seperate these into a list of strings by \n (new line).
    private String directions;
    private String ingredients;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate() called");
        setContentView(R.layout.activity_edit_recipe);

        final Recipe passedRecipe = (Recipe) getIntent().getSerializableExtra("passed_recipe_key");

        mRecipeName = (EditText) findViewById(R.id.recipe_name_text_edit);
        mPrepTime = (EditText) findViewById(R.id.prep_time_edit_text);
        mCookTime = (EditText) findViewById(R.id.cook_time_edit_text);
        mServings = (EditText) findViewById(R.id.serving_edit_text);
        mIngredients = (EditText) findViewById(R.id.ingredients_edit_text);
        mDirections = (EditText) findViewById(R.id.directions_edit_text);
        mSaveButton = (Button) findViewById(R.id.save_edit_recipe_button);

        //TODO: Make preptime and cook time strings, then change it to null.
        //The following if statements will only be triggered if this class is accessed from editing an
        //already existing recipe. Otherwise a new recipe is created.
        if(passedRecipe.getRecipeName() != null){
            mRecipeName.setText(passedRecipe.getRecipeName(), TextView.BufferType.EDITABLE);
        }
        if(passedRecipe.getPrepTime() != null){
            mPrepTime.setText(passedRecipe.getPrepTime(), TextView.BufferType.EDITABLE);
        }
        if(passedRecipe.getCookTime() != null){
            mCookTime.setText(passedRecipe.getCookTime(), TextView.BufferType.EDITABLE);
        }
        if(passedRecipe.getServings() != null){
            mServings.setText(passedRecipe.getServings(), TextView.BufferType.EDITABLE);
        }
        //For the array list values, we check if the array list is empty
        //If it isn't empty, we save each value of the array list into a string concatenated with a new line
        //We then set that string as the new line
        if(passedRecipe.getIngredients() != null){
            String passedIngredientString = "";
            for(int i = 0; i < passedRecipe.getIngredients().size(); i++){
                passedIngredientString += passedRecipe.getIngredients().get(i) + "\n";
            }
            mIngredients.setText(passedIngredientString, TextView.BufferType.EDITABLE);
        }
        if(passedRecipe.getDirections() != null){
            String passedDirectionString = "";
            for(int i = 0; i < passedRecipe.getDirections().size(); i++){
                passedDirectionString += passedRecipe.getDirections().get(i) + "\n";
            }
            mDirections.setText(passedDirectionString, TextView.BufferType.EDITABLE);
        }



        //In the following Listeners I use .trim at the end to get rid of white space users tend to leave
        //For the integer values I first store the data in a string then parse it to an int.
        mRecipeName.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                recipeName = mRecipeName.getText().toString().trim();

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        mPrepTime.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                String prepString = mPrepTime.getText().toString().trim();
                prepTime = prepString;
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        mServings.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                String servingString = mServings.getText().toString().trim();
                servings = servingString;
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        mCookTime.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                String cookTimeString = mCookTime.getText().toString().trim();
                cookTime = cookTimeString;
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        mDirections.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                //For now we will save this in a string, then seperate each line into an array list.
                directions = mDirections.getText().toString().trim();


            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        mIngredients.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                //For now we will save this in a string, then seperate each line into an array list.
                ingredients = mIngredients.getText().toString().trim();
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });


        //TODO: Pass the created object back to the RecipeList Class when save is pressed
        mSaveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //If statement to make sure the recipe name exists. Every other value can be empty
                //if the user wishes
                if (TextUtils.isEmpty(recipeName)){
                    mRecipeName.setError("Recipe name can not be empty.");
                    return;
                }

                passedRecipe.setRecipeName(recipeName);
                passedRecipe.setCookTime(cookTime);
                passedRecipe.setPrepTime(prepTime);
                passedRecipe.setServingSize(servings);

                //Save each line of the strings directions and ingredients into an array list
                List<String> directionsList = Arrays.asList(directions.split("\n"));
                List<String> ingredientsList = Arrays.asList(ingredients.split("\n"));

                passedRecipe.setDirections(directionsList);
                passedRecipe.setIngredients(ingredientsList);

                Intent i = new Intent(EditRecipe.this, RecipeList.class);
                i.putExtra("recipe_key", (Serializable) passedRecipe);
                EditRecipe.this.startActivity(i);


            }
        });
    }


}

I'm pretty confident the issue is in the onCreate method of RecipeList, and I figured maybe I'm recreating the list every time that class is run, but I'm not sure how to correct that. The adapter was hard enough for me to get working so I still don't fully understand how everything works in that class.

I have a for loop in there that's commented out that was used to create fake data to test the app, and the list worked fine with that loop.

knokout1
  • 43
  • 8

1 Answers1

0

you recreate the first activity (RecipeList) from the second activity (EditRecipe) so the class of RecipeList activity will be recreated and then the List would be instantiated again.

here you re create the RecipeList activity again

 Intent i = new Intent(EditRecipe.this, RecipeList.class);
 i.putExtra("recipe_key", (Serializable) passedRecipe);
 EditRecipe.this.startActivity(i);

and in the RecipeList activity this line would run again:

 ArrayList<Recipe> recipes = new ArrayList<>();

for these kinds of actions between two Activity you need to use StartActivityForResult and waitning for the result of editing in the first activity. then the same RecipeList activity would receive the edited Recipe.

here is you can learn how to do it:

How to manage `startActivityForResult` on Android?

Community
  • 1
  • 1
Amir Ziarati
  • 14,248
  • 11
  • 47
  • 52