How to Dismiss a Progress Bar Even If There Is No View to Populate in the Firebaselistadapter

how to add progressbar for loading in Firebase RecyclerView

Because you are using FirebaseRecyclerAdapter, you can override the onDataChanged() method in your adapter class. So create a new object of ProgressBar and start showing it in the onCreate() method, or if you want, you can add it directly in your .XML file. Finally in your adapter class, use the following lines of code:

@Override
public void onDataChanged() {
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
}

FirebaseListAdapter emptyView

To solve this, override FirebaseListAdapter's getCount() method in your adapter class and check for nullity:

if(getCount() == 0) {
mEmptyView.setVisibility(View.VISIBLE);
mRecyclerView.setVisibility(View.GONE);
} else {
mEmptyView.setVisibility(View.GONE);
mAdapter = new SearchResultsAdapter(getContext(), foundRoutesList, this);
mRecyclerView.setAdapter(mAdapter);
}

How to be notified when FirebaseListAdapter finishes?

Firebase is built on the premise of synchronizing data. For this reason there is never really a moment when it is done synchronizing.

The initial set of data for a location is downloaded in one go and results in a call to onChildAdded() for each child, followed by a single onDataChanged(). If you want to hide the progress bar when this initial data has loaded, you can register a ValueEventListener for a single event:

ref.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// the initial data has been loaded, hide the progress bar
}

@Override
public void onCancelled(DatabaseError firebaseError) {

}
});

In the above snippet, all the children will have been added to your ListView already. Depending on your use-case, this may be the right time to hide the progress bar or it may be later than wanted.

If you want to know when the first data has been rendered, you can register an observer with your adapter:

adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
super.onChanged();
// the first time you get here, hide the progress bar
}

@Override
public void onInvalidated() {
super.onInvalidated();
}
});

You could use this approach to hide the progress bar as soon as the first data has arrived from Firebase.

FirebaseRecyclerAdapter with empty view

Here is what I would try. First check out the accepted answer to the question linked below. It provides some very good insight into how Firebase queries work. I'd consider the info trusted since the answer is by someone on the Firebase team:

How to separate initial data load from incremental children with Firebase?

So, based on the answer to the question linked above and the fact that the FirebaseRecyclerAdapter is backed by a FirebaseArray which is populated using a ChildEventListener I would add a Single value event listener on the same database reference used to populate your FirebaseRecyclerAdapter. Something like this:

//create database reference that will be used for both the
//FirebaseRecyclerAdapter and the single value event listener
dbRef = FirebaseDatabase.getInstance().getReference();

//setup FirebaseRecyclerAdapter
mAdapter = new FirebaseRecyclerAdapter<Model, YourViewHolder>(
Model.class, R.layout.your_layout, YourViewHolder.class, dbRef) {

@Override
public void populateViewHolder(YourViewHolder holder, Model model, int position){

//your code for populating each recycler view item

};

mRecyclerView.setAdapter(mAdapter);

//add the listener for the single value event that will function
//like a completion listener for initial data load of the FirebaseRecyclerAdapter
dbRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
//onDataChange called so remove progress bar

//make a call to dataSnapshot.hasChildren() and based
//on returned value show/hide empty view

//use helper method to add an Observer to RecyclerView
}

@Override
public void onCancelled(DatabaseError databaseError) {

}
});

That would handle the initial setup of the RecyclerView. When onDataChange is called on the single value event listener use a helper method to add an observer to the FirebaseRecyclerAdapter to handle any subsequent additions/deletions to database location.

mObserver = new RecyclerView.AdapterDataObserver() {
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
//perform check and show/hide empty view
}

@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
//perform check and show/hide empty view
}
};
mAdapter.registerAdapterDataObserver(mObserver);

Querying a FirebaseListAdapter to only return specific children to a custom view

You can construct a FirebaseListAdapter based on a DatabaseReference or based on a Query. with a DatabaseReference it will show all the data at a location. But when you use a Query you can show a subset of the data at the location.

Say that you want to show only students between ages 20 and 25:

students =    FirebaseDatabase.getInstance().getReference().child("students");
studentsQuery = students.orderByChild("age").startAt(20).endAt(25);

bikeAdapter = new FirebaseListAdapter<Student>
(getActivity(), Student.class, R.layout.list_item, studentsQuery) {

Also see the Firebase documentation on queries.

How to know that two Nested FirebaseIndexRecyclerAdapter finished data load?

I used two counters to be able to detect the end of asynchronous retrievals. Key counts are cumulatively added into totalItemCount so it holds total number of items to be loaded inside the inner adapters. When inner adapters populate a ViewHolder, populatedItemCount is incremented by one and its current value is compared with totalItemCount. If values are equal, it means all data is now retrieved and the parent RecyclerView and RecyclerViews inside its rows can be made visible.

Here is the last version of the nested adapters I provided in the question:

rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference subMenuKeyRef = rootRef.child("menu").child(menuKey).child("subMenus");
DatabaseReference subMenuRef = rootRef.child("subMenu");
subMenuAdapter = new FirebaseIndexRecyclerAdapter<MySubMenu, SubMenuHolder>(
MySubMenu.class,
R.layout.row_submenu,
SubMenuHolder.class,
subMenuKeyRef,
subMenuRef) {
int totalItemCount = 0;
int populatedItemCount = 0;

@Override
protected void populateViewHolder(SubMenuHolder viewHolder, MySubMenu model, int position) {
totalItemCount += model.getMenuItems().size();
String subMenuKey = getRef(position).getKey();
DatabaseReference menuItemKeyRef = rootRef.child("subMenu").child(subMenuKey).child("menuItems");
DatabaseReference menuItemRef = rootRef.child("menuItem");
FirebaseIndexRecyclerAdapter menuItemAdapter = new FirebaseIndexRecyclerAdapter<MyMenuItem, MenuItemHolder>(
MyMenuItem.class,
R.layout.row_menu_item,
MenuItemHolder.class,
menuItemKeyRef,
menuItemRef) {
@Override
protected void populateViewHolder(MenuItemHolder viewHolderx, MyMenuItem modelx, int positionx) {
populatedMenuItemCount++;
viewHolderx.setName(modelx.getName());
viewHolderx.setPrice(modelx.getPrice());

// TODO - Find a resolution if this if never gets true
if (populatedMenuItemCount == totalMenuItemCount) {
(new Handler()).postDelayed(new Runnable() {
@Override
public void run() {
showSubMenuList();
}
}, 500);
}
}
};
viewHolder.setName(model.getName());
viewHolder.setMenuItemList(menuItemAdapter);
}
};
subMenuList.setAdapter(subMenuAdapter);

I am still not sure about the if statement that compares two counters. Would there be a case where data retrieval somehow stops and thus the condition of the if statement never become true and app hangs?



Related Topics



Leave a reply



Submit