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
Drawerlayout Double Drawer (Left and Right Drawers Simultaneously)
How to Get Access Token After User Is Signed in from Gmail in Android
Android Broadcast Receiver Bluetooth Events Catching
Ics Android Enable Gps Programmatically
How to Use Adb to Send Touch Events to Device Using Sendevent Command
How to Detect Whether the Android Phone in Silent Mode Programmatically
Why Is My Smallicon for Notifications Always Greyed Out
Android Youtube Upload Video with Static Username and Password
Google Firebase Sign Out and Forget User in Android App
How to Put Media Controller Button on Notification Bar
How to Create a Custom Notification Layout in Android
Get Context of Popupmenu Like Contextmenu
Android Gradle Apache Httpclient Does Not Exist
How to Keep My Android Service Running When the Screen Is Turned Off