Search code examples
androidfirebase-realtime-databasearraylistpojo

When I calculate an average rating of foods, save this value to an food object it displays as 0.0


In my Realtime Database (Firebase) i have two 'collections' one for essen(food) and one for bewertung (rating of the foods)

I try to display the data for each food in my app, which works for the name of the food an the price (Preis)

therefore i calculate the average rating for each food, which works according to the values which are displayed in the console

but when i try to access the average of each food, to set it to an TextView the average is 0.0, which is the default float-value. But every other value of a food is displayed correctly (name and price)

Here is my fragment java class where i calculate the average and set all values i retrieve from firebase:

public class Aktuelle_Bewertungen_Fragment extends Fragment{


    //Elemente:
    private RecyclerView recyclerView_aktuelle_Bewertungen;


    private View aktuelleBewertungenView;

    private DatabaseReference essenRef;
    private DatabaseReference bewertungenRef;



    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        aktuelleBewertungenView =  inflater.inflate(R.layout.fragment_aktuelle_bewertungen, container, false);

        //Elemente finden:
        recyclerView_aktuelle_Bewertungen = (RecyclerView) aktuelleBewertungenView.findViewById(R.id.recyler_aktuelle_bewertungen);

        //Referenz auf essen:
        essenRef = FirebaseDatabase.getInstance().getReference().child("essen");
        //Referenz auf bewertungen:
        bewertungenRef = FirebaseDatabase.getInstance().getReference().child("bewertung");


        return aktuelleBewertungenView;
    }



    @Override
    public void onStart() {

        super.onStart();

        FirebaseRecyclerOptions options = new FirebaseRecyclerOptions.Builder<essen>().setQuery(essenRef, essen.class).build();
        final FirebaseRecyclerAdapter<essen, EssenViewHolder> adapter = new FirebaseRecyclerAdapter<essen, EssenViewHolder>(options) {

            @NonNull
            @Override
            public EssenViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.reihen, parent, false);
                EssenViewHolder viewHolder = new EssenViewHolder(view);

                return viewHolder;

            }

            @Override
            protected void onBindViewHolder(@NonNull final EssenViewHolder holder, final int position, @NonNull essen model) {


                final ArrayList<essen> testessen =new ArrayList<>();

                essenRef.addValueEventListener(new ValueEventListener() {

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

                        for (DataSnapshot ds : dataSnapshot.getChildren()){

                                final essen e = new essen();
                                  e.name =   ds.child("name").getValue(String.class);
                                  e.Preis = ds.child("Preis").getValue(String.class);


                            bewertungenRef.addValueEventListener(new ValueEventListener() {
                                @Override
                                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

                                    float sum = (float) 0.0;
                                    int counter = 0;


                                    for (DataSnapshot ds1 : dataSnapshot.getChildren()){
                                        if (ds1.child("eid").getValue(String.class).equals(e.name)){

                                            sum = sum + ds1.child("wert").getValue(float.class);
                                            counter ++;

                                        }
                                    }

                                    float durchschnitt = sum/counter;

                                    Log.i("durchschnitt" , " " + sum + " "+ counter + " " +durchschnitt);

                                    e.setAverage(durchschnitt);

                                    Log.i("essen wert: " , " " + e.getAverage());

                                }

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

                                }
                            });

                            testessen.add(e);

                        }


                        holder.essenName.setText(testessen.get(position).getName());
                        holder.essenPreis.setText(testessen.get(position).getPreis());

                        float test = (float) testessen.get(position).getAverage();
                        Log.i("test :" , " "+ test);
                        holder.test.setText(testessen.get(position).getAverage());

                    }

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

                    }
                });
            }
        };

        recyclerView_aktuelle_Bewertungen.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView_aktuelle_Bewertungen.setAdapter(adapter);
        recyclerView_aktuelle_Bewertungen.hasFixedSize();
        adapter.startListening();
    }



    public  class EssenViewHolder extends RecyclerView.ViewHolder{

        TextView essenName, essenPreis, test;

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

            essenName = itemView.findViewById(R.id.reihen_name);
            essenPreis = itemView.findViewById(R.id.reihen_preis);

            test = itemView.findViewById(R.id.reihen_test);


        }


    }


}

the output from the average calculation is: for the food test1:

I/durchschnitt: 12.5 4 3.125 -> 12.5/4 = 3.125

For test2:

I/durchschnitt: 12.5 4 3.125 -> 12.5/4 = 3.125

For test3:

I/durchschnitt: 2.5 1 2.5 -> 2.5/1 = 2.5

entries in DB: Test1: 3 + 3+ 4 +2.5 = 12.5 test 2: 3.5+3.5+3+2.5= 12.5 test 3: 2.5 = 2.5 the calculation seems to work just fine...

Here is my essen class:

    String name,Preis;
    float average;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPreis() {
        return Preis;
    }

    public void setPreis(String preis) {
        Preis = preis;
    }

    public float getAverage() {
        return average;
    }

    public void setAverage(float average) {
        this.average = average;
    }


    public essen (String name, String Preis, float average){
        this.name = name;
        this.Preis = Preis;
        this.average = average;
    }

    public essen(){}

}

Here is my XML layout File, which is displayed in an recycler view (i deleted all unnecessary Views, therefore the constrains might look a bit strange):

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:layout_margin="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">



            <TextView
                android:id="@+id/reihen_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:fontFamily="@font/roboto"
                android:text="Name"
                android:textColor="@color/rot"
                android:textSize="17sp"
                android:textStyle="bold"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.064"
                app:layout_constraintStart_toEndOf="@+id/reihen_bilder"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.149" />

            <TextView
                android:id="@+id/reihen_preis"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:fontFamily="@font/roboto"
                android:text="Preis"
                android:textColor="@color/black"
                android:textSize="17sp"
                android:textStyle="normal"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.723"
                app:layout_constraintStart_toEndOf="@+id/reihen_bilder"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.507" />



            <TextView
                android:id="@+id/reihen_test"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="TextView"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.552"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.14" />


        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

And here is a Screenshot from my Firebase Database:

enter image description here


Solution

  • I now see that you're setting the average to the views outside of the onDataChange that calculates the data. That won't work since by the time your holder.test.setText(testessen.get(position).getAverage()) runs, the e.setAverage(durchschnitt) hasn't run yet.

    Any code that needs the data from the database needs to be inside the onDataChange for that data, or be called from there.

    The simplest way to do this is to just show the average from inside the nested onDataChange:

    final ArrayList<essen> testessen =new ArrayList<>();
    
    essenRef.addValueEventListener(new ValueEventListener() {
    
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
    
            for (DataSnapshot ds : dataSnapshot.getChildren()){
    
                final essen e = new essen();
                e.name  = ds.child("name").getValue(String.class);
                e.Preis = ds.child("Preis").getValue(String.class);
    
                bewertungenRef.addListenerForSingleValue(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
    
                        float sum = (float) 0.0;
                        int counter = 0;
    
    
                        for (DataSnapshot ds1 : dataSnapshot.getChildren()){
                            if (ds1.child("eid").getValue(String.class).equals(e.name)){
    
                                sum = sum + ds1.child("wert").getValue(float.class);
                                counter ++;
    
                            }
                        }
    
                        float durchschnitt = sum/counter;
    
                        Log.i("durchschnitt" , " " + sum + " "+ counter + " " +durchschnitt);
    
                        e.setAverage(durchschnitt);
    
                        Log.i("essen wert: " , " " + e.getAverage());
    
                        holder.essenName.setText(testessen.get(position).getName());
                        holder.essenPreis.setText(testessen.get(position).getPreis());
    
                        float test = (float) testessen.get(position).getAverage();
                        Log.i("test :" , " "+ test);
                        holder.test.setText(testessen.get(position).getAverage());
                    }
    
                    @Override
                    public void onCancelled(@NonNull DatabaseError databaseError) {
                        throw databaseError.toException(); // don't ignore errors
                    }
                });
    
                testessen.add(e);
            }
        }
    
        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            throw databaseError.toException(); // don't ignore errors
        }
    });
    

    In the above you're loading all data from bewertungenRef, while you only need a subset of these items. You're also reloading that data for every iteration, which is quite wasteful (but should work).

    Depending on how much data you have, you might want to consider loading just the child nodes from bewertungenRef that are needed. This would mean you have multiple listeners, and this is a good moment to also then only update the average in the UI once all data is loaded.

    That'd look something like this (untested, so the code may contain small problems):

    final ArrayList<essen> testessen =new ArrayList<>();
    
    essenRef.addValueEventListener(new ValueEventListener() {
    
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            float sum = (float) 0.0;
            int counter = 0;
    
            // these variables track how many items we need to load, and how many we've already loaded
            long essenCount = dataSnapshot.getChildrenCount();
            long loadedCount = 0;
    
            for (DataSnapshot ds : dataSnapshot.getChildren()){
                final essen e = new essen();
                e.name  = ds.child("name").getValue(String.class);
                e.Preis = ds.child("Preis").getValue(String.class);
    
                bewertungenRef.orderByChild("eid").equalTo(e.name).addListenerForSingleValue(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                        for (DataSnapshot ds1 : dataSnapshot.getChildren()){
                            sum = sum + ds1.child("wert").getValue(float.class);
                            counter++;
                        }
    
                        if (loadedCount++ == essenCont) {
                            float durchschnitt = sum/counter;
    
                            e.setAverage(durchschnitt);
    
                            Log.i("essen wert: " , " " + e.getAverage());
    
                            holder.essenName.setText(testessen.get(position).getName());
                            holder.essenPreis.setText(testessen.get(position).getPreis());
    
                            holder.test.setText(testessen.get(position).getAverage());
                        }
                    }
    
                    @Override
                    public void onCancelled(@NonNull DatabaseError databaseError) {
                        throw databaseError.toException(); // don't ignore errors
                    }
                });
    
                testessen.add(e);
            }
        }
    
        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            throw databaseError.toException(); // don't ignore errors
        }
    });