I am new to Android and just started to understand some concepts like the RecyclerView. I am using Firebase as a database so I implemented the Firebase solution for that.
My Adapter:
FirebaseRecyclerAdapter<Offer,OfferViewHolder> adapter = new FirebaseRecyclerAdapter<Offer, OfferViewHolder>(
Offer.class,
R.layout.card_item,
OfferViewHolder.class,
mDatabaseReference.child("offer").getRef()
) {
#Override
protected void populateViewHolder(OfferViewHolder viewHolder, Offer model, int position) {
if(tvNoMovies.getVisibility()== View.VISIBLE){
tvNoMovies.setVisibility(View.GONE);
}
viewHolder.tvHeading.setText(model.getHeader());
viewHolder.tvStoreName.setText(model.getStoreName());
}
};
Now I have two questions:
Is it possible to filter results inside the adapter or do i have to use ChildEventListeners for that?
When referencing to a key that contains an Array or an object itself how to retrieve child values?
You can initialize the FirebaseRecyclerAdapter with either a DatabaseReference or a Query. As its name implies, the latter allows you to get a subset of the children at a specific location in the database.
For example say if you want to only show offers from a specific store:
DatabaseReference offers = mDatabaseReference.child("offer").getRef();
Query storeOffers = offers.orderByChild("storeName").equalTo("A. lazzi's store");
FirebaseRecyclerAdapter<Offer,OfferViewHolder> adapter = new FirebaseRecyclerAdapter<Offer, OfferViewHolder>(
Offer.class,
R.layout.card_item,
OfferViewHolder.class,
storeOffers
) {
Related
I want to search data from firebase. whenever a user type anything in the searchview it will search the related data. For example if user input the name of company it should show data related to that company, OR if user input price it should show data related to that specific price. My Recyclerview is working fine. I'm getting data but it only search data according to company name.
Here is My Code
private void getData(String query) {
Query firebaseSearchQuery = myRef.orderByChild("companyName").startAt(query).endAt("\uf8ff");
FirebaseRecyclerOptions<Package> options = new FirebaseRecyclerOptions
.Builder<Package>().setQuery(firebaseSearchQuery, Package.class).build();
final FirebaseRecyclerAdapter<Package, PackagesViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<Package, PackagesViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull PackagesViewHolder packagesViewHolder, int i, #NonNull Package aPackage){
packagesViewHolder.setDetails(getApplicationContext(), packagesViewHolder, i, aPackage.getPackageName()/*, aPackage.getPackageType()*/, aPackage.getCompanyName(), aPackage.getData()
, aPackage.getOnNetMinutes(), aPackage.getOffNetMinutes(), aPackage.getSms()
, aPackage.getPrice(), aPackage.getValidity()
, aPackage.getSubscriptionCode(), aPackage.getUnsubCode()
, aPackage.getRemainingDataCode(), aPackage.getInfo()
/*, aPackage.getTermsAndConditions()*/);
}
#NonNull
#Override
public PackagesViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_layout, parent, false);
PackagesViewHolder packagesViewHolder = new PackagesViewHolder(view, SearchActivity.this);
return packagesViewHolder;
}
};
recyclerView.setAdapter(firebaseRecyclerAdapter);
firebaseRecyclerAdapter.startListening();
}
There is no built-in operator on Firebase to search across all properties of a node. If your app requires such functionality, you might want to consider using a different/additional database for that, such as using Algolia for the searches and Firebase for the realtime sync.
Alternatively, you can build your own search structure inside of Firebase, as I've shown in my answer here: How to query based on multiple conditions in Firebase?
Also see:
Google Firestore: Query on substring of a property value (text search)
How to query firebase for property with specific value inside all children
How to perform sql "LIKE" operation on firebase?
Angularfire: How to access an item by one of it's properties?
You need to add an extra attribute to the database for example:
price-name : "18 comp"
And then do :
Query firebaseQuery = myRef.orderByChild("price-name").startAt(query).endAt("\uf8ff");
There is no or clause in firebase Realtime Database. The above is an alternative to be able to achieve that.
I can query Firestore and get IDs manually, but I'm using FirestoreRecyclerAdapter because it comes with a ton of additional work done out of the box. So, if I have code like this:
FirebaseFirestore db = FirebaseFirestore.getInstance();
Query query = db.collection(COLLECTION_NAME)
.whereEqualTo("userId", USER_ID)
.limit(LIMIT);
FirestoreRecyclerOptions options = new FirestoreRecyclerOptions
.Builder<SomeEntity>()
.setQuery(query, SomeEntity.class)
.build();
FirestoreRecyclerAdapter adapter = new FirestoreRecyclerAdapter<SomeEntity, MyHolder>(options) {
#Override
public void onBindViewHolder(MyHolder holder, int position, SomeEntity model) {
// I need model ID here
}
}
So, if I use FirestoreRecyclerAdapter, it automatically deserializes my SomeEntity class into model object. This is all fine and dandy, but now I need to attach listeners to the list, and react with model ID. Now, if I was manually deserializing objects from Firestore I'd do something like this:
for (DocumentSnapshot document : task.getResult()) {
SomeEntity entity = document.toObject(SomeEntity.class).setId(document.getId());
list.add(entity);
}
But, since FirestoreRecyclerAdapter is doing deserialisation for me, I don't get to call document.getId() myself. And by the time I'm in onBindViewHolder method, I don't have access to document ID anymore. Is there some method I'm missing, some way to retrieve IDs from adapter that I overlooked?
Note, I do not consider redundantly storing IDs as a field a solution. I will rather inherit and override FirestoreRecyclerAdapter instead, but I'd prefer if I could solve this without that much work.
When you need the document ID, you should know something already about the item you're dealing with. Typically this will be the position of the item in the list. I.e. when the user clicks on an item, the click handler is passed the ID of that item. If you have the position, you can get the DataSnapshot from the adapter with:
adapter.getSnapshots().getSnapshot(position);
So if you want to get the ID, you'd get it with:
String id = adapter.getSnapshots().getSnapshot(position).getId();
holder.itemView.setOnClickListener { v ->
Toast.makeText(v.context, snapshots.getSnapshot(position).id , Toast.LENGTH_SHORT).show()
}
This comes Under BindviewHolder
Use this in OnBindViewHolder
String DocID = Objects.requireNonNull (getCurrentList ()).snapshot ().get (position).getId ();
If you are using items.class you can create a string with with getter and #DocumentID.
private String docID;
#DocumentId
public String getDocID() {
return docID;
}
Then call this in onBindViewHolder
protected void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position, #NonNull #NotNull Items model) {
model.getDocId();
}
I am using firebase recyclerview to populate my data and retrieving those data from firebase realtime database. I don't want to retrieve all of my data but with a condition that is if "Available == yes " then show me the data in recyclerview. My condition is working but the problem is I have 4 data set on firebase realtime database. From them in 3 set value of available is yes .So, I supposed to get 3 list of data but I am getting all 4 set. 3 set data has actual value and 1 set taking the dummy one. How can I stop that?
FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder>(
BloodModelSchema.class,
R.layout.search_card,
UserViewHolder.class,
databaseReference
) {
#Override
protected void populateViewHolder(UserViewHolder viewHolder, BloodModelSchema model, int position) {
if(model.getAvailable().equals("Yes"))
{
viewHolder.setDetails(model.getBlood_Group(),model.getName(),
model.getArea(),model.getMobile(),
model.getEmail());
}
}
};
recyclerView.setAdapter(firebaseRecyclerAdapter);
firebaseRecyclerAdapter.notifyDataSetChanged();
If you have one databaseReference just add .limitToFirst(3)
FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<BloodModelSchema, UserViewHolder>(
BloodModelSchema.class,
R.layout.search_card,
UserViewHolder.class,
databaseReference.limitToFirst(3))
) { .....
But you can create second databaseReference2 and limit it to 4.
For different cases use two different adapters with different limitations. Good luck!
Is it possible to specify the which data is read by the FirebaseListAdapter?
At the moment, I could only read the complete database. The database holds a lot of different objects. The object has a value which represents the User who has created the object.
Now, I only want to read the objects from this special user. When I try it like this, there are empty list field.
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReferenceFromUrl("URL");
//HOW CAN I DO THE QUERY FOR THAT DATABASE REFERENCE?
FirebaseListAdapter<Machine> firebaseListAdapter = new FirebaseListAdapter<Machine>(
this,
Machine.class,
R.layout.list_item,
databaseReference) {
#Override
protected void populateView(View v, final Machine model, int position) {
//CAN I DO THIS BY QUERY?
if(model.getS_UserID.equals(user.getID)){
//SOME STUFF WHICH I DO
}
};
}
list.setAdapter(firebaseListAdapter);
The question is, can I do the If-Statement by doing a query for the FirebaseListAdapter?
At the moment, I do something like this:
if(model.getS_UserID.equals(user.getID()) --> DO SOME STUFF
But it also creates some empty fields which are really ugly.
My Database looks like this:
OBJECT
-USERID = STRING
-NAME = ...
-.....
You can pass in a Query to the FirebaseListAdapter instead of the database reference you use right now. To filter on items with the user ID:
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReferenceFromUrl("URL");
Query query = databaseReference.orderByChild("s_UserId").equalTo(user.getID);
FirebaseListAdapter<Machine> firebaseListAdapter = new FirebaseListAdapter<Machine>(
this,
Machine.class,
R.layout.list_item,
query) {
Read more about this in the Firebase documentation on ordering and filtering data.
I'm currently using FirebaseRecyclerAdapter to retrieve data from a Firebase database, and I've pretty much worked out how to retrieve the data in the order I desire.
To avoid duplication and ease database maintenance, I'd like to add a key to a database entry that allows me to return queries based on that key. I was originally storing data twice. Once for all to see, and once if a user had joined a certain group (under groupName).
To return a query based on group, my original search was as follows:
databaseReference.child(groupName).child("exerciseId"+mExerciseId).orderByChild(sortOrder).limitToFirst(100);
but I believe duplication can be avoided by adding the key "group" to my post. (it also make maintenance much easier as users switch groups).
The "group" database query has now become:
databaseReference.child("exerciseId"+mExerciseId).orderByChild("group").equalTo(groupName);
All is good, except that the data is no longer sorted as per "sortOrder". As firebase does not allow multiple sort criteria, I believe my solution lies in offline sorting.
So, how does one sort the adapter offline?
My adapter is pretty standard:
mAdapter = new FirebaseRecyclerAdapter<Post, PostViewHolder>(Post.class, R.layout.item_post, PostViewHolder.class, dataQuery)
{
#Override
protected void populateViewHolder(final PostViewHolder viewHolder, final Post model, final int position)
{
final DatabaseReference postRef = getRef(position);
// Bind Post to ViewHolder, setting OnClickListener for the star button
viewHolder.bindToPost(model, position, postRef.getKey(), null);
}
mRecycler.setAdapter(mAdapter);
}
I've implemented Comparable in Post, the problem is where is the data stored so that I can pass it to Collections.sort() in this sort of way:
private List<Post> mPosts = new ArrayList<>();
mPosts.add(model);
Collections.sort(mPosts, Post.Comparators.ALLTIME);
The adapters in the FirebaseUI library currently always display data in the order that it is returned by the underlying FirebaseDatabase reference or Query.
You could file a feature request on the Github repo for it (since I'm not sure it is covered in this one yet). Alternatively you could fork the library and roll your own implementation of this functionality.
To feedback on the solution I came up with. I ended up populating my own List, which I can then sort or add to (if using multiple queries) depending on what I'm trying to achieve. This is then displayed using a standard RecyclerView adapter. It works great.
private final List<Post> postList = new ArrayList<>();
dataQuery.addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
postList.clear(); // in this example it's a newQuery, so clear postList
for (DataSnapshot child : dataSnapshot.getChildren())
{
Post post = child.getValue(Post.class);
postList.add(post);
int myPosition = sortData(); // uses Collections.sort() to sort data
mAdapter.addPosts(postList);
mAdapter.notifyDataSetChanged();
mRecycler.scrollToPosition(myPosition);
}
}
});