Using GeoFire to Populate Firebase Recycler View in android - android

I want to populate my recycler view so that, I can see who are the people/places nearby. I am using GeoFire to Query my database, which looks something like this.
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(latLngCenter.latitude, latLngCenter.longitude), 0.1);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
#Override
public void onKeyEntered(String key, GeoLocation location) {
System.out.println(String.format("Key %s entered the search area at [%f,%f]", key, location.latitude, location.longitude));
Log.e("TAG", key + location.latitude + location.longitude);
}
#Override
public void onKeyExited(String key) {
System.out.println(String.format("Key %s is no longer in the search area", key));
}
#Override
public void onKeyMoved(String key, GeoLocation location) {
System.out.println(String.format("Key %s moved within the search area to [%f,%f]", key, location.latitude, location.longitude));
Log.e("TAG", key + location.latitude + location.longitude);
}
#Override
public void onGeoQueryReady() {
System.out.println("All initial data has been loaded and events have been fired!");
}
#Override
public void onGeoQueryError(DatabaseError error) {
System.err.println("There was an error with this query: " + error);
}
});
and I am using this Firebase RecyclerView
RecyclerView recycler = (RecyclerView) findViewById(R.id.RecyclerView);
recycler.setHasFixedSize(true);
recycler.setLayoutManager(new LinearLayoutManager(this));
FirebaseRecyclerAdapter<Chat, ChatHolder> mAdapter = new FirebaseRecyclerAdapter<Chat, ChatHolder>(Chat.class, R.layout.recyclerview, ChatHolder.class, mUsers) {
#Override
public void populateViewHolder(final ChatHolder chatMessageViewHolder, final Chat chatMessage, int position) {
chatMessageViewHolder.setName(chatMessage.getName());
chatMessageViewHolder.setText(chatMessage.getText());
chatMessageViewHolder.setTimestamp(chatMessage.getTimestamp());
}
};
recycler.setAdapter(mAdapter);
with these Chat Holder class and chat object class
public static class ChatHolder extends RecyclerView.ViewHolder {
View mView;
public ChatHolder(View itemView) {
super(itemView);
mView = itemView;
}
public void setName(String name) {
TextView field = (TextView) mView.findViewById(R.id.textViewName);
field.setText(name);
}
public void setText(String text) {
TextView field = (TextView) mView.findViewById(R.id.textViewMessage);
field.setText(text);
}
public void setTimestamp(String text) {
TextView field = (TextView) mView.findViewById(R.id.textViewTime);
field.setText(text);
}
}
public static class Chat {
String name;
String text;
String uid;
String timestamp;
public Chat() {
}
public Chat(String name, String uid, String message, String timestamp) {
this.name = name;
this.text = message;
this.uid = uid;
this.timestamp = timestamp;
}
public String getName() {
return name;
}
public String getUid() {
return uid;
}
public String getText() {
return text;
}
public String getTimestamp() {
return timestamp;
}
}
Currently this, adapter which is provided in FirebaseUI library, populates recyclerview so that, only one reference is used and all child events are shown in the view,
Now, I want to populate my recyclerView so that when ever a key enters it populates my recyclerview based on my key = to my reference, this how my firebase database looksmy firebase database

It'll be simpler to push all data (which is also uid) you have retrieved from geofire to a new node to store geofire results in firebase, something like
my-node //your new firebase node to store all uids retrieved from geofire
- {chatUid}: true //true is just a dummy data, you can use anything else except null and empty string.
- {chatUid}: true
- ...
and set FirebaseRecyclerAdapter ref to that node.
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("my-node");
FirebaseRecyclerAdapter fra = new FirebaseRecyclerAdapter<Boolean, MyVH>(Boolean.class, R.layout.my_layout, MyVH.class, ref) { ... }
and then, in populateViewHolder() method in your implementation of FirebaseRecyclerAdapter, you can use the String key to fetch data from main node that contains the data.
public void populateViewHolder(MyVH viewHolder, Boolean model, int position){
// Get references of child views
final TextView nameTextView = viewHolder.getItemView().findViewById(R.id.my_name_text_view_in_vh_layout);
final TextView addressTextView = viewHolder.getItemView().findViewById(R.id.my_address_text_view_in_vh_layout);
// Key in this position.
String key = getRef(position).key;
// Query the full data of the current key located in the `main-data-node`
FirebaseDatabase.getInstance().getReference("main-data-node").child(key).addValueEventListener(new ValueEventListener(){
... //Truncated onCancelled
#Override
public void onDataChange(snap: DataSnapshot){
MyDataModel model = snap.getValue(MyDataModel.class);
nameTextView = model.getName();
addressTextView = model.getAddress();
}
}
}
// The data model class
public class MyDataModel {
private String name;
private String address;
... // Truncated getter, setter, constructor
}
For any changes in geofire result, just push those results to my-node, and it will automatically informs your FirebaseRecyclerAdapter.
P.S. I'm too lazy to match my solution to your data model (sorry), so I made a simplified sample class, so if anyone stumbled upon this, they can understand it easier.
P.S.S. It's been a while since I code in java, I mainly use kotlin (and you soon should too lol), so, if there're some syntax mistakes out there, feel free to edit.

This is the Emanuelet's custom FirebaseListAdapter from which I have created by FirebaseRecyclerAdapter.
public abstract class FirebaseRecyclerAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH > implements Filterable {
private static final String LOG_TAG = "FirebaseListAdapter";
private Query mRef;
private Class<T> mModelClass;
private int mLayout;
private LayoutInflater mInflater;
protected Class<VH> mViewHolderClass;
private List<T> mModels;
private List<T> mFilteredModels;
private List<String> mKeys = new ArrayList<>();
private Map<String, T> mModelKeys;
private Map<String, T> mFilteredKeys;
private ChildEventListener mListener;
private FirebaseRecyclerAdapter.ValueFilter valueFilter;
/**
* #param mRef The Firebase location to watch for data changes. Can also be a slice of a location, using some
* combination of <code>limit()</code>, <code>startAt()</code>, and <code>endAt()</code>,
* #param mModelClass Firebase will marshall the data at a location into an instance of a class that you provide
* #param mLayout This is the mLayout used to represent a single list item. You will be responsible for populating an
* instance of the corresponding view with the data from an instance of mModelClass.
* #param activity The activity containing the ListView
*/
public FirebaseRecyclerAdapter(Query mRef, Class<T> mModelClass, int mLayout, Activity activity, Class<VH> viewHolderClass) {
this.mRef = mRef;
this.mModelClass = mModelClass;
this.mLayout = mLayout;
this.mViewHolderClass = viewHolderClass;
mInflater = activity.getLayoutInflater();
mModels = new ArrayList<>();
mModelKeys = new HashMap<>();
// Look for all child events. We will then map them to our own internal ArrayList, which backs ListView
mListener = this.mRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
T model = dataSnapshot.getValue(FirebaseRecyclerAdapter.this.mModelClass);
mModelKeys.put(dataSnapshot.getKey(), model);
// Insert into the correct location, based on previousChildName
if (previousChildName == null) {
mModels.add(0, model);
} else {
T previousModel = mModelKeys.get(previousChildName);
int previousIndex = mModels.indexOf(previousModel);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
mModels.add(model);
mKeys.add(dataSnapshot.getKey());
} else {
mModels.add(nextIndex, model);
mKeys.add(dataSnapshot.getKey());
}
}
notifyDataSetChanged();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.d(LOG_TAG, "onChildChanged");
// One of the mModels changed. Replace it in our list and name mapping
String modelName = dataSnapshot.getKey();
T oldModel = mModelKeys.get(modelName);
T newModel = dataSnapshot.getValue(FirebaseRecyclerAdapter.this.mModelClass);
int index = mModels.indexOf(oldModel);
mModels.set(index, newModel);
mModelKeys.put(modelName, newModel);
notifyDataSetChanged();
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(LOG_TAG, "onChildRemoved");
// A model was removed from the list. Remove it from our list and the name mapping
String modelName = dataSnapshot.getKey();
T oldModel = mModelKeys.get(modelName);
mModels.remove(oldModel);
mKeys.remove(dataSnapshot.getKey());
mModelKeys.remove(modelName);
notifyDataSetChanged();
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(LOG_TAG, "onChildMoved");
// A model changed position in the list. Update our list accordingly
String modelName = dataSnapshot.getKey();
T oldModel = mModelKeys.get(modelName);
T newModel = dataSnapshot.getValue(FirebaseRecyclerAdapter.this.mModelClass);
int index = mModels.indexOf(oldModel);
mModels.remove(index);
if (previousChildName == null) {
mModels.add(0, newModel);
mKeys.add(dataSnapshot.getKey());
} else {
T previousModel = mModelKeys.get(previousChildName);
int previousIndex = mModels.indexOf(previousModel);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
mModels.add(newModel);
mKeys.add(dataSnapshot.getKey());
} else {
mModels.add(nextIndex, newModel);
mKeys.add(dataSnapshot.getKey());
}
}
notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError error) {
Log.e("FirebaseListAdapter", "Listen was cancelled, no more updates will occur");
}
});
}
public void cleanup() {
// We're being destroyed, let go of our mListener and forget about all of the mModels
mRef.removeEventListener(mListener);
mModels.clear();
mModelKeys.clear();
mKeys.clear();
}
#Override
public int getItemCount() {
return mModels.size();
}
public T getItem(int position) {
return mModels.get(position);
}
#Override
public void onBindViewHolder(VH holder, int position) {
T model = getItem(position);
populateViewHolder(holder, model, position, mKeys);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public int getItemViewType(int position) {
return mLayout;
}
public void remove(String key) {
T oldModel = mModelKeys.get(key);
mModels.remove(oldModel);
mKeys.remove(key);
mModelKeys.remove(key);
notifyDataSetChanged();
}
#Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
try {
Constructor<VH> constructor = mViewHolderClass.getConstructor(View.class);
return constructor.newInstance(view);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* Each time the data at the given Firebase location changes, this method will be called for each item that needs
* to be displayed. The arguments correspond to the mLayout and mModelClass given to the constructor of this class.
* <p/>
* Your implementation should populate the view using the data contained in the model.
*
* #param viewHolder The view to populate
* #param model The object containing the data used to populate the view
*/
protected abstract void populateViewHolder(VH viewHolder, T model, int position, List<String> mKeys);
public void addSingle(DataSnapshot snapshot) {
T model = snapshot.getValue(FirebaseRecyclerAdapter.this.mModelClass);
mModelKeys.put(snapshot.getKey(), model);
mModels.add(model);
mKeys.add(snapshot.getKey());
notifyDataSetChanged();
}
public void update(DataSnapshot snapshot, String key) {
T oldModel = mModelKeys.get(key);
T newModel = snapshot.getValue(FirebaseRecyclerAdapter.this.mModelClass);
int index = mModels.indexOf(oldModel);
if (index >= 0) {
mModels.set(index, newModel);
mModelKeys.put(key, newModel);
notifyDataSetChanged();
}
}
public boolean exists(String key) {
return mModelKeys.containsKey(key);
}
#Override
public Filter getFilter() {
if (valueFilter == null) {
valueFilter = new FirebaseRecyclerAdapter.ValueFilter();
}
return valueFilter;
}
protected abstract List<T> filters(List<T> models, CharSequence constraint);
private class ValueFilter extends Filter {
//Invoked in a worker thread to filter the data according to the constraint.
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new Filter.FilterResults();
if (mFilteredModels == null) {
mFilteredModels = new ArrayList<>(mModels); // saves the original data in mOriginalValues
mFilteredKeys = new HashMap<>(mModelKeys); // saves the original data in mOriginalValues
}
if (constraint != null && constraint.length() > 0) {
List<T> filtered = filters(mFilteredModels, constraint);
results.count = filtered.size();
results.values = filtered;
mModelKeys = filterKeys(mModels);
} else {
results.count = mFilteredModels.size();
results.values = mFilteredModels;
mModelKeys = mFilteredKeys;
}
return results;
}
//Invoked in the UI thread to publish the filtering results in the user interface.
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
Log.d(LOG_TAG, "filter for " + constraint + ", results nr: " + results.count);
mModels = (List<T>) results.values;
notifyDataSetChanged();
}
}
protected abstract Map<String, T> filterKeys(List<T> mModels);
}
Extend the PostsQueryAdapter with the FirebaseRecyclerAdapter
public class FirebasePostsQueryAdapter extends FirebaseRecyclerAdapter<Feeds, PostViewHolder> {
Activity mActivity;
/**
* #param mRef The Firebase location to watch for data changes. Can also be a slice of a location, using some
* combination of <code>limit()</code>, <code>startAt()</code>, and <code>endAt()</code>,
* #param mLayout This is the mLayout used to represent a single list item. You will be responsible for populating an
* instance of the corresponding view with the data from an instance of mModelClass.
* #param activity The activity containing the ListView
* #param viewHolderClass This is the PostsViewHolder Class which will be used to populate data.
*/
Query query;
public FirebasePostsQueryAdapter(Query mRef, int mLayout, Activity activity, Class<PostViewHolder> viewHolderClass) {
super(mRef, Feeds.class, mLayout, activity, viewHolderClass);
this.query = mRef;
this.mActivity = activity;
}
#Override
protected void populateViewHolder(final PostViewHolder viewHolder, final Feeds model, final int position, final List<String> mKeys) {
viewHolder.setPhoto(model.getThumb_url());
viewHolder.setTimestamp(DateUtils.getRelativeTimeSpanString(
(long) model.getTimestamp()).toString());
viewHolder.setAuthor(model.getUser().getFull_name(), model.getUser().getUid());
viewHolder.setIcon(model.getUser().getProfile_picture(), model.getUser().getUid());
viewHolder.setText(model.getText());
viewHolder.mPhotoView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(mActivity, SingleVideoView.class);
intent.putExtra(Constants.INTENT_VIDEO,model.getVideo_url());
intent.putExtra(Constants.KEY, mKeys.get(position));
mActivity.startActivity(intent);
}
});
ValueEventListener likeListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
viewHolder.setNumLikes(dataSnapshot.getChildrenCount());
if (dataSnapshot.hasChild(FirebaseUtil.getCurrentUserId())) {
viewHolder.setLikeStatus(PostViewHolder.LikeStatus.LIKED, mActivity);
} else {
viewHolder.setLikeStatus(PostViewHolder.LikeStatus.NOT_LIKED, mActivity);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
FirebaseUtil.getLikesRef().child(mKeys.get(position)).addValueEventListener(likeListener);
viewHolder.mLikeListener = likeListener;
}
#Override
protected List<Feeds> filters(List<Feeds> models, CharSequence constraint) {
return null;
}
#Override
protected Map<String, Feeds> filterKeys(List<Feeds> mModels) {
return null;
}
}
Where you populate the data into FirebasePostsQueryAdapter
#Override
public void onCreate() {
super.onCreate();
mFirebaseRef = FirebaseDatabase.getInstance().getReference(POSTS_STRING);
mFirebaseRef.keepSynced(true);
this.geoFire = new GeoFire(FirebaseDatabase.getInstance().getReference(GEO_POINTS));
mItemListAdapter = new FirebasePostQueryAdapter(mFirebaseRef.equalTo(GEOFIRE_CHILD), getActivity(), R.layout.list_item_items);
}
#Override
public void onConnected(Bundle bundle) {
startLocationUpdates();
center = new GeoLocation(MainActivity.mLastLocation.getLatitude(), MainActivity.mLastLocation.getLongitude());
if (!mActiveGeoQuery) {
center = new GeoLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
startGeoQuery();
mAdapter.notifyDataSetChanged();
}
center = new GeoLocation(MainActivity.mLastLocation.getLatitude(), MainActivity.mLastLocation.getLongitude());
if (center.latitude != 0 && center.longitude != 0 && !mActiveGeoQuery) {
startGeoQuery();
} else if (mActiveGeoQuery) {
Log.d(TAG, "geoquery already active");
} else {
Log.d(TAG, "center not setted");
//I first try to set the center at the Last Known Location if retrieved
if (Double.isNaN(mCurrentLocation.getLatitude()) && mCurrentLocation.getLatitude() != 0) {
center = new GeoLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
startGeoQuery();
}
}
fragment.checkForItems();
}
private void startGeoQuery() {
query = geoFire.queryAtLocation(center, 2);
Log.d(TAG, "center: " + center.toString() + ", radius: " + 2);
query.addGeoQueryEventListener(new GeoQueryEventListener() {
#Override
public void onKeyEntered(String key, GeoLocation location) {
Log.d(TAG, "Key " + key + " entered the search area at [" + location.latitude + "," + location.longitude + "]");
DatabaseReference tempRef = mFirebaseRef.child(key);
tempRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
// I add the deal only if it doesn't exist already in the adapter
String key = snapshot.getKey();
if (!mAdapter.exists(key)) {
Log.d(TAG, "item added " + key);
mAdapter.addSingle(snapshot);
mAdapter.notifyDataSetChanged();
} else {
//...otherwise I will update the record
Log.d(TAG, "item updated: " + key);
mAdapter.update(snapshot, key);
mAdapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onKeyExited(String key) {
Log.d(TAG, "deal " + key + " is no longer in the search area");
mAdapter.remove(key);
fragment.isListEmpty();
}
#Override
public void onKeyMoved(String key, GeoLocation location) {
Log.d(TAG, String.format("Key " + key + " moved within the search area to [%f,%f]", location.latitude, location.longitude));
DatabaseReference tempRef = mFirebaseRef.child(key);
tempRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
// I add the deal only if it doesn't exist already in the adapter
String key = snapshot.getKey();
if (!mAdapter.exists(key)) {
Log.d(TAG, "item added " + key);
mAdapter.addSingle(snapshot);
mAdapter.notifyDataSetChanged();
} else {
//...otherwise I will update the record
Log.d(TAG, "item updated: " + key);
mAdapter.update(snapshot, key);
mAdapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onGeoQueryReady() {
Log.d(TAG, "All initial data has been loaded and events have been fired!");
fragment.isListEmpty();
mActiveGeoQuery = true;
}
#Override
public void onGeoQueryError(DatabaseError error) {
Log.e(TAG, "There was an error with this query: " + error);
mActiveGeoQuery = false;
}
});
fragment.bindRecyclerView();
}

Related

How to send data from adapter in method BindView into activity?

I do have a total price in adapter OnBindViewHolder method how to send that data back to CartActivity to assign set text on the TextView in the CartActivity . should i go for Bundle to send the data to activity from the adapter or is there any other way to do it . thanks
My adapter code which extends FirestoreAdapter
public class ItemCartRecyelerAdapter extends
FirestoreAdapter<ItemCartRecyelerAdapter.ViewHolder>{
private static final String TAG = "ItemRecyelerAdapter";
private int TotalPrice = 0;
private Context context;
public interface OnItemSelectedListener {
void OnItemSelected(DocumentSnapshot item);
}
private OnItemSelectedListener mListener;
public ItemCartRecyelerAdapter(Query query, OnItemSelectedListener listener) {
super(query);
mListener = listener;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new ViewHolder(inflater.inflate(R.layout.item_cart_adapter,parent,false));
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
holder.bind(getSnapshot(position), mListener);
Attachment attachment = getSnapshot(position).toObject(Attachment.class);
TotalPrice += attachment.getItem_price() * attachment.getItem_quantity();
Log.d(TAG, "onBindViewHolder: FinalCrossToatal: " + TotalPrice)
}
public void deleteItem(int position){
getSnapshot(position).getReference().delete();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView item_name, item_company, item_price, discount_price, discount;
ImageView item_image, subtract_image;
ElegantNumberButton quantityPicker;
int TotalPrice = 0 ;
public ViewHolder(#NonNull View itemView) {
super(itemView);
item_image = itemView.findViewById(R.id.itemImageView);
item_company = itemView.findViewById(R.id.item_company);
item_price = itemView.findViewById(R.id.item_price);
item_name = itemView.findViewById(R.id.item_name);
quantityPicker = itemView.findViewById(R.id.quantityPicker);
subtract_image = itemView.findViewById(R.id.subtract_image);
discount_price = itemView.findViewById(R.id.item_discount_price);
discount = itemView.findViewById(R.id.discount);
}
public void bind(final DocumentSnapshot snapshot, final OnItemSelectedListener listener) {
final Attachment attachment = snapshot.toObject(Attachment.class);
Resources resources = itemView.getResources();
TotalPrice += (attachment.getItem_price() * attachment.getItem_quantity());
Log.d(TAG, "bind: Totalss1 = " + TotalPrice);
item_name.setText(attachment.getItem_name());
item_company.setText(attachment.getItem_brand());
discount.setText(String.valueOf(attachment.getItem_discount()) + "%\noff");
quantityPicker.setNumber(String.valueOf(attachment.getItem_quantity()));
if (attachment.getItem_discount() != null && attachment.getItem_discount() != 0) {
subtract_image.setVisibility(View.VISIBLE);
discount_price.setVisibility(View.VISIBLE);
discount.setVisibility(View.VISIBLE);
Integer discountedPrice = attachment.getItem_price() * attachment.getItem_discount() / 100;
Integer priceDiscounted = attachment.getItem_price() - discountedPrice;
discount_price.setText(String.valueOf(priceDiscounted));
}
Log.d(TAG, "bind: Urs: " + attachment.getUrls());
for (Map.Entry<String, String> result : attachment.getUrls().entrySet()) {
String key = result.getKey();
String value = result.getValue();
Log.d(TAG, "bind: Urls+valuew" + key + value);
//Load Image
Glide.with(item_image.getContext())
.load(value)
.into(item_image);
}
quantityPicker.setRange(0, 10);
quantityPicker.setOnValueChangeListener(new ElegantNumberButton.OnValueChangeListener() {
#Override
public void onValueChange(ElegantNumberButton view, int oldValue, int newValue) {
Log.d(TAG, "onValueChange: postion " + attachment.getItem_id());
Log.d(TAG, String.format("oldValue: %d newValue: %d", oldValue, newValue));
passData(newValue, attachment.getItem_id(), oldValue);
}
private void passData(int newValue, String item_id, int oldValue) {
// Go to the details page for the selected restaurant
//sharing to seperate cart node in store
if (newValue == 0) {
FirebaseFirestore updateQ = FirebaseFirestore.getInstance();
DocumentReference CartREf = updateQ.collection("Cart")
.document(item_id);
CartREf.delete().addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "onSuccess: Succed to quantity");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "onFailure: Failed to change cart");
}
});
FirebaseFirestore dQ = FirebaseFirestore.getInstance();
DocumentReference QuanityRef = dQ.collection("fruits & vegetables")
.document("UyGXpk2n1A6mHsUcYjCi")
.collection("Organic Fruits")
.document(item_id);
QuanityRef.update("item_quantity", newValue).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "onSuccess: Success updated item_quanity in Products");
}
});
}
Log.d(TAG, "passData: new update StrARTED");
FirebaseFirestore updateQ = FirebaseFirestore.getInstance();
DocumentReference CartREf = updateQ.collection("Cart")
.document(item_id);
CartREf.update("item_quantity", newValue).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "onSuccess: Succed to quantity");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "onFailure: Failed to change cart");
}
});
Log.d(TAG, "passData: new update StrARTED");
FirebaseFirestore dQ = FirebaseFirestore.getInstance();
DocumentReference QuanityRef = dQ.collection("fruits & vegetables")
.document("UyGXpk2n1A6mHsUcYjCi")
.collection("Organic Fruits")
.document(item_id);
QuanityRef.update("item_quantity", newValue).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "onSuccess: Succed to quantity");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "onFailure: Failed to change cart");
}
});
}
});
int items_price = attachment.getItem_price() * attachment.getItem_quantity();
item_price.setText("INR " + items_price + "Rs");
//Click Listener
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
listener.OnItemSelected(snapshot);
}
}
});
}
}
}
this is the method:
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
holder.bind(getSnapshot(position), mListener);
Attachment attachment = getSnapshot(position).toObject(Attachment.class);
TotalPrice += attachment.getItem_price() * attachment.getItem_quantity();
Log.d(TAG, "onBindViewHolder: FinalCrossToatal: " + TotalPrice)
Using Interface to communicate from Recyclerviewadapter with the activity.
Create interface, and activity implements this interface.
public interface OnItemClick {
void onClick (String value);
}
When you create adapter (last parameter is this interface)
public class MainActivity extends AppCompatActivity implements OnItemClick {
recycleAdapter = new RecycleAdapter(MainActivity.this,onlineData, this);
recyclerView.setAdapter(recycleAdapter);
#Override
void onClick (String value){
//Use the data in Activity
}
// In Adapter
private OnItemClick mCallback;
RecycleAdapter(Context context,List<HashMap<String, String>> onlineData,OnItemClick listener){
this.onlineData = onlineData;
this.context = context;
this.mCallback = listener;
}
....
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
holder.bind(getSnapshot(position), mListener);
Attachment attachment = getSnapshot(position).toObject(Attachment.class);
TotalPrice += attachment.getItem_price() * attachment.getItem_quantity();
Log.d(TAG, "onBindViewHolder: FinalCrossToatal: " + TotalPrice)
mCallback.onClick(TotalPrice);
Try Bundle to send the data in onBindViewHolder
SomeActivity activity= new SomeActivity ();
Bundle args = new Bundle();
args.putInt("totalPrice ", TotalPrice );
fragment.setArguments(args);

Database appears, but data cannot be filtered or searched

sorry if the explanation is incomplete. I just learned about Android. and in my project this time, I made a filter feature using the radio button
I followed a totorial, but the tutorial uses static data, then I change it to dynamic using my data in the database. all works !! the data appears
but when I type something in the search bar that I make, suddenly my data disappears, I make 3 radio buttons to be selected by the user to be searched / filtered, namely type, price and facilities. the three radio buttons don't work, my data that appears are gone.
How to handle that problem?? if anyone can help me please help, thank you
this is the code
this fragment
public class FilterFragment extends Fragment implements RadioGroup.OnCheckedChangeListener, View.OnClickListener {
private static final String data_url = "http://000.000.00.000/kos/get_kos.php";
RecyclerView mRecyclerView;
ProgressDialog pd;
private Context context;
private RecyclerViewAdapter adapter;
private ArrayList<UserModel> arrayList;
private RadioGroup searchViaRadioGroup;
private EditText searchEditText;
private TextView searchViaLabel;
/* Filter Type to identify the type of Filter */
private FilterType filterType;
/* boolean variable for Filtering */
private boolean isSearchWithPrefix = false;
public FilterFragment() {
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_filter, container, false);
pd = new ProgressDialog(getActivity());
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
arrayList = new ArrayList<>();
mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), LinearLayoutManager.VERTICAL));
adapter = new RecyclerViewAdapter(getActivity(), arrayList);
mRecyclerView.setAdapter(adapter);
loadjson();
return view;
}
private void loadjson(){
pd.setMessage("Mengambil Data");
pd.setCancelable(false);
pd.show();
JsonArrayRequest arrayRequest = new JsonArrayRequest(Request.Method.POST, data_url, null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
pd.cancel();
Log.d("volley", "response : " + response.toString());
for (int i=0; i < response.length(); i++)
try {
JSONObject data = response.getJSONObject(i);
UserModel md = new UserModel();
md.setJudul(data.getString("judul")); // memanggil nama array yang kita buat
md.setAlamat(data.getString("alamat"));
md.setHarga(data.getString("harga"));
md.setTipe_kos(data.getString("tipe_kos"));
md.setFasilitas(data.getString("fasilitas"));
arrayList.add(md);
} catch (JSONException e) {
e.printStackTrace();
}
adapter.notifyDataSetChanged();
}
}, new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error) {
pd.cancel();
Log.d("volley", "error : " + error.getMessage());
}
});
Controller.getInstance().addToRequestQueue(arrayRequest);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
findViews(view);
implementEvents();
}
//Bind all Views
private void findViews(View view) {
filterType = FilterType.TIPE_KOS;
searchViaRadioGroup = (RadioGroup) view.findViewById(R.id.search_via_radio_group);
searchEditText = (EditText) view.findViewById(R.id.search_text);
searchViaLabel = (TextView) view.findViewById(R.id.search_via_label);
}
//Populate recycler view
private void implementEvents() {
searchViaRadioGroup.setOnCheckedChangeListener(this);
searchViaLabel.setOnClickListener(this);
searchEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
//On text changed in Edit text start filtering the list
adapter.filter(filterType, charSequence.toString(), isSearchWithPrefix);
}
#Override
public void afterTextChanged(Editable editable) {
}
});
}
#Override
public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
int pos = radioGroup.indexOfChild(radioGroup.findViewById(checkedId));//get the checked position of radio button
switch (radioGroup.getId()) {
case R.id.search_via_radio_group:
switch (pos) {
case 0:
filterType = FilterType.TIPE_KOS;//Change filter type to Name if pos = 0
break;
case 1:
filterType = FilterType.NUMBER;//Change filter type to Number if pos = 1
break;
case 2:
filterType = FilterType.EMAIL;//Change filter type to Email if pos = 2
break;
}
}
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.search_via_label:
//show hide the radio group
if (searchViaRadioGroup.isShown()) {
searchViaLabel.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.up_dropdown, 0);
searchViaRadioGroup.setVisibility(View.GONE);
} else {
searchViaLabel.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.down_dropdown, 0);
searchViaRadioGroup.setVisibility(View.VISIBLE);
}
break;
}
}
}
this adapter
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {
static class RecyclerViewHolder extends RecyclerView.ViewHolder {
private TextView judul, alamat, tipe_kos, fasilitas, harga;
RecyclerViewHolder(View view) {
super(view);
judul = (TextView) view.findViewById(R.id.judul);
alamat = (TextView) view.findViewById(R.id.alamat);
tipe_kos = (TextView) view.findViewById(R.id.tipe_kos);
fasilitas = (TextView) view.findViewById(R.id.fasilitas);
harga = (TextView) view.findViewById(R.id.harga);
}
}
private ArrayList<UserModel> arrayList;
private ArrayList<UserModel> filterArrayList;//duplicate list for filtering
private Context context;
public RecyclerViewAdapter(Context context, ArrayList<UserModel> arrayList) {
this.arrayList = arrayList;
this.context = context;
this.filterArrayList = new ArrayList<>();//initiate filter list
this.filterArrayList.addAll(arrayList);//add all items of array list to filter list
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.custom_list_filter, viewGroup, false);
return new RecyclerViewHolder(v);
}
#Override
public void onBindViewHolder(final RecyclerViewHolder holder, final int i) {
final UserModel model = arrayList.get(i);
holder.judul.setText(model.getJudul());//menampilkan data
holder.alamat.setText(model.getAlamat());
holder.harga.setText(model.getHarga());
holder.tipe_kos.setText(model.getTipe_kos());
holder.fasilitas.setText(model.getFasilitas());
}
#Override
public int getItemCount() {
return (null != arrayList ? arrayList.size() : 0);
}
// Filter Class to filter data
public void filter(FilterType filterType, String charText, boolean isSearchWithPrefix) {
//If Filter type is TIPE_KOS and EMAIL then only do lowercase, else in case of NUMBER no need to do lowercase because of number format
if (filterType == FilterType.TIPE_KOS || filterType == FilterType.EMAIL)
charText = charText.toLowerCase(Locale.getDefault());
arrayList.clear();//Clear the main ArrayList
//If search query is null or length is 0 then add all filterList items back to arrayList
if (charText.length() == 0) {
arrayList.addAll(filterArrayList);
} else {
//Else if search query is not null do a loop to all filterList items
for (UserModel model : filterArrayList) {
//Now check the type of search filter
switch (filterType) {
case TIPE_KOS:
if (isSearchWithPrefix) {
//if STARTS WITH radio button is selected then it will match the exact TIPE_KOS which match with search query
if (model.getTipe_kos().toLowerCase(Locale.getDefault()).startsWith(charText))
arrayList.add(model);
} else {
//if CONTAINS radio button is selected then it will match the TIPE_KOS wherever it contains search query
if (model.getTipe_kos().toLowerCase(Locale.getDefault()).contains(charText))
arrayList.add(model);
}
break;
case EMAIL:
if (isSearchWithPrefix) {
//if STARTS WITH radio button is selected then it will match the exact EMAIL which match with search query
if (model.getFasilitas().toLowerCase(Locale.getDefault()).startsWith(charText))
arrayList.add(model);
} else {
//if CONTAINS radio button is selected then it will match the EMAIL wherever it contains search query
if (model.getFasilitas().toLowerCase(Locale.getDefault()).contains(charText))
arrayList.add(model);
}
break;
case NUMBER:
if (isSearchWithPrefix) {
//if STARTS WITH radio button is selected then it will match the exact NUMBER which match with search query
if (model.getHarga().startsWith(charText))
arrayList.add(model);
} else {
//if CONTAINS radio button is selected then it will match the NUMBER wherever it contains search query
if (model.getHarga().contains(charText))
arrayList.add(model);
}
break;
}
}
}
notifyDataSetChanged();
}
}
this FilterType
public enum FilterType {
TIPE_KOS, NUMBER, EMAIL;
}
and this user model
package com.example.asus.myapplication.fragment.filter;
public class UserModel {
private String judul, alamat, harga, tipe_kos, fasilitas;
public String getJudul() {
return judul;
}
public String getAlamat() {
return alamat;
}
public String getHarga() {
return harga;
}
public String getTipe_kos() {
return tipe_kos;
}
public String getFasilitas() {
return fasilitas;
}
public void setJudul(String mJudul) {
judul = mJudul;
}
public void setAlamat(String mAlamat) {
alamat = mAlamat;
}
public void setHarga(String mHarga) {
harga = mHarga;
}
public void setTipe_kos(String mTipe_kos) {
tipe_kos = mTipe_kos;
}
public void setFasilitas(String mFasilitas) { fasilitas= mFasilitas;
}
}
another one I do not understand at all for this filter API, I just pulled all the data in my database
My database is like this, which I circle, that's the data I want to filter later

How to retrieve the data in firebase?

My database structure
I am trying to create an activity where I can display all the logs. Just timestamp and the log message. I have tried with firebaseUI and adapter but I can't get the data to show. Best I have done was to post same last log in all positions. This is what I have so far but no success. I am new to firebase and all I need is to display the logs in a list. It can be lisView or recyclerView. If anyone can help me with code or example. Thank you.
Database structure is | "logs" node / userId / logId / fields |
public class LogActivity extends AppCompatActivity {
private static final String TAG = "LogActivity";
private static final int ACTIVITY_NUM = 3;
//widgets
private Context mContext = LogActivity.this;
private RecyclerView mLogRecycleView;
private TextView timeStamp, log;
//firebase
private DatabaseReference mLogDatabase;
private FirebaseAuth mAuth;
//adapter
private FirebaseRecyclerAdapter adapter;
//vars
private String mCurrentUserID, logID;
List<AppLogs> logsList = new ArrayList<>();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_log);
Log.d(TAG, "onCreate: Started");
mCurrentUserID = FirebaseAuth.getInstance().getCurrentUser().getUid();
mLogRecycleView = findViewById(R.id.recyclerList);
mLogDatabase = FirebaseDatabase.getInstance().getReference().child("logs").child(mCurrentUserID);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mLogRecycleView.setHasFixedSize(true);
mLogRecycleView.setLayoutManager(linearLayoutManager);
firebaseListAdapter();
mLogRecycleView.setAdapter(adapter);
setupBottomNavigationView();
}
#Override
protected void onStart() {
super.onStart();
adapter.startListening();
}
#Override
protected void onStop() {
super.onStop();
adapter.stopListening();
}
private void firebaseListAdapter() {
Log.d(TAG, "firebaseListAdapter: started");
Query logQuery = mLogDatabase.orderByChild("time");
FirebaseRecyclerOptions<AppLogs> options =
new FirebaseRecyclerOptions.Builder<AppLogs>()
.setQuery(logQuery, AppLogs.class).build();
adapter = new FirebaseRecyclerAdapter<AppLogs, LogViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull final LogViewHolder holder, int position, #NonNull AppLogs model) {
Log.d(TAG, "onBindViewHolder: started");
//get the ID of the messages
//final String logID = getRef(position).getKey();
//Log.d(TAG, "onBindViewHolder: logID : " + logID);
Query logQuery = mLogDatabase;
logQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot singData : dataSnapshot.getChildren()) {
//AppLogs logs = dataSnapshot.getValue(AppLogs.class);
Log.d(TAG, "onChildAdded: log:==== " + singData.child("log").getValue());
//Log.d(TAG, "onChildAdded: log_ID:==== " + logs.getLog_id());
String log = singData.child("log").getValue().toString();
// String timeStamp = Long.toString(logs.getTime());
//
holder.setLog(log);
// holder.setTimeStamp(timeStamp);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#NonNull
#Override
public LogViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Log.d(TAG, "onCreateViewHolder: create users view holder: ");
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_log_list_view, parent, false);
return new LogViewHolder(view);
}
};
}
public static class LogViewHolder extends RecyclerView.ViewHolder {
View mView;
public LogViewHolder(View itemView) {
super(itemView);
this.mView = itemView;
}
public void setLog(String log) {
TextView tvLog = mView.findViewById(R.id.tvLog);
tvLog.setText(log);
}
public void setTimeStamp(String timeStamp) {
TextView tvTimeStamp = mView.findViewById(R.id.tvTimeStamp);
tvTimeStamp.setText(timeStamp);
}
}
/*
*BottomNavigationView Setup
*/
private void setupBottomNavigationView() {
Log.d(TAG, "setupBottomNavigationView: setting up BottomNavigationView");
BottomNavigationViewEx bottomNavigationViewEx = (BottomNavigationViewEx) findViewById(R.id.bottomNavViewBar);
BottomNavigationViewHelper.setupBottomNavigationView(bottomNavigationViewEx);
BottomNavigationViewHelper.enableNavigation(mContext, this, bottomNavigationViewEx);
Menu menu = bottomNavigationViewEx.getMenu();
MenuItem menuItem = menu.getItem(ACTIVITY_NUM);
menuItem.setChecked(true);
}
}
and my log model class
package com.logistics.alucard.socialnetwork.Models;
public class AppLogs {
private String log, log_id;
private long time;
public AppLogs(String log, String log_id, long time) {
this.log = log;
this.log_id = log_id;
this.time = time;
}
public AppLogs() {
}
public String getLog() {
return log;
}
public void setLog(String log) {
this.log = log;
}
public String getLog_id() {
return log_id;
}
public void setLog_id(String log_id) {
this.log_id = log_id;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
}
I manage to figure it out! Thank you for your help. Still a bit confusing how to
build queries but I'll try to get better :)
This is my solution to the firebase retrieve data:
protected void onBindViewHolder(#NonNull final LogViewHolder holder, int position, #NonNull AppLogs model) {
Log.d(TAG, "onBindViewHolder: started");
//get the ID of the messages
final String logID = getRef(position).getKey();
//Log.d(TAG, "onBindViewHolder: logID : " + logID);
Query logQuery = mLogDatabase;
logQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//AppLogs appLogs = dataSnapshot.getValue(AppLogs.class);
//Log.d(TAG, "onDataChange: logs:---------" + dataSnapshot.child(logID).child("log").getValue());
String log = dataSnapshot.child(logID).child("log").getValue().toString();
String timeStamp = dataSnapshot.child(logID).child("time").getValue().toString();
Log.d(TAG, "onDataChange: logs:--------------" + log);
holder.setLog(log);
holder.setTimeStamp(timeStamp);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});

How to load batches of data in a recycler view using firestore?

I wanted to know how to load more data in recylcer view using firestore.
Query query = FirebaseFirestore.getInstance()
.collection("ie").limit(5);
adapter=new InterviewAdapter(this,query);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
Adapter class looks like this:
public class InterviewAdapter extends FireStoreAdapter<InterviewAdapter.ViewHolder> {
public interface OnInterviewSelectedListener {
void onInterviewSelected(DocumentSnapshot interview);
}
private InterviewAdapter.OnInterviewSelectedListener mListener;
public InterviewAdapter(Query query, OnInterviewSelectedListener listener) {
super(query);
mListener = listener;
}
#Override
public InterviewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new InterviewAdapter.ViewHolder(inflater.inflate(R.layout.ie, parent, false));
}
#Override
public void onBindViewHolder(InterviewAdapter.ViewHolder holder, int position) {
holder.bind(getSnapshot(position), mListener);
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView title,companyName,username,views,isHired;
public ViewHolder(View itemView) {
super(itemView);
title= (TextView) itemView.findViewById(R.id.title);
companyName= (TextView) itemView.findViewById(R.id.companyName);
username= (TextView) itemView.findViewById(R.id.username);
views= (TextView) itemView.findViewById(R.id.views);
isHired= (TextView) itemView.findViewById(R.id.isHired);
}
public void bind(final DocumentSnapshot snapshot,
final OnInterviewSelectedListener listener) {
InterviewExperience experience;
String companyName=snapshot.getString("companyName");
boolean isHired=Boolean.valueOf(snapshot.getBoolean("isHired"));
String username=snapshot.getString("username");
long views=new Double(Double.valueOf(snapshot.getDouble("views"))).longValue();
String id=snapshot.getId();
String title=snapshot.getString("title");
experience=new InterviewExperience(id,title,companyName,username,isHired,views,null,null);
this.title.setText(experience.getTitle());
this.companyName.setText("Company Name: "+experience.getCompanyName());
this.isHired.setText("Hired: "+experience.isHired());
this.views.setText("Views: "+experience.getViews()+"");
this.username.setText("Created By: "+experience.getUsername());
// Click listener
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
listener.onInterviewSelected(snapshot);
}
}
});
}
}
}
public abstract class FireStoreAdapter<VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH>
implements EventListener<QuerySnapshot> {
private static final String TAG = "FirestoreAdapter";
private Query mQuery;
private ListenerRegistration mRegistration;
private ArrayList<DocumentSnapshot> mSnapshots = new ArrayList<>();
public FireStoreAdapter(Query query) {
mQuery = query;
}
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "onEvent:error", e);
onError(e);
return;
}
// Dispatch the event
Log.d(TAG, "onEvent:numChanges:" + documentSnapshots.getDocumentChanges().size());
for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
switch (change.getType()) {
case ADDED:
onDocumentAdded(change);
break;
case MODIFIED:
onDocumentModified(change);
break;
case REMOVED:
onDocumentRemoved(change);
break;
}
}
onDataChanged();
}
public void startListening() {
if (mQuery != null && mRegistration == null) {
mRegistration = mQuery.addSnapshotListener(this);
}
}
public void stopListening() {
if (mRegistration != null) {
mRegistration.remove();
mRegistration = null;
}
mSnapshots.clear();
notifyDataSetChanged();
}
public void setQuery(Query query) {
// Stop listening
stopListening();
// Clear existing data
mSnapshots.clear();
notifyDataSetChanged();
// Listen to new query
mQuery = query;
startListening();
}
#Override
public int getItemCount() {
return mSnapshots.size();
}
protected DocumentSnapshot getSnapshot(int index) {
return mSnapshots.get(index);
}
protected void onDocumentAdded(DocumentChange change) {
mSnapshots.add(change.getNewIndex(), change.getDocument());
notifyItemInserted(change.getNewIndex());
}
protected void onDocumentModified(DocumentChange change) {
if (change.getOldIndex() == change.getNewIndex()) {
// Item changed but remained in same position
mSnapshots.set(change.getOldIndex(), change.getDocument());
notifyItemChanged(change.getOldIndex());
} else {
// Item changed and changed position
mSnapshots.remove(change.getOldIndex());
mSnapshots.add(change.getNewIndex(), change.getDocument());
notifyItemMoved(change.getOldIndex(), change.getNewIndex());
}
}
protected void onDocumentRemoved(DocumentChange change) {
mSnapshots.remove(change.getOldIndex());
notifyItemRemoved(change.getOldIndex());
}
protected void onError(FirebaseFirestoreException e) {};
protected void onDataChanged() {}
}
I used Firestore Adapter code which was given in samples of firestore documentation. Can anyone tell how to use the query object to load more data?
How to load the next 5 items in the recycler view when users scrolls to the end of the list?
You can paginate your Query's result using Query's methods like, startAt(), startAfter(), endAt(), endBefore() with a specified DocumentSnapshot.
If I considered your collection is called "interviews", you can add a method to your FireStoreAdapter like this:
private void paginate(final DocumentSnapshot last, final int limit) {
final Query subset;
if (last == null) {
subset = db.collection("interviews")
.limit(limit);
} else {
subset = db.collection("interviews")
.startAfter(last)
.limit(limit);
}
setQuery(subset);
}
You can perserve the last DocumentSnapshot within onEvent():
final List<DocumentChange> changes = documentSnapshots.getDocumentChanges();
final DocumentSnapshot lastDocument = changes.get(changes.size() - 1).getDocument();
Finally, when users scrolls to the end of the list:
paginate(lastDocument, 5);
And onDocumentAdded() will take care of it. Be carfure NOT to use startAt() because it will not execlude the last one (that already at the end of your list, and will duplicate it).

How to delete duplicate data that I get after updating data in firebase console

I am developing an android app that displays the ranks of students based on their marks retrieved from the firebase database. Everything is working fine but, when I update the marks in the db, it keeps the old data and adds the new data in the recyclerView. I can restart the app to refresh the data. But while it is still running, it shows the old data too.
Below is my firebase data:
Student1: {
c: 70,
cPlus: 90,
java: 70,
name: "Samson",
regno: "16sksb7034",
unix: 60
}
Student2: {
c: 20,
cPlus: 85,
java: 68,
name: "Samson",
regno: "16sksb7034",
unix: 86
}
Student3: {
c: 70,
cPlus: 70,
java: 80,
name: "Samson",
regno: "16sksb7034",
unix: 90
}
Here is my dataModel class:
public class Marks {
private String name;
private String regno;
private int c;
private int cPlus;
private int java;
private int unix;
private int percentage;
public Marks() {}
public Marks(int c, int cPlus, int java, int unix) {
this.c = c;
this.cPlus = cPlus;
this.java = java;
this.unix = unix;
}
public int getPercentage() {
return percentage;
}
public void setPercentage(int percentage) {
this.percentage = percentage;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRegno() {
return regno;
}
public void setRegno(String regno) {
this.regno = regno;
}
public int getC() {
return c;
}
public void setC(int c) {
this.c = c;
}
public int getcPlus() {
return cPlus;
}
public void setcPlus(int cPlus) {
this.cPlus = cPlus;
}
public int getJava() {
return java;
}
public void setJava(int java) {
this.java = java;
}
public int getUnix() {
return unix;
}
public void setUnix(int unix) {
this.unix = unix;
}
}
class MarksComparator implements Comparator<Marks> {
#Override
public int compare(Marks marks1, Marks marks2) {
int Marks1Total = marks1.getPercentage();
int Marks2Total = marks2.getPercentage();
if (Marks2Total < Marks1Total) {
return -1;
} else if (Marks2Total > Marks1Total) {
return 1;
} else {
return 0;
}
}
}
Here's my activity class:
public class MarksFragment extends Fragment{
private List<Marks> mMarksList = new ArrayList<>();
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private FirebaseDatabase mDatabase;
private DatabaseReference mReference;
private int total=0;
public MarksFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_marks, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
// use a linear layout manager
mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
// specify an adapter (see also next example)
/*mAdapter = new MyAdapter(getContext(),mMarksList);
mAdapter.notifyDataSetChanged();
mRecyclerView.setAdapter(mAdapter);*/
//get Firebase Reference
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
mDatabase = FirebaseDatabase.getInstance();
mReference = mDatabase.getReference();
mReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
fetchData(dataSnapshot);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
fetchData(dataSnapshot);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return view;
}
public void findPercentage(Marks value) {
total =value.getC() + value.getcPlus() + value.getJava() + value.getUnix();
value.setPercentage(total);
}
private void fetchData(DataSnapshot dataSnapshot) {
Marks value = dataSnapshot.getValue(Marks.class);
Log.v("Marks Fragment", "" +value);
findPercentage(value);
mMarksList.add(value);
Collections.sort(mMarksList, new MarksComparator());
// specify an adapter (see also next example)
mAdapter = new MyAdapter(getContext(),mMarksList);
mAdapter.notifyDataSetChanged();
mRecyclerView.setAdapter(mAdapter);
Here is my adapter class:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder>{
private Context mContext;
private List<Marks> marksList;
public MyAdapter(Context mContext, List<Marks> marksList) {
this.mContext = mContext;
this.marksList = marksList;
}
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView mItemName, mItemRegNo, mItemNo, mTotal;
CircleImageView mImageView;
public MyViewHolder(View view) {
super(view);
mItemName = (TextView) view.findViewById(R.id.card_name);
mItemRegNo = (TextView) view.findViewById(R.id.card_regno);
mItemNo = (TextView) view.findViewById(R.id.item_id);
mImageView = (CircleImageView) view.findViewById(R.id.item_photo);
mTotal = view.findViewById(R.id.card_total);
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_item, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
Marks marks = marksList.get(position);
int count = position + 1;
holder.mItemName.setText("" + marks.getName());
holder.mItemRegNo.setText("" + marks.getRegno());
holder.mItemNo.setText("" + count);
holder.mImageView.setImageResource(R.drawable.after_cookie);
holder.mTotal.setText(""+ marks.getPercentage());
}
#Override
public int getItemCount() {
return marksList.size();
}
}
So the code does what its intended to do it retrieves the data and calculates the total and ranks the students. but when I update the data in firebase console the views in recyclerView duplicates temporarily. Like for example if I update Student1 unix value as 10 then two views will be shown in the recyclerView: 1 for previous value and 2 for updated value and again if I update the values it will yet show another views representing the new data without removing the old views. But if I restart recyclerView gets refreshed and its all ok but while I am running the app during the update it shows temporary duplicate views too.
I am new here and this is my first question so I can't even upload picture as you need 10 points to upload photo. I really hope someone help me out on this. I thank you in advance.
UPDATE
Here is link to the image:
When I start the app, the image is:
first Image
when I update the unix value of Student3, the image in recyclerView becomes like this:
After updating the data in firebase console
So, you see it adds new data as well as keeps the old data untill I restart.
Your problem is that you're never checking if the student already exists in your mMarksList so you're simply duplicating him by adding him again with new grades.
What I would do in you case is to add an unique id in firebase to each student.
Then you can check in your fetchData whether the student with that id is already in the array, delete him and add the new one.
private void fetchData(DataSnapshot dataSnapshot) {
Marks value = dataSnapshot.getValue(Marks.class);
Log.v("Marks Fragment", "" +value);
findPercentage(value);
// Get an iterator.
Iterator<Marks> ite = mMarksList.iterator();
while(ite.hasNext()) {
Marks iteValue = ite.next();
if(iteValue.getId().equals(value.getId())) ite.remove();
}
mMarksList.add(value);
....
}
Optionally To make that even cleaner, you can override the equals and hashcode methods in your Marks data model, so that a Marks object is considered the same if the id is equal. More
//ASSUMING THAT ID IS int
#Override
public int hashCode() {
return id;
}
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (this.getClass() != obj.getClass()) return false;
Marks other = (Marks) obj;
if (this.getId != other.getId) {
return false;
}
return true;
}
Then it's possible to either use a hashmap, which will override the old student automatically or a arraylist as is and iterate through it before and check if a student equals your new student, like this:
private void fetchData(DataSnapshot dataSnapshot) {
Marks value = dataSnapshot.getValue(Marks.class);
Log.v("Marks Fragment", "" +value);
findPercentage(value);
// Use an iterator.
Iterator<Marks> ite = mMarksList.iterator();
while(ite.hasNext()) {
Marks iteValue = ite.next();
if(iteValue.equals(value)) ite.remove();
}
mMarksList.add(value);
....
}

Categories

Resources