I am new to Android Programming. I want to list all child lists from Firebase in Android.
I wrote this code. But When I start the application, the listview returns null.
What am doing wrong?
public class haber_goruntule extends AppCompatActivity {
private ListView liste;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_haber_goruntule);
final List<connector_haber> mesajlar=new ArrayList<connector_haber>();
liste= (ListView) findViewById(R.id.list);
FirebaseDatabase firebaseDatabase=FirebaseDatabase.getInstance();
DatabaseReference ref=firebaseDatabase.getReference();
ref.child("haberler").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> children=dataSnapshot.getChildren();
for(DataSnapshot child : children){
connector_haber haber=child.getValue(connector_haber.class);
mesajlar.add(haber);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
final ArrayAdapter<connector_haber> arrayAdapter=new ArrayAdapter<connector_haber>(this,android.R.layout.simple_list_item_1,mesajlar);
liste.setAdapter(arrayAdapter);
}
}
The data is loaded from Firebase asynchronously. By the time it comes back, you're adding it to the adapter, but Android isn't aware of that anymore. To make it aware, call notifyDataSetChanged() after you've added the data:
final ArrayAdapter<connector_haber> arrayAdapter=new ArrayAdapter<connector_haber>(this,android.R.layout.simple_list_item_1,mesajlar);
liste.setAdapter(arrayAdapter);
ref.child("haberler").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> children=dataSnapshot.getChildren();
for(DataSnapshot child : children){
connector_haber haber=child.getValue(connector_haber.class);
mesajlar.add(haber);
}
adapter.notifyDataSetChanged(); // tell the adapter to update the view
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore this error
}
});
Try this :
ref.child("haberler").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> children=dataSnapshot.getChildren();
for(DataSnapshot child : children){
connector_haber haber=child.getValue(connector_haber.class);
mesajlar.add(haber);
}
final ArrayAdapter<connector_haber> arrayAdapter=new ArrayAdapter<connector_haber>(this,android.R.layout.simple_list_item_1,mesajlar);
liste.setAdapter(arrayAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore this error
}
});
well.. there is an open source library for implementing UI with firebase. Try this one FirebaseUI
Related
I need to get the nested children (See Photo) from firebase. I would like to show a recyclerview that is only from a particular user. Below is what I have so far, but this is giving me all of the children.
DatabaseReference notificationInviteRef = FirebaseDatabase.getInstance().getReference().child(Strings.InvitesReference);
notificationInviteRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot1) {
ArrayList<Model_Notifications_Invite> model_notification_invites = new ArrayList<>();
for (DataSnapshot snapshot2:snapshot1.getChildren()) {
Model_Notifications_Invite invites = snapshot2.getValue(Model_Notifications_Invite.class);
model_notifications_invites.add(invites);
}
myNotificationsAdapter.updateNotificationsList(model_notifications_invites);
}
#Override
public void onCancelled(DatabaseError databaseError) {
//throw databaseError.toException();
}
});
If I understand correctly, you have two unknown levels in your data structure, which means you need two nested loops in your onDataChange: one for each level.
DatabaseReference notificationInviteRef = FirebaseDatabase.getInstance().getReference().child(Strings.InvitesReference);
notificationInviteRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot1) {
ArrayList<Model_Notifications_Invite> model_notification_invites = new ArrayList<>();
for (DataSnapshot snapshot2:snapshot1.getChildren()) {
for (DataSnapshot snapshot3:snapshot2.getChildren()) {
Model_Notifications_Invite invites = snapshot3.getValue(Model_Notifications_Invite.class);
model_notifications_invites.add(invites);
}
}
myNotificationsAdapter.updateNotificationsList(model_notifications_invites);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // 👈 never ignore errors
}
});
I use groupie lib to show items that I fetch from the firebase database. But when I want to do setOnItemClickListener in Groupie adapter, nothing happens
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("/users");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
adapter = new GroupAdapter();
for (DataSnapshot ds:dataSnapshot.getChildren()) {
Userinfo user = ds.getValue(Userinfo.class);
if(user !=null)
adapter.add(new ContactItem(user));
}
newmessage_recyclerview.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
newmessage_recyclerview.addItemDecoration(new DividerItemDecoration(getApplicationContext(),DividerItemDecoration.VERTICAL));
newmessage_recyclerview.setAdapter(adapter);
adapter.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(#NonNull Item item, #NonNull View view) {
Log.d("main", String.valueOf(item.getId()));
}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Your adapter and listview should be initialized and assigned outside onDataChange() method, as you can see, each time your data from firebase database change, the adapter will be changed and the listview is set up repeatly, it's redundant. Try this.
//set up adapter and listview one time only
adapter = new GroupAdapter();
newmessage_recyclerview.setLayoutManager(new
LinearLayoutManager(getApplicationContext()));
newmessage_recyclerview.addItemDecoration(new
DividerItemDecoration(getApplicationContext(),DividerItemDecoration.VERTICAL));
newmessage_recyclerview.setAdapter(adapter);
adapter.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(#NonNull Item item, #NonNull View view) {
Log.d("main", String.valueOf(item.getId()));
}
});
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("/users");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot ds:dataSnapshot.getChildren()) {
Userinfo user = ds.getValue(Userinfo.class);
if(user !=null)
adapter.add(new ContactItem(user));
adapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
I'm trying to retrieve nested data from multiple records and display them in a ListView. My data is recorded like so:
I want to display the GroupNumber and the GroupVaccinationDate of all GroupVaccinations recorded in a listView but at the moment the listView remains empty when I run the following code:
listView = (ListView)findViewById(R.id.listItemAllGroupVaccinations);
databaseReference = FirebaseDatabase.getInstance().getReference("groupVaccinations");
groupVaccinations = new ArrayList<>();
protected void onStart(){
super.onStart();
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot groupVaccinationSnapshot : dataSnapshot.getChildren()){
GroupVaccination groupVaccination = groupVaccinationSnapshot.getValue(GroupVaccination.class);
groupVaccinations.add(groupVaccination);
}
AllGroupVaccinationList groupVaccinationInfoAdapter = new AllGroupVaccinationList(ActivityAllGroupVaccinations.this, groupVaccinations);
listView.setAdapter(groupVaccinationInfoAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Any help or suggestions would be greatly appreciated!
Gaston's answer will show you all vaccinations in a single group. If instead you want to show all vaccinations in all groups, you can loop over the nested snapshots in your onDataChange:
databaseReference = FirebaseDatabase.getInstance().getReference("groupVaccinations");
groupVaccinations = new ArrayList<>();
AllGroupVaccinationList groupVaccinationInfoAdapter = new AllGroupVaccinationList(ActivityAllGroupVaccinations.this, groupVaccinations);
listView.setAdapter(groupVaccinationInfoAdapter);
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot groupVaccinationSnapshot : dataSnapshot.getChildren()){
for(DataSnapshot vaccinationSnapshot : groupVaccinationSnapshot.getChildren()){
GroupVaccination groupVaccination = vaccinationSnapshot.getValue(GroupVaccination.class);
groupVaccinations.add(groupVaccination);
}
}
groupVaccinationInfoAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
I also changed the code to call groupVaccinationInfoAdapter.notifyDataSetChanged(), so that you can keep using the same adapter and update it if needed.
You will need to go one level deeper where your id -LZk5tpB5PXqHVJkj8Vz (GroupID) contains all the sub pushKeys (groupVaccinationID) with data inside.
listView = (ListView)findViewById(R.id.listItemAllGroupVaccinations);
databaseReference = FirebaseDatabase.getInstance().getReference("groupVaccinations").child("-LZk5tpB5PXqHVJkj8Vz");
groupVaccinations = new ArrayList<>();
protected void onStart(){
super.onStart();
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot groupVaccinationSnapshot : dataSnapshot.getChildren()){
GroupVaccination groupVaccination = groupVaccinationSnapshot.getValue(GroupVaccination.class);
groupVaccinations.add(groupVaccination);
}
AllGroupVaccinationList groupVaccinationInfoAdapter = new AllGroupVaccinationList(ActivityAllGroupVaccinations.this, groupVaccinations);
listView.setAdapter(groupVaccinationInfoAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
EDIT
The -LZk5tpB5PXqHVJkj8Vz child should be avoided since is hardcoded to show you this example, instead, you should take that child key from where you get your GroupID, doing that you can get as Frank says all the vaccinations from each GroupID.
I'm facin a really weird problem, Im calling two nodes in my database , from one node I get all my user ids , then I just loop inside my user node to get the user metadata. The problem is that when I run the debugger, it seems like the mAdapter is beign called before the data is beign fetched. So my adapter set the recyclerview with no data. After the adapter has been called, my query fetching the user begins and the array is populated normally
This is what I have done so far
mDatabase.child("teamsNode").child(teamID).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot : dataSnapshot.getChildren()){
mDatabase.child("users").child(snapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
UserModel model = dataSnapshot.getValue(UserModel.class);
String playerName = model.getName();
model.setName(playerName);
mArrayPlayers.add(model);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) { }
});
}
mAdapter = new PlayersAdapter(mArrayPlayers,mContext,R.layout.recycler_row);
if(mAdapter.getItemCount()>0)
mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) { }
});
As you can see, I set the adapter to the recyclerview after the first foreach that fetchs all the data, I know that beign asynchronous I need to wait for the inner mDatabase to be done but if I move the setting of the adapter one line above to wait for the second call to finish it does the same, any idea?
Database structure is like this
teamsNode
|__Jm2KSMjslpow2Ipoasz : true
|__601KSMjsldfjd2Ipos0 : true
|__asgm2Kshalpow2IposJ : true
users
|__Jm2KSMjslpow2Ipoasz
|_____name: randomname1
|__601KSMjsldfjd2Ipos0
|_____name: randomname2
|__asgm2Kshalpow2IposJ
|_____name: randomname3
Recyclerview is not populated. Any idea?
A weird thing is that sometimes runing it with the debugger does fill the data, but its rarely to happend.
If you want to only initialize the adapter when all data is loaded, you will need to do so in the nested onDataChange callback:
mDatabase.child("teamsNode").child(teamID).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshotTeam) {
AtomicInteger loadedUserCount = new AtomicInteger(0);
for(DataSnapshot snapshot : dataSnapshotTeam.getChildren()){
mDatabase.child("users").child(snapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
UserModel model = dataSnapshot.getValue(UserModel.class);
String playerName = model.getName();
model.setName(playerName);
mArrayPlayers.add(model);
if (loadedUserCount.getAndIncrement() + 1 == dataSnapshotTeam.getChildrenCount()) {
mAdapter = new PlayersAdapter(mArrayPlayers,mContext,R.layout.recycler_row);
if(mAdapter.getItemCount()>0) {
mRecyclerView.setAdapter(mAdapter);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
The problem you have is that the calls to the addListenerForSingleValueEvent methods are asynchronous, so they are executed in another thread. When you call the second addListenerForSingleValueEvent this is executed in another thread, while the main thread follows its execution. You have to do the call of your adapter in the answer of your second call addListenerForSingleValueEvent in the onDataChange method. In this way to avoid anomalous operations of your application.
Hope this will help.
mDatabase.child("teamsNode").child(teamID).addListenerForSingleValueEvent(new ValueEventListener() {
mArrayPalyer.clear();//add this
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot : dataSnapshot.getChildren()){
mDatabase.child("users").child(snapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
UserModel model = dataSnapshot.getValue(UserModel.class);
String playerName = model.getName();
model.setName(playerName);
mArrayPlayers.add(model);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
mAdapter = new PlayersAdapter(mArrayPlayers,mContext,R.layout.recycler_row);
mRecyclerView.setAdapter(mAdapter); //makes changes here because you are checking for adapter item count before setting it and this always return null.
}
You can look at my code I am using ValueEventListener just like you and binding the data to recycler view
ValueEventListener postListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()){
UserListModel userListModel =dataSnapshot1.getValue(UserListModel.class);
userModelArrayList.add(userListModel);
}
userListAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(">>value", "loadPost:onCancelled", databaseError.toException());
}
};
//mDatabase is the reference to the Firebase Node
mDatabase.addValueEventListener(postListener);
userListRecyclerView.setLayoutManager(M.gridLayoutRecyclerView(getActivity() , 2));
userListAdapter = new UserListAdapter(getActivity(), userModelArrayList);
userListRecyclerView.setAdapter(userListAdapter);
This loop is fetching 1 key at a time. I want to store all the keys in String[] basically at the end of loop and then use it in ArrayAdapter for auto complete text view.
fdatabaseuser.addValueEventListener(new ValueEventListener({
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String item="";
for (DataSnapshot list: dataSnapshot.getChildren())
{
Object obj= list.getKey();
item=obj.toString();
}
Toast.makeText(GiveActivity.this,item,Toast.LENGTH_LONG).show();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Actually you already have the keys. You can create an ArrayList and add items.
ArrayList<String> keys = new ArrayList<>();
fdatabaseuser.addValueEventListener(new ValueEventListener({
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String item="";
for (DataSnapshot list: dataSnapshot.getChildren())
{
Object obj= list.getKey();
item=obj.toString();
keys.add(item);
}
Toast.makeText(GiveActivity.this,item,Toast.LENGTH_LONG).show();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
List<String> item;
Initialise list in the OnCreate Method
item=new ArrayList<>();
Then change this code:
fdatabaseuser.addValueEventListener(new ValueEventListener({
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot list: dataSnapshot.getChildren())
{
Object obj= list.getKey();
item.add(obj.toString();)
}
Toast.makeText(GiveActivity.this,item,Toast.LENGTH_LONG).show();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
To achieve this, please use the following code:
fdatabaseuser.addValueEventListener(new ValueEventListener({
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> list = new ArrayList<>();;
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String key = ds.getKey();
list.add(key);
}
Log.d("TAG", list);
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
Remember, the list must be declared and updated inside the onDataChange() method, otherwise it will be null, due the asynchronous behaviour of onDataChange() which is called even before you are adding those keys to the list.