0

Firstly, I've looked into the existing questions that discuss similar issues as mine. Primarily this answer is the closet to the issue I'm having --> Link. I didn't see any solutions that fit my issue.

I've attached below the code for both my HomeFragment & HomeListAdapter. I've done some research as some say I shouldn't do the firebase database calls within the bindviewholder, but Firebase quick start database example is what I am following so if any firebase android engineers might give me some direction that would be great.

When I click add on my Edit Recipe Fragment. After the upload has completed it goes to the home fragment. The confusion I have is every other adapter I have in the app has the correct behavior. It only adds one recipe (only one is ever added at a time is the expected behavior).

HomeFragment

package com.irondigitalmedia.keep;


import java.util.ArrayList;


public class HomeFragment extends BaseFragment {

    private static final String TAG = HomeFragment.class.getSimpleName();

    private ArrayList<Recipe> mRecipeList;
    private ArrayList<String> mRecipeIds;
    private RecyclerView HomeRecyclerView;
    private HomeListAdapter adapter;
    private LinearLayoutManager LLM;
    private Context mContext;
    private FirebaseDatabase database;
    private DatabaseReference myRef;
    private Recipe mRecipe;
    private User mUser;
    private int likeCounter = 0;
    private MainActivity mainActivity;
    private BaseActivity baseActivity;
    private Toolbar toolbar;
    private ProgressBar progressBar;

    public HomeFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_home,container,false);

        mContext = view.getContext();
        mRecipeList = new ArrayList<>();
        mRecipeIds = new ArrayList<>();

        HomeRecyclerView = view.findViewById(R.id.frag_search_rv);
        HomeRecyclerView.addItemDecoration(new SpacesItemDecoration(8));
        LLM = new LinearLayoutManager(getContext());
        HomeRecyclerView.setLayoutManager(LLM);
        adapter = new HomeListAdapter(mContext, mRecipeList, mUser);
        HomeRecyclerView.setAdapter(adapter);

        mainActivity = (MainActivity) view.getContext();
        mainActivity.mMainNav.setSelectedItemId(R.id.nav_home);
        toolbar = mainActivity.findViewById(R.id.main_toolbar);
        toolbar.setTitle("Home");
        mainActivity.setSupportActionBar(toolbar);

        if(savedInstanceState != null){
            Log.e(TAG, "onCreateView: savedInstanceState is null");
        }else{
            LoadRecipes();
        }

        return view;
    }

    private void LoadRecipes() {
        database = FirebaseDatabase.getInstance();
        myRef = database.getReference();

        myRef.child(Constants.DATABASE_ROOT_FOLLOWING).child(getUid()).addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
                String key = dataSnapshot.getKey();
                Log.i(TAG, "onChildAdded: key is = " + key);
                if(key!=null){
                    Log.i(TAG, "onChildAdded: key is not null ");
                    myRef.child(Constants.DATABASE_ROOT_USERS_RECIPES).child(key).limitToFirst(5).addChildEventListener(new ChildEventListener() {
                        @Override
                        public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
                            Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

                            // A new comment has been added, add it to the displayed list
                            mRecipe = dataSnapshot.getValue(Recipe.class);

                            // [START_EXCLUDE]
                            // Update RecyclerView
                            mRecipeIds.add(dataSnapshot.getKey());
                            mRecipeList.add(mRecipe);
                            adapter.notifyItemInserted(mRecipeList.size() - 1);
                            // [END_EXCLUDE]
                        }

                        @Override
                        public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
                            Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

                            // A comment has changed, use the key to determine if we are displaying this
                            // comment and if so displayed the changed comment.
                            mRecipe = dataSnapshot.getValue(Recipe.class);
                            String recipeKey = dataSnapshot.getKey();

                            // [START_EXCLUDE]
                            int commentIndex = mRecipeIds.indexOf(recipeKey);
                            if (commentIndex > -1) {
                                // Replace with the new data
                                mRecipeList.set(commentIndex, mRecipe);

                                // Update the RecyclerView
                                adapter.notifyItemChanged(commentIndex);
                            } else {
                                Log.w(TAG, "onChildChanged:unknown_child:" + recipeKey);
                            }
                            // [END_EXCLUDE]
                        }

                        @Override
                        public void onChildRemoved(DataSnapshot dataSnapshot) {
                            Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

                            // A comment has changed, use the key to determine if we are displaying this
                            // comment and if so remove it.
                            String recipeKey = dataSnapshot.getKey();

                            // [START_EXCLUDE]
                            int commentIndex = mRecipeIds.indexOf(recipeKey);
                            if (commentIndex > -1) {
                                // Remove data from the list
                                mRecipeIds.remove(commentIndex);
                                mRecipeList.remove(commentIndex);

                                // Update the RecyclerView
                                adapter.notifyItemRemoved(commentIndex);
                            } else {
                                Log.w(TAG, "onChildRemoved:unknown_child:" + recipeKey);
                            }
                            // [END_EXCLUDE]
                        }

                        @Override
                        public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
                            Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

                            // A comment has changed position, use the key to determine if we are
                            // displaying this comment and if so move it.
                            mRecipe = dataSnapshot.getValue(Recipe.class);
                            String recipeKey = dataSnapshot.getKey();

                            // ...
                        }

                        @Override
                        public void onCancelled(DatabaseError databaseError) {
                            Log.w(TAG, "recipes:onCancelled", databaseError.toException());
                            Toast.makeText(mContext, "Failed to load recipes.",
                                    Toast.LENGTH_SHORT).show();
                        }
                    });

                }else{
                    Log.e(TAG, "onChildAdded: Key is null");
                }


            }

            @Override
            public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

            @Override
            public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });

    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelableArrayList(Constants.SAVED_STATE_HOME,mRecipeList);
    }

    @Override
    public void onStart() {
        super.onStart();
        getActivity().setTitle("Home");
    }

    public String getUid() {
        return FirebaseAuth.getInstance().getCurrentUser().getUid();
    }



}

HomeListAdapter

package com.irondigitalmedia.keep.Adapters;

public class HomeListAdapter extends RecyclerView.Adapter<HomeListAdapter.ViewHolder> {
    private static final String TAG = HomeListAdapter.class.getSimpleName();
    private DatabaseReference mDatabase;
    private Context context;
    private List<Recipe> mRecipesList;
    private MainActivity mainActivity;
    private User user;
    private int likeCounter = 0;

    public HomeListAdapter(Context context, List<Recipe> mRecipesList, User user) {
        this.context = context;
        this.mRecipesList = mRecipesList;
        this.user = user;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_recipes_recipe_item, parent, false);

        mDatabase = FirebaseDatabase.getInstance().getReference();
        mainActivity = (MainActivity) view.getContext();

        return new HomeListAdapter.ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        final Recipe recipe = mRecipesList.get(position);
        SetUserData(holder, position);
        holder.tv_recipe_title.setText(mRecipesList.get(position).getTitle());
        holder.tv_recipe_prepTime.setText(mRecipesList.get(position).getPrepTime());


        Glide.with(context).load(mRecipesList.get(position).getUrl())
                .placeholder(R.drawable.ic_loading).thumbnail(0.05f).fitCenter()
                .transition(DrawableTransitionOptions.withCrossFade()).centerCrop()
                .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
                .into(holder.recipe_thumbnail);

        Log.i(TAG, "onBindViewHolder: Database Reference = " +
                mDatabase.child(Constants.DATABASE_ROOT_RECIPES).child(recipe.getUid()).child(Constants.DATABASE_ROOT_LIKES));

        mDatabase.child(Constants.DATABASE_ROOT_RECIPES).child(recipe.getUid())
                .child(Constants.DATABASE_ROOT_LIKES).addValueEventListener(new ValueEventListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                likeCounter = (int) dataSnapshot.getChildrenCount();
                Log.i(TAG, "onDataChange: ChildrenCount = " + recipe.getTitle() + " " + likeCounter);
                holder.tv_like_counter.setText(Integer.toString(likeCounter));
            }


            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });



        mDatabase.child(Constants.DATABASE_ROOT_RECIPES).child(recipe.getUid())
                .child(Constants.DATABASE_ROOT_LIKES).addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                if(dataSnapshot.hasChild(getUid())){
                    holder.like.setLiked(true);
                    Log.i(TAG, "onDataChange: LIKED RECIPE...");
                }else{
                    Log.i(TAG, "onDataChange: RECIPE IS NOT LIKED...");
                    holder.like.setLiked(false);
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });


        mDatabase.child(Constants.DATABASE_ROOT_RECIPES).child(recipe.getUid())
                .addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                        if(dataSnapshot.hasChild(Constants.DATABASE_RECIPE_LIKE_COUNT_VALUE)){
                            holder.tv_like_counter.setText(String.valueOf(dataSnapshot.child(Constants.DATABASE_RECIPE_LIKE_COUNT_VALUE).getValue()));
                        }else{
                            // likes do not exist
                        }
                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError databaseError) {

                    }
                });


        holder.like.setOnLikeListener(new OnLikeListener() {
            @Override
            public void liked(LikeButton likeButton) {
                Log.i(TAG, "liked: LIKED");
                // Add like
                holder.like.setLiked(true);
                mDatabase.child(Constants.DATABASE_ROOT_RECIPES).child(recipe.getUid()).child(Constants.DATABASE_ROOT_LIKES).child(getUid()).setValue("true");

            }

            @Override
            public void unLiked(LikeButton likeButton) {
                Log.i(TAG, "unLiked: UNLIKED");
                // remove Like
                holder.like.setLiked(false);
                mDatabase.child(Constants.DATABASE_ROOT_RECIPES).child(recipe.getUid()).child(Constants.DATABASE_ROOT_LIKES).child(getUid()).removeValue();

            }

        });



    }

    private void SetUserData(ViewHolder holder, int position) {
        mDatabase.child(Constants.DATABASE_ROOT_USERS).child(mRecipesList.get(position).getCreatorId())
                .addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                        User user = dataSnapshot.getValue(User.class);
                        holder.tv_user_username.setText(user.getUsername());
                        Glide.with(context).load(user.getUrl()).centerCrop().placeholder(R.drawable.ic_loading).into(holder.userPhoto);
                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError databaseError) {

                    }
                });
    }


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


    public class ViewHolder extends RecyclerView.ViewHolder {

        public TextView tv_recipe_title, tv_recipe_prepTime, tv_user_username, tv_like_counter;
        public ImageView recipe_thumbnail;
        public LikeButton like;
        public CircleImageView userPhoto;
        public LinearLayout user_ll;
        public FirebaseAuth mAuth;
        public FirebaseDatabase mDatabase;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            mainActivity = (MainActivity) itemView.getContext();
            mDatabase = FirebaseDatabase.getInstance();
            tv_recipe_title = itemView.findViewById(R.id.recipe_item_title);
            tv_recipe_prepTime = itemView.findViewById(R.id.recipe_item_time);
            recipe_thumbnail = itemView.findViewById(R.id.recipe_item_photo);
            like = itemView.findViewById(R.id.recipe_item_image_like);
            tv_like_counter = itemView.findViewById(R.id.recipe_item_like_counter);
            userPhoto = itemView.findViewById(R.id.recipe_item_user_photo);
            tv_user_username = itemView.findViewById(R.id.recipe_item_user_username);
            user_ll = itemView.findViewById(R.id.recipe_item_user_linearLayout);

            user_ll.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    ProfileFragment pf = new ProfileFragment();
                    if(pf.isAdded()){
                        return;
                    }else{
                        Bundle bundle = new Bundle();
                        bundle.putString(Constants.EXTRA_USER_UID,mRecipesList.get(getAdapterPosition()).getCreatorId());
                        Log.i(TAG, "onClick: Fragment Interaction recipe Creator Id = " + mRecipesList.get(getAdapterPosition()).getCreatorId());
                        FragmentTransaction ft = mainActivity.getSupportFragmentManager().beginTransaction();
                        pf.setArguments(bundle);
                        ft.replace(R.id.main_frame, pf, Constants.FRAGMENT_TAG_PROFILE);
                        ft.addToBackStack(Constants.FRAGMENT_TAG_PROFILE);
                        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                        ft.commit();
                    }
                }
            });

            itemView.setOnClickListener(v -> {
                RecipeDetailsFragment rd = new RecipeDetailsFragment();
                if(rd.isAdded()){
                    return;
                }else{
                    Bundle bundle = new Bundle();
                    bundle.putString(Constants.EXTRA_RECIPE_KEY,mRecipesList.get(getAdapterPosition()).getUid());
                    bundle.putString(Constants.EXTRA_RECIPE_CREATOR_ID, mRecipesList.get(getAdapterPosition()).getCreatorId());
                    Log.i(TAG, "onClick: Fragment Interaction recipe Key is = " + mRecipesList.get(getAdapterPosition()).getUid());
                    FragmentTransaction ft = mainActivity.getSupportFragmentManager().beginTransaction();
                    rd.setArguments(bundle);
                    ft.replace(R.id.main_frame, rd, Constants.FRAGMENT_TAG_RECIPE_DETAILS);
                    ft.addToBackStack(null);
                    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                    ft.commit();
                }
            });
        }

    }

    public String getUid() {
        return FirebaseAuth.getInstance().getCurrentUser().getUid();
    }
}

EditRecipeFragment

package com.irondigitalmedia.keep.Adapters;



public class EditIngredientAdapter extends RecyclerView.Adapter<EditIngredientAdapter.IngredientViewHolder> {

    private static final String TAG = EditIngredientAdapter.class.getSimpleName();

    private String dataSnapShotKey;
    private Context mContext;
    private DatabaseReference mDatabaseReference;
    private ChildEventListener mChildEventListener;

    public List<String> mIngredientIds = new ArrayList<>();
    public List<Ingredient> mIngredients = new ArrayList<>();

    public EditIngredientAdapter(final Context mContext, DatabaseReference ref) {
        this.mContext = mContext;
        this.mDatabaseReference = ref;


        // Create child event listener
        // [START child_event_listener_recycler]
        ChildEventListener childEventListener = new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
                Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
                dataSnapShotKey = dataSnapshot.getKey();
                // A new comment has been added, add it to the displayed list
                Ingredient ingredient = dataSnapshot.getValue(Ingredient.class);

                // [START_EXCLUDE]
                // Update RecyclerView
                mIngredientIds.add(dataSnapshot.getKey());
                mIngredients.add(ingredient);
                notifyItemInserted(mIngredients.size() - 1);
                // [END_EXCLUDE]
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
                Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

                // A comment has changed, use the key to determine if we are displaying this
                // comment and if so displayed the changed comment.
                Ingredient newIngredient = dataSnapshot.getValue(Ingredient.class);
                String ingredientKey = dataSnapshot.getKey();

                // [START_EXCLUDE]
                int ingredientIndex = mIngredientIds.indexOf(ingredientKey);
                if (ingredientIndex > -1) {
                    // Replace with the new data
                    mIngredients.set(ingredientIndex, newIngredient);

                    // Update the RecyclerView
                    notifyItemChanged(ingredientIndex);
                } else {
                    Log.w(TAG, "onChildChanged:unknown_child:" + ingredientKey);
                }
                // [END_EXCLUDE]
            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {
                Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

                // A comment has changed, use the key to determine if we are displaying this
                // comment and if so remove it.
                String ingredientKey = dataSnapshot.getKey();

                // [START_EXCLUDE]
                int ingredientIndex = mIngredientIds.indexOf(ingredientKey);
                if (ingredientIndex > -1) {
                    // Remove data from the list
                    mIngredientIds.remove(ingredientIndex);
                    mIngredients.remove(ingredientIndex);

                    // Update the RecyclerView
                    notifyItemRemoved(ingredientIndex);
                } else {
                    Log.w(TAG, "onChildRemoved:unknown_child:" + ingredientKey);
                }
                // [END_EXCLUDE]
            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
                Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

                // A comment has changed position, use the key to determine if we are
                // displaying this comment and if so move it.
                Ingredient movedIngredient = dataSnapshot.getValue(Ingredient.class);
                String ingredientKey = dataSnapshot.getKey();

                // ...
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.w(TAG, "postComments:onCancelled", databaseError.toException());
                Toast.makeText(mContext, "Failed to load comments.",
                        Toast.LENGTH_SHORT).show();
            }
        };


        ref.addChildEventListener(childEventListener);
        // [END child_event_listener_recycler]

        // Store reference to listener so it can be removed on app stop
        mChildEventListener = childEventListener;

    }

    @NonNull
    @Override
    public IngredientViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        View view = inflater.inflate(R.layout.list_item_recipe_ingredient, parent, false);
        return new IngredientViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull IngredientViewHolder holder, int position) {
        Ingredient ingredient = mIngredients.get(position);
        holder.ingred.setText(ingredient.ingredient);
    }

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

    public class IngredientViewHolder extends RecyclerView.ViewHolder {

        public TextView ingred;

        public IngredientViewHolder(View itemView) {
            super(itemView);

            ingred = itemView.findViewById(R.id.recipe_ingredients_tv);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext, "Ingredient: " + mIngredients.get(getAdapterPosition()).ingredient, Toast.LENGTH_SHORT).show();
                }
            });

            itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    Toast.makeText(mContext, "Long Clicked " + getAdapterPosition(), Toast.LENGTH_SHORT).show();

                    return true;
                }
            });
        }
    }

    public void RemoveIngredient(DatabaseReference reference){
        reference.removeValue();
    }

    public void cleanupListener() {
        if (mChildEventListener != null) {
            mDatabaseReference.removeEventListener(mChildEventListener);
        }
    }


}

Screenshots:

enter image description here enter image description here enter image description here

Go To Search and back to home

enter image description here

Back to home

enter image description here

Attempted solution from Majuran (thank you), but it didn't work. Same result. When adding the list.clear() method to both lists.

wesley franks
  • 6,765
  • 4
  • 16
  • 29

2 Answers2

3

In my understanding, One thing is added again in your view. when you come back to the same fragment right? (I got the same issue in my last project)

The problem is in HomeFragment. Same values are adding again in your these lists.

mRecipeList = new ArrayList<>();
mRecipeIds = new ArrayList<>();

so clear it, before adding the listener

myRef = database.getReference();

mRecipeList.clear();
mRecipeIds.clear();

myRef.child(Constants.DATABASE_ROOT_FOLLOWING).child(getUid()).add...

Hope its helpful, Happy coding!!!

majurageerthan
  • 2,169
  • 3
  • 17
  • 30
0

I was able to resolve this by putting the database call inside the constructor of the adapter. This is the same practice that is within the example that the firebase team put inside their database example app.

wesley franks
  • 6,765
  • 4
  • 16
  • 29