0

I have a custom listview to display data from Firebase database.

SectionDetails model

public class SectionDetails {
    private String sectionCode;
    private String sectionSeats;

public SectionDetails() {
}

public SectionDetails(String sectionCode, String sectionSeats) {
    this.sectionCode = sectionCode;
    this.sectionSeats = sectionSeats;
}

public String getSectionCode() {
    return sectionCode;
}

public String getSectionSeats() {
    return sectionSeats;
}

public void setSectionCode(String sectionCode) {
    this.sectionCode = sectionCode;
}

public void setSectionSeats(String sectionSeats) {
    this.sectionSeats = sectionSeats;
}
}

FirebaseHelper class

public class FirebaseHelper {
        DatabaseReference db;
        Boolean saved;
        ArrayList<SectionDetails> sectionDetailsArrayList = new ArrayList<>();

    public FirebaseHelper(DatabaseReference db) {
        this.db = db;
    }

    public Boolean save(SectionDetails sectionDetails) {

        if (sectionDetails == null) {
            saved = false;
        } else {
            try {
                db.push().setValue(sectionDetails);
                saved = true;

                adapter.notifyDataSetChanged();

            } catch(DatabaseException e) {
                e.printStackTrace();
                saved = false;
            }
        }

        return saved;
    }

    private void fetchData(DataSnapshot dataSnapshot) {
        sectionDetailsArrayList.clear();

        SectionDetails sectionDetails = dataSnapshot.getValue(SectionDetails.class);
        sectionDetailsArrayList.add(sectionDetails);
    }

    public ArrayList<SectionDetails> retrieve() {
        db.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                fetchData(dataSnapshot);
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                fetchData(dataSnapshot);
            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

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

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
        return sectionDetailsArrayList;
    }
}

CustomAdapter class

public class CustomAdapter extends BaseAdapter {
        Context c;
        ArrayList<SectionDetails> sectionDetailsArrayList;

    public CustomAdapter(Context c, ArrayList<SectionDetails> sectionDetailsArrayList) {
        this.c = c;
        this.sectionDetailsArrayList = sectionDetailsArrayList;
    }

    @Override
    public int getCount() {
        return sectionDetailsArrayList.size();
    }

    @Override
    public Object getItem(int position) {
        return sectionDetailsArrayList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(c).inflate(R.layout.sections_custom_listview, parent, false);
        }

        TextView lvTvSectionCode = (TextView) convertView.findViewById(R.id.lvTvSectionCode);
        TextView lvTvSectionSeats = (TextView) convertView.findViewById(R.id.lvTvSectionSeats);

        final SectionDetails sd = (SectionDetails) this.getItem(position);

        lvTvSectionCode.setText(sd.getSectionCode());
        lvTvSectionSeats.setText("allocated seats: " + sd.getSectionSeats());

        convertView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(c, sd.getSectionCode(), Toast.LENGTH_LONG).show();
            }
        });

        return convertView;
    }
}

MainActivity class

public class AddSection extends AppCompatActivity implements View.OnClickListener {

DatabaseReference mRef;
FirebaseHelper helper;
CustomAdapter adapter;
Button btnCreateSection;
ListView lvSectionsListOne;
String getFullSection, seats;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_add_section);

    lvSectionsListOne = (ListView) findViewById(R.id.lvSectionsList);

    mRef = FirebaseDatabase.getInstance().getReference().child(Constants.FIREBASE_COURSES).child("sections");
    helper = new FirebaseHelper(mRef);

    adapter = new CustomAdapter(this, helper.retrieve());
    lvSectionsListOne.setAdapter(adapter);

    btnCreateSection = (Button) findViewById(R.id.btnCreateSection);
    btnCreateSection.setOnClickListener(this);

}

@Override
public void onClick(View v) {
    if (v == btnCreateSection) {

        getFullSection = "section 1A";
        seats = "20";

        SectionDetails sectionDetails = new SectionDetails(getFullSection, seats);

            if (helper.save(sectionDetails)) {

                adapter = new CustomAdapter(AddSection.this, helper.retrieve());
                lvSectionsListOne.setAdapter(adapter);

                adapter.notifyDataSetChanged();

            }
    }
}
}

I posted a similar question here, but this problem is different. When first data is added, nothing is shown in the listview. When second data is added, first data is shown in listview. And when third data is added, first data is replaced by second data, and second data is shown in listview. I have tried adding adapter.notifyDataSetChanged(), but still the same result.

Community
  • 1
  • 1
Lorek Bryanson
  • 195
  • 1
  • 7
  • 22
  • How about in database, are children added as expected or they are being replaced? – niraj Aug 10 '16 at 17:12
  • Hi again @openSource :) Children are added as expected – Lorek Bryanson Aug 10 '16 at 17:29
  • I think the problem here is it is not waiting enough until the Firebase retrieves data, so most probably there is issue with retrieve() method in the helper class as similar to http://stackoverflow.com/questions/30659569/wait-until-firebase-retrieves-data – niraj Aug 10 '16 at 17:31
  • Strange. But data is retrieved and shown when new data is added. – Lorek Bryanson Aug 10 '16 at 17:41
  • 1
    My suggestion would be to update the list used by adapter within the onChildAdded and onChildChanged and call notify adapter method in each child method. Also, in fetchData, the list is being cleared, so wouldn't it always have one item whether it is changed or added DataSnapshot? – niraj Aug 10 '16 at 17:46

1 Answers1

3

Data is retrieved (and synchronized) from the Firebase Database to your app asynchronously. This means that your sectionDetailsArrayList may be modified at any time. When you modify the data, you need to tell the adapter about it, so that it can update the view.

private void fetchData(DataSnapshot dataSnapshot) {
    sectionDetailsArrayList.clear();

    SectionDetails sectionDetails = dataSnapshot.getValue(SectionDetails.class);
    sectionDetailsArrayList.add(sectionDetails);

    // tell the adapter that we changed its data
    adapter.notifyDataSetChanged();
}

This should update the view. But since you clear out the list for every child that gets added or changed, the app will only show the latest added/modified item.

Setting up a synchronized array in Firebase is somewhat involved, since you have to deal with all event types and with the fact that evens can happen in any order. For that reason we create the FirebaseUI library, which contains adapters from the Firebase Database to a ListView and RecyclerView.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807