Lorek Bryanson Lorek Bryanson - 3 months ago 101
Android Question

Android - Display data in ListView from Firebase database

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.

Answer

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.

Comments