My Firebase Realtime Database is like this :
{Singer :
Billie Eilish :
01 :
songType : "type01"
songName : "bad guy"
02 :
songType : "type02"
songName : "bury a friend"
Lauv :
01 :
songType : "type01"
songName : "I Like Me Better"
02 :
songType : "type03"
songName : "lonely"
Anne Marie :
01 :
songType : "type02"
songName : "2002"
...
...
...
}
If I want to get all the song that "type01", what should I do? This is my Adapter class that show the data in recyclerView in MainActivity.
Adapter class :
public class MyAdapter extends Recycler.Adapter {
...
#Override
public void onBindViewHolder(#Nonnull ViewHolder holder, int i) {
...
Query query = FirebaseDatabase.getInstance().getReference().child("Singer");
options = new FirebaseRecyclerOptions.Builder<ItemModel>().setQuery(query, new SnapshotParser<ItemModel>() {
#NonNull
#Override
public ItemModel parseSnapshot(#NonNull DataSnapshot snapshot) {
if (snapshot.child("songType").getValue().toString().equals("type01") {
String song = snapshot.child("songName").getValue().toString();
return new ItemModel(song);
}
return null;
}
}).build();
recyclerAdapter = new AnotherAdapter(options);
recyclerAdapter.startListening();
}
I think it is weird in the query. It called only the child "Singer". I want to call all the child and get what songType is "type01". AnotherAdapter is just extending FirebaseReyclcerAdapter and just set the text in its AnotherViewHolder.
why don't you just go with fetch data which contains only songType : type01 try this
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Query query = reference.child("Singer").orderByChild("songType").equalTo("type01");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot issue : dataSnapshot.getChildren()) {
//set featched value to recyclerview
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Since you're passing a reference to Singers into the recycler view, your parseSnapshot is called for each singer. In there you need to make sure you check the songType of each song child of the singer, where now you check the songType of the singers themselves (which never exists, so never goes into the if block).
So it'd be something like this:
Query query = FirebaseDatabase.getInstance().getReference().child("Singer");
options = new FirebaseRecyclerOptions.Builder<ItemModel>().setQuery(query, new SnapshotParser<ItemModel>() {
#NonNull
#Override
public ItemModel parseSnapshot(#NonNull DataSnapshot snapshot) {
String song = "No song found";
for (DataSnapshot songSnapshot: snapshot.getChildren()) {
String songType = songSnapshot.child("songType").getValue(String.class)
if (songType.equals("type01") {
song = songType;
...
I'm not sure though if this is what you want, because you seem to want a list of songs, while you're passing in a list of singers. The above will only work if each singer has exactly one song of type01 (not 0, not more).
If that is not the case, the adapters in FirebaseUI won't fit your needs with your current data structure, since they show a list of items from the database, while you want to show items from a tree-like structure.
The two main options that come to mind in that case:
Build your own adapter, typically based on ArrayAdapter as shown here.
Change your data structure so that all songs are in a flat list (and the singer then becomes a property of each song).
Related
im working with firebase database. i want to get specific data after searching from "data" node. Below im sharing my database Screenshot and java function that i have already tried.
I'm working on it from two days. but i can't solve it.
please help me.
Thanks in advance.
private void getSearchedData(String selectedItem) {
reference = FirebaseDatabase.getInstance().getReference().child("Category");
Query query = reference.orderByChild("data/dataName").equalTo(selectedItem);
ArrayList<ChannelData> channelDataArrayList = new ArrayList<>();
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot requestSnapshot : snapshot.getChildren()) {
DataSnapshot channelsSnapshot = requestSnapshot.child("data");
for (DataSnapshot ds : channelsSnapshot.getChildren()) {
ChannelData channelData = new ChannelData(ds.child("dataName").getValue(String.class),
ds.child("dataImage").getValue(String.class));
channelDataArrayList.add(channelData);
searchAdapter adapter = new searchAdapter(channelDataArrayList, getApplicationContext());
rvSearchedChannel.setAdapter(adapter);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Toast.makeText(MainActivity2.this, "Error: " + error, Toast.LENGTH_SHORT).show();
}
});
}
And this is my database structure. i want to get "dataImage", "dataName" and "dataLink" after searching "dataName".
if you are using edit text for as a search box. add addTextChangedListener on edit text and you will get 3 methods on afterTextChanged method creat a function and pass it editable.tostring you get it in function.
and function code is :
FirebaseDatabase.getInstance().getReference().child("Category").child("data/dataName").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChildren())
{
list.clear();
for (DataSnapshot dss : dataSnapshot.getChildren())
{
Register register = dss.getValue(Register.class);
if (register.getUsername().toLowerCase().contains(s.toLowerCase())){
list.add(register);
}
if (Objects.equals(register.getId(), FirebaseAuth.getInstance().getCurrentUser().getUid())){
list.remove(register);
}
}
SearchUserAdapter adapter = new SearchUserAdapter(list, getContext());
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
search_rv.setLayoutManager(linearLayoutManager);
search_rv.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Queries in the Firebase Realtime Database search the direct child nodes of the path you run them at for a value at a fixed path that you specify.
In your example, the path of the value in the JSON is not fixed as you have relevant nodes in data/01/dataName and data/02/dataName, which isn't possible. Since the path that you order/filter on (data/dataName) doesn't even exist under Category/01, so you won't get any results.
If you want to allow this query on the database, you'll need to change the data structure (or add an additional one) where the values you want to order/filter on are at a fixed path under the direct child nodes of the path you query. So you'll have to lose a level of your JSON structure, but can keep the information by for example duplicating the categoryName value into each child node:
CategoryData: {
"0101": {
CategoryName: "Pakistani",
dataName: "News",
...
},
"0102": {
CategoryName: "Pakistani",
dataName: "Dramas",
...
},
"0201": {
CategoryName: "India",
dataName: "news",
...
},
"0202": {
CategoryName: "India",
dataName: "movies",
...
}
}
I am trying to query Firebase and populate a recycler adapter with conditional data from the query's DataSnapshot. I tried putting the populate function inside the if statement that correctly logs the data I want, however the recycler view instead just returns everything from the node I was searching in (the main query I started with). Any suggestions on how to just populate the items that apply to the "if" statement? Thank you!
rootRef = FirebaseDatabase.getInstance().getReference();
//below is the node i query
mAlbumQuery = rootRef.child(Constants.FIREBASE_CHILD_ALBUMS).orderByChild("genres");
mAlbumQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot reco : dataSnapshot.getChildren()) {
if (reco.getValue().toString().contains(mRecommendation.getGenre())) {
//below returns the items i want
Log.d("is this correct", reco.getValue().toString());
//below returns everything in the original query
//how to populate only items that match the above?
mAdapter = new FirebaseRecyclerAdapter<Album, AlbumsViewHolder>(
Album.class,
R.layout.album_cards,
AlbumsViewHolder.class,
mAlbumQuery) {
#Override
public void populateViewHolder(AlbumsViewHolder holder, Album album, int position) {
holder.bindView(album.getImage(), album.getTitle());
if (!album.getGenres().contains(mRecommendation.getGenre())) {
//added as a hypothetical... should i have something in here?
}
}
};
mAlbumsRecycler.setAdapter(mAdapter);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return view;
}
if you want to extract any particular node u can use this:-
String notific = String.valueOf(dataSnapshot.getValue());
int key=dataSnapshot.getKey();
String title=String.valueOf(dataSnapshot.child("title").getValue());
String content=String.valueOf(dataSnapshot.child("content").getValue());
Well, if you send mAlbumQuery as param to your FirebaseRecyclerAdapter, I believe, it takes its size as number of items.
As an option (for quick fix) you can create new collection and inside this loop:
for (DataSnapshot reco : dataSnapshot.getChildren()) {
}
you can fill that new collection with needed items.
After loop you can create new adapter and pass filtered collection to it.
Here is how I see this:
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Collection<> myNewCollection = new Collection<>(); //HashMap, ArrayList - depends on what you are storing in Firebase
for (DataSnapshot reco : dataSnapshot.getChildren()) {
if (reco.getValue().toString().contains(mRecommendation.getGenre())) {
//below returns the items i want
Log.d("is this correct", reco.getValue().toString());
//below returns everything in the original query
//how to populate only items that match the above?
myNewCollection.add(reco.getValue);
}
}
recyclerView.setAdapter(new MyRecyclerViewAdapter(myNewCollection, ...));
}
Also pls take a look at Firebase docs and this SO question.
There are interesting methods - startAt, endAt and equalTo, which might help you. I didn't find method contains, unfortunately, but methods above might be enough for you.
I am trying to get a list of items from my firebase database...but I have a problem obtaining them.
My code looks more or less like this:
List<Items> itemsList;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_work_items_report);
itemsList = GetItems();
}
and the method that should return my items looks like:
private ArrayList<Items> GetItems(){
DatabaseReference database = FirebaseDatabase.getInstance().getReference();
DatabaseReference ref = database.child("items");
final ArrayList<Items> itemsRez = new ArrayList<Items>();
Query itemsQuery = ref.orderByChild("type");
itemsQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot singleSnapshot : dataSnapshot.getChildren()) {
Items item = singleSnapshot.getValue(Items.class);
itemsRez.add(item);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return itemsRez;
}
The GetItems() always returns me a null list, which kinda makes sense, as there is nothing to fire the onDataChange event before I return this list, so my question is...how can I make this method to return the list of items from DB?
When you call addListenerForSingleValueEvent() the Firebase client starts loading the data from the server, which may take some time. To prevent blocking your app (which would lead to an Application Not Responding dialog), it loads the data in a separate thread. So while the data is loading, your main thread goes on and returns the current state of itemsRez, which is an empty list.
It's easiest to see this if you add a few logging statements to your code:
private ArrayList<Items> GetItems(){
DatabaseReference database = FirebaseDatabase.getInstance().getReference();
DatabaseReference ref = database.child("items");
final ArrayList<Items> itemsRez = new ArrayList<Items>();
Query itemsQuery = ref.orderByChild("type");
System.out.println("Attaching listener");
itemsQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot singleSnapshot : dataSnapshot.getChildren()) {
Items item = singleSnapshot.getValue(Items.class);
itemsRez.add(item);
}
}
System.out.println("Received "+itemsRez.size()+" items");
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
System.out.println("Returning "+itemsRez.size()+" items");
return itemsRez;
}
Contrary to what you likely expect, this will print the logging in this order:
Attaching listener
Returning 0 items
Received items
One common solution to your problem is to reframe the goal of you code. Instead of writing "first get the items, then do abc with them", write the code as "start getting the items. Then when they come in, do abc with them". In code that means you move the code that needs itemsRec into the onDataChange method, where it will be invoked at the right moment: when the items have loaded.
Also see:
Setting Singleton property value in Firebase Listener
Firebase/Android: Adding retrieved values from Firebase to arraylist returns null pointer exception
Wait Firebase async retrive data in android
this blog post from Doug Stevenson
I have a firebase backend for my android app. I've pulled the data I need from the backend (Names of different players) and put it in an arraylist of Strings ..For example
{"John Doe1", "John Doe2"...."John Doe15"}
I now need to populate 15 different textviews Ids with these names for my activity. What is the most efficient way to do this? Here is my code so far and here is what my activity looks like...
public void onDataChange(DataSnapshot dataSnapshot) {
ArrayList<String> al= new ArrayList<String>();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Player player = snapshot.getValue(Player.class);
String name=player.Name;
al.add(name);
}
You should place all the TextView and their desired ids into HashMap, like this:
// init object
HashMap<Integer, TexView> textViewMap = new HashMap<>();
... onCreate(...) {
....
// put value inside
TextView textView1 = (TextView) findViewById(R.id.text_view_1);
textViewMap.put(1, textView1);
...
}
Then when you get data from Firebase Database, you can use stored id you placed there to indicate which TextView the data belongs:
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
// if you use custom Player object, replace following line
Integer id = snapshot.child("id").getValue(Integer.class);
String name = snapshot.child("name").getValue(String.class);
// then get the TextView and put text in it
textViewMap.get(id).setText(name);
}
...
}
That should do it. Hope this helps.
I am trying to pull a list of friend IDs from my firebase database into my app, and then look up any additional data associated with the player (such as username, online status etc..) by looking up their entry under "users" using their unique ID
My schema looks as follows:
{friends
{
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"-KR0aTQGT6pfRfB5qIUz" : {
"friend_id" : "6vFVAAQfwiYERl03C3lzxdPjnEp2"
},
"-KR0aaMAOS3FWOAosBmo" : {
"friend_id" : "kxrQFVjGv0XUHyV5N764Nq50Q3J3"
}
}
}
}
The first unique ID is the ID of the player, which enables querying their friends list. The child objects of that represent the ID of the relationship, and the friend_id under that shows the other players (the friends) ID
The user schema looks as follows:
{
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : {
"emailAddress" : "b#b.com",
"level" : 1,
"userName" : "steve"
},
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"emailAddress" : "a#a.com",
"level" : 1,
"userName" : "bob"
},
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : {
"emailAddress" : "bg#b.com",
"level" : 1,
"userName" : "tim"
},
"rNtYvwF8LBhTRM1Wk8ybBJyrFIg2" : {
"emailAddress" : "c#c.com",
"level" : 1,
"userName" : "test account"
}
}
Now, in my app, I can successfully pull all of the friend_id entries, but am not sure how to then turn around and pull additional information on the friend by using this ID. Ideally I would be able to query each friend one by one by their unique player ID, and populate the friends list fragment I have using a firebaseListAdapter.
This is how I am pulling the friend IDs and populating the list object.
ValueEventListener friendListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
friendIdList.clear();
for (DataSnapshot messageSnapshot: dataSnapshot.getChildren()) {
String friend_id = (String) messageSnapshot.child("friend_id").getValue();
friendIdList.add(friend_id);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
mDatabase.addValueEventListener(friendListener);
Could anybody help me figure out the best way to pull all of this additional information on the friends once the IDs are received from the initial query?
You can use ChildEventListener to get all friends then on your populateview or onBindViewHolder use ValueEventListener to get all data of a friend. also try to use FirebaseRecyclerView instead of FirebaseListView
Yes agreed even I have same confusion.
What are we struggling is with Contains[*, *, *] query.
Even I have a similar problem
/Users{
"uuid1":{
data1:"value1"
data2:"value2"
}
"uuid2":{
data1:"value1"
data2:"value2"
}
}
/Friends:{
uuid1:{
"uuid2":true
....
}
uuid2:{
"uuid1":true
...
}
}
My Query is how to Query list of only my friends and present their data("/User") with FirebaseRecyclerViewAdapter.
I'd suggest a different data structure. Here it is based on your data:
{
friends : {
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : true,
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : true
}
}
}
Then loop over each one of them and pull up their individual profiles.
So I'm just going to connect the answers given by Mathew Berg and pastillas. Based on your question and comments I think combined they provide the solution you are looking for.
Data Structure
The structure you are using for your users location looks good to me so I'd say you can leave that as is:
{
users: {
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : {
"emailAddress" : "b#b.com",
"level" : 1,
"userName" : "steve"
},
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"emailAddress" : "a#a.com",
"level" : 1,
"userName" : "bob"
}
}
}
For you friends location I agree with the structure given by Mathew Berg:
{
friends : {
"Ko2D1of4KxXHzX0OqEZEAKDfw4r2" : {
"6vFVAAQfwiYERl03C3lzxdPjnEp2" : true,
"kxrQFVjGv0XUHyV5N764Nq50Q3J3" : true
}
}
}
Just a quick FYI, you don't need to use a Boolean value with this structure. You can use any allowed data type as the value for each key. In this instance the key is what's important, the value is just a place holder because Firebase doesn't allow keys with null values. That said if you find a reason to use a value that is more useful you could do that. In my opinion using a Boolean value makes the structure a little more readable by team members or someone who may follow behind you.
Retrieving Your Data
There are multiple ways you can query a friends list and get the data for each user. Since you referred to using a FirebaseListAdapter in your question I'll go with that. The same pattern can be used with a FirebaseRecyclerAdapter if you want to use choose to use a RecyclerView with FirebaseUI instead. The basic steps are:
Create a DatabaseReference that points to the location of the user
who's friends list you want.
Create a DatabaseReference that points to your users location
Create your FirebaseListAdapter.
When you override populateView get the key for each item
Use a addListenersingleValueEvent() to retrieve each friends user information and populate your listview row items with the data from
this query.
Within onDataChange of value event listener use the info for each user to populate views for each list item.
DatabaseReference mFriendsRef = FirebaseDatabase.getInstance().getReference().child("friends").child(someUserID;
DatabaseReference mUsersRef = FirebaseDatabase.getInstance().getReference().child("users");
...
mAdapter = new FirebaseListAdapter<YourModel>(this, YourModel.class, android.R.your_layout, mFriendsRef) {
#Override
protected void populateView(final View view, YourModel model, int position) {
String key = getRef(position).getKey();
mUsersRef.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//get data from dataSnapshot and bind to views for each list item
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
messagesView.setAdapter(mAdapter);
Check out the comment made on this question for concerns about performance when using this approach: Android Firebase - Denormalized Queries using FirebaseUI
Sorry sir, I dont know if it will work on FirebaseUI but here is my code that I think has the same problem and also same solution, it will display the list of a user followers then if a follower clicked it will open and pass the id of item that is clicked to other activity. I did not use FirebaseUI.
public class FollowersAdapter extends RecyclerView.Adapter<FollowersAdapter.FollowersHolder>{
public ArrayList<Follower> followers;
public void addFollower(Follower follower) {
followers.add(follower);
notifyItemInserted(getItemCount());
}
public FollowersAdapter(Context context) {
this.followers = new ArrayList<>();
mDatabase = FirebaseDatabase.getInstance().getReference();
this.fUser = FirebaseAuth.getInstance().getCurrentUser();
mDatabase.child("follower").child(fUser.getUid()).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Follower follower = dataSnapshot.getValue(Follower.class);
addFollower(follower);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Follower follower = dataSnapshot.getValue(Follower.class);
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public FollowersHolder onCreateViewHolder(ViewGroup parent, int position) {
View view = inflater.inflate(R.layout.followers_model, parent, false);
FollowersHolder holder = new FollowersHolder(view);
return holder;
}
#Override
public void onBindViewHolder(final FollowersHolder holder, final int position) {
Query userQuery = mDatabase.child("users").child(followers.get(position).getId());
userQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
user = dataSnapshot.getValue(User.class);
holder.name.setText(user.getName());
final Bundle extras = new Bundle();
extras.putString(EXTRAS_POSTER_ID, user.getId());
holder.cont.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!fUser.getUid().equals(user.getId())) {
Intent intent = new Intent(context, ViewUserProfile.class);
intent.putExtra(EXTRAS_BUNDLE, extras);
context.startActivity(intent);
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public int getItemCount() {
return followers.size();
}
class FollowersHolder extends RecyclerView.ViewHolder {
View cont;
TextView name;
public FollowersHolder(View itemView) {
super(itemView);
cont = itemView.findViewById(R.id.cont_followers);
name = (TextView) itemView.findViewById(R.id.tv_followers_name);
}
}
}
you get the data using childEventListener then on the onBindViewHolder or populateView add onClickListener and pass the id when it clicked to other activity. sorry sir im a beginner lol :)