skip to Main Content

This is the result of RecyclerView:

enter image description here

Firestore database schema:

Frestore Data

this is the code I am using. This only retrieve data like in a image

db.collection("Expenses")
    .whereEqualTo("Email",email)
    .whereGreaterThanOrEqualTo("Date",MonthFirstDate)
    .whereLessThanOrEqualTo("Date",TodayDate)
    .orderBy("Date")
    .get()
    .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>()                                            {
        @Override
        public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
            if (!queryDocumentSnapshots.isEmpty()){
                expensesArrayList.clear();
                List<DocumentSnapshot> list = queryDocumentSnapshots.getDocuments();
                for (DocumentSnapshot ds : list){
                    expenses expenses = ds.toObject(expenses.class);
                    expensesArrayList.add(expenses);
                }
                adapter.notifyDataSetChanged();
            }
        }
    })

recyclerview Adapter code

import java.util.ArrayList;

public class expensesAdapter extends RecyclerView.Adapter<expensesAdapter.expHolder> {

    private ArrayList<expenses> expensesArrayList;
    private Context context;


    public expensesAdapter(ArrayList<expenses> expensesArrayList, Context context) {
        this.expensesArrayList = expensesArrayList;
        this.context = context;
    }

    @NonNull
    @Override
    public expensesAdapter.expHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(context).inflate(R.layout.expensesitems,parent,false);
        return new expHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull expensesAdapter.expHolder holder, int position) {
        expenses expenses = expensesArrayList.get(position);
        holder.catName.setText(expenses.getCategory());
        holder.catAmont.setText(expenses.getAmount());
    }

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

    class expHolder extends RecyclerView.ViewHolder {

        private final TextView catName,catAmont;

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

            catName = itemView.findViewById(R.id.expCategory);
            catAmont =itemView.findViewById(R.id.expAmount);
        }
    }
}

This is the expenses class

public class expenses {
    String Category;
    String Date;
    String Email;
    String Amount;
    String Description;
    String expID;

    public expenses(String category, String date, String email, String amount, String description, String expID) {
        Category = category;
        Date = date;
        Email = email;
        Amount = amount;
        Description = description;
        this.expID = expID;
    }

    public expenses(){}

    public String getCategory() {
        return Category;
    }

    public void setCategory(String category) {
        Category = category;
    }

    public String getDate() {
        return Date;
    }

    public void setDate(String date) {
        Date = date;
    }

    public String getEmail() {
        return Email;
    }

    public void setEmail(String email) {
        Email = email;
    }

    public String getAmount() {
        return Amount;
    }

    public void setAmount(String amount) {
        Amount = amount;
    }

    public String getDescription() {
        return Description;
    }

    public void setDescription(String description) {
        Description = description;
    }

    public String getExpID() {
        return expID;
    }

    public void setExpID(String expID) {
        this.expID = expID;
    }
}

But i need this kind of result:

I want a category wise Sum amount on recyclerview. In my code its only showing separately.
Please gys help me for figure this out!

2

Answers


  1. This is because for every document in the database collection, you are adding it to your expenses list. You can store all the expenses category wise and then add it to your adapter list.

    ...//other code
    
    expensesArrayList.clear();
    List<DocumentSnapshot> list = queryDocumentSnapshots.getDocuments();
    
    // create a map for category wise total expense
    Map<String,Integer> map = new HashMap<>();
    
    for (DocumentSnapshot ds : list){
        expenses expenses = ds.toObject(expenses.class);
        String category = expenses.getCategory();
        map.put(category, map.getOrDefault(category, 0) + Integer.parseInt(expenses.getAmount());
    }
    
    // Iterate over map and add expense for each category
    for (Map.Entry<String,Integer> entry : map.entrySet()){
        
        // add a constructor in your expenses class for this
        expenses categoryExpense = new expenses(entry.getKey(), String.valueOf(entry.getValue()));
        expensesArrayList.add(categoryExpense);
    }
    adapter.notifyDataSetChanged();
    
    ...// other code
    
    // In you expenses.java class add this constructor as well
    public expenses(String category, String amount) {
            Category = category;
            Amount = amount;
    }
    

    However, there can be problems with this implementation in case you have similar categories with different names like, phone and Phone, they will be considered different categories. I would suggest you to redesign your implementation with enum classes for categories.

    Quick Tips:

    1. Class names should always be capitalized. So it should be Expense and not expenses.
    2. Member fields name shouldn’t be capitalized, so they should be category, amount, email, etc.
    3. Make data type of your amount field as int or double, to avoid parsing and easier calculations.
    Login or Signup to reply.
  2. The problem in your code lies in the fact that you have in your expenses class a field called Category and a getter called getCategory(), which is not correct since Firebase is looking in the database for a field named category and not Category. See the lowercase c letter vs. capital letter C?

    The Firebase SDK maps between your Java object and the JSON properties in the Cloud Firestore based on JavaBean property naming conventions. That being said, there are multiple solutions to solve this problem, but here are the simplest ones:

    • Make the fields follow the naming convention of the properties:
    public class Expenses {
        String category;
        String date;
        String email;
        String amount;
        String description;
        String expId;
    
        public expenses(String category, String date, String email, String amount, String description, String expId) {
            this.category = category;
            this.date = date;
            this.email = email;
            this.amount = amount;
            this.description = description;
            this.expId = expId;
        }
    
        public Expenses(){}
    
        public String getCategory() {
            return category;
        }
    
        public void setCategory(String category) {
            this.category = category;
        }
    
        public String getDate() {
            return date;
        }
    
        public void setDate(String date) {
            this.date = date;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    
        public String getAmount() {
            return Amount;
        }
    
        public void setAmount(String amount) {
            this.amount = amount;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        public String getExpId() {
            return expId;
        }
    
        public void setExpId(String expId) {
            this.expId = expId;
        }
    }
    
    • Use @PropertyName annotation in front of the getters, to map the the fields in your class into the name that exists inside the database:
    public class expenses {
        String Category;
        String Date;
        String Email;
        String Amount;
        String Description;
        String expID;
    
        public expenses(String category, String date, String email, String amount, String description, String expId) {
            Category = category;
            Date = date;
            Email = email;
            Amount = amount;
            Description = description;
            this.expID = expID;
        }
    
        public expenses(){}
    
        @PropertyName("Category")
        public String getCategory() {
            return Category;
        }
    
        public void setCategory(String category) {
            Category = category;
        }
    
        @PropertyName("Date")
        public String getDate() {
            return Date;
        }
    
        public void setDate(String date) {
            Date = date;
        }
    
        @PropertyName("Email")
        public String getEmail() {
            return Email;
        }
    
        public void setEmail(String email) {
            Email = email;
        }
    
        @PropertyName("Amount")
        public String getAmount() {
            return Amount;
        }
    
        public void setAmount(String amount) {
            Amount = amount;
        }
    
        @PropertyName("Description")
        public String getDescription() {
            return Description;
        }
    
        public void setDescription(String description) {
            Description = description;
        }
    
        public String getExpID() {
            return expID;
        }
    
        public void setExpID(String expID) {
            this.expID = expID;
        }
    }
    

    P.S. Please also see that I have renamed the class from expenses to Expenses, to the Java Naming Conventions.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search