How to implement ViewModel with FirebaseRecyclerAdapter - android

I am trying to separate my database from my UI controller using ViewModel and prevent constant data usage. I have searched for solution for days now without any headway.
I am using Firebase RecyclerAdapter in my app and the code is working well, but I don't know how to implement a LiveData Observer with the RecyclerAdapter.
any sample app or snippet will be of great help to me.
Here is a sample code from my Fragment
private void fetchValues() {
Query query1 = mDatabaseReference;
FirebaseRecyclerOptions<Course> options = new FirebaseRecyclerOptions.Builder<Course>()
.setQuery(query1, new SnapshotParser<Course>() {
#NonNull
#Override
public Course parseSnapshot(#NonNull DataSnapshot snapshot) {
return new Course(snapshot.child("course_title").getValue().toString(),
snapshot.child("course_code").getValue().toString(),
snapshot.child("course_lecturer").getValue().toString());
}
})
.build();
mFirebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Course, CourseViewHolder>(options){
#NonNull
#Override
public CourseViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
mCourseItemBinding = CourseItemBinding.inflate(LayoutInflater.from(getActivity()), parent, false);
View view = mCourseItemBinding.getRoot();
return new CourseViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CourseViewHolder holder, int position, Course course) {
holder.bind(course);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getActivity(), String.valueOf(position), Toast.LENGTH_LONG).show();
}
});
}
};
mRecyclerView.setAdapter(mFirebaseRecyclerAdapter);
}

Related

How to code the view holder binding when using FirebaseRecyclerAdapter

#Override
public void onStart() {
super.onStart();
FirebaseRecyclerAdapter<Data,MyViewHolder>adapter=new FirebaseRecyclerAdapter<Data, MyViewHolder> ("what to put here?") {
#Override
protected void onBindViewHolder(#NonNull MyViewHolder holder, int position, #NonNull Data model) {
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return null;
}
};
}
I was trying to do a recycler view where in I can put the FirebaseRealtimeDatabase
You will need a FirebaseRecyclerOptions:
FirebaseRecyclerOptions <Data> options = new FirebaseRecyclerOptions.Builder <Data>().setQuery(query, Data.class).build();
Data : Is your class.
query : Is your DatabaseReference that reference, your data node that you want to populate in the recycler view.
And pass this options to the place that you are referring to:
//here
.... = new FirebaseRecyclerAdapter<Data, MyViewHolder> (options) {

How can I get the currently logged in user and prevent him from searching for himself?

The method searchForFriends retrieves users stored in firebase database based on their
usernames. However a logged in user can search for himself too.
I tried couple solutions but none worked.
I tried to get the currentuserid and transform it to string variable even though
the issue continued.
searchIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
String searchTextInput = searchText.getText().toString();
searchForFriends(searchTextInput);
}
});
private void searchForFriends(String searchTextInput)
{
Toast.makeText(FindFriendsActivity.this, "Searching...", Toast.LENGTH_SHORT).show();
Query searchForPeople = usersRef.orderByChild("username")
.startAt(searchTextInput).endAt(searchTextInput+"\uf8ff");
FirebaseRecyclerOptions<FindFriends> options
= new FirebaseRecyclerOptions.Builder<FindFriends>()
.setQuery(searchForPeople,FindFriends.class).build();
FirebaseRecyclerAdapter<FindFriends,FindFriendsViewHolder> adapter
= new FirebaseRecyclerAdapter<FindFriends, FindFriendsViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull FindFriendsViewHolder holder, int position, #NonNull FindFriends model)
{
holder.userFullName.setText(model.getFullname());
holder.userStatus.setText(model.getStatus());
Picasso.get().load(model.getProfileimage()).into(holder.userProfileImage);
}
#NonNull
#Override
public FindFriendsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.show_searched_users,parent,false);
FindFriendsViewHolder viewHolder = new FindFriendsViewHolder(view);
return viewHolder;
}
};
searchResults.setAdapter(adapter);
adapter.startListening();
}
public static class FindFriendsViewHolder extends RecyclerView.ViewHolder
{
TextView userFullName, userStatus;
CircleImageView userProfileImage;
public FindFriendsViewHolder(#NonNull View itemView)
{
super(itemView);
userFullName = itemView.findViewById(R.id.show_searched_users_full_name);
userStatus = itemView.findViewById(R.id.show_searched_users_status);
userProfileImage = itemView.findViewById(R.id.searched_user_profile_image);
}
}

I am having trouble with firebase, I am trying to get a list but even though there are values, the list is not displayed in my recycleview

This is my code for the firebase access and through several methods, it should be displayed in the below code.
So the problem is that even though I have values in my firebase realtime database I am unable to get them. The strange thing is that I have used the same way this code in another part of the application and there it works fine, just it only retrieves when I click on a TextView, so if someone would know how to call this upon opening the fragment that would be good to know as well!
#Override
public ArrayList<Chore> getChoresByFlatmate() {
ArrayList<Chore> chores = new ArrayList<>();
myref.child("flats").child(flatID).child("chores").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
chores.clear();
for (DataSnapshot snapshot1 : snapshot.getChildren()) {
if (snapshot1.child("assignedto").getValue().equals(flatmateID)) {
chores.add(snapshot1.getValue(Chore.class));
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Log.e("Getting chores error", error.getDetails());
}
});
return chores;
}
Where it should display it:
Adapter class:
public class OwnChoresAdapter extends RecyclerView.Adapter<OwnChoresAdapter.ViewHolder> {
private List<Chore> chores;
private HomeViewModel homeViewModel;
public OwnChoresAdapter() {
homeViewModel = new HomeViewModel();
chores = homeViewModel.getChoresByUser();
}
#NonNull
#Override
public OwnChoresAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.singleownchore, parent, false);
return new OwnChoresAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull OwnChoresAdapter.ViewHolder holder, int position) {
holder.done.setOnClickListener(v -> homeViewModel.delete(chores.get(position).getChoreID()));
}
#Override
public int getItemCount() {
return chores.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView title;
TextView desc;
Button done;
public ViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.task_nameown);
desc = itemView.findViewById(R.id.task_descown);
done = itemView.findViewById(R.id.accept_taskown);
}
}
}
Fagment where it should display:
RecyclerView latestCosts;
CostAdapter costAdapter;
RecyclerView ownchores;
OwnChoresAdapter ownChoresAdapter;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_home, container, false);
latestCosts = root.findViewById(R.id.rvlatestexpenses);
latestCosts.hasFixedSize();
latestCosts.setLayoutManager(new LinearLayoutManager(getActivity()));
costAdapter = new CostAdapter();
latestCosts.setAdapter(costAdapter);
ownchores = root.findViewById(R.id.rvowntasks);
ownchores.hasFixedSize();
ownchores.setLayoutManager(new LinearLayoutManager(getActivity()));
ownChoresAdapter = new OwnChoresAdapter();
ownchores.setAdapter(ownChoresAdapter);
return root;
}
While you're adding the updated data to the chores array that is shown in the view, the adapter will only update the UI when you inform it about the change in its data by calling notifyDataSetChanged.
So you'll want to call that at the end of onDataChange:
public void onDataChange(#NonNull DataSnapshot snapshot) {
chores.clear();
for (DataSnapshot snapshot1 : snapshot.getChildren()) {
if (snapshot1.child("assignedto").getValue().equals(flatmateID)) {
chores.add(snapshot1.getValue(Chore.class));
}
}
ownChoresAdapter.notifyDataSetChanged(); // 👈 add this to your code
}

Recycler View showing only very last item added to realtime database

I have a firebase strucuture as
The recycler view is only displaying the last added or say latest added node to the database instead of displaying each and every node
the database reference i am using is
r2 = FirebaseDatabase.getInstance().getReference().child("Uploads").child("Wheat").child(S).child(D).child(T).child(FirebaseAuth.getInstance().getUid());
and the code for recycler view is as
public class MyPost extends AppCompatActivity {
private RecyclerView recyclerView;
private DatabaseReference r, r2, r3;
private String S, D, T;
FirebaseRecyclerAdapter<RecyclerCropView, ViewHolder2> Fbra1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_post);
r = FirebaseDatabase.getInstance().getReference().child("User_Data").child(MainDashboard.type).child(FirebaseAuth.getInstance().getUid());
recyclerView = (RecyclerView) findViewById(R.id.R_view2);
r.addListenerForSingleValueEvent(rr);
}
private ValueEventListener rr = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
S = dataSnapshot.child("State").getValue().toString();
D = dataSnapshot.child("City").getValue().toString();
T = dataSnapshot.child("Tehsil").getValue().toString();
// Toast.makeText(MyPost.this,S+D+T.toString(),Toast.LENGTH_LONG).show();
r2 = FirebaseDatabase.getInstance().getReference().child("Uploads").child("Wheat").child(S).child(D).child(T).child(FirebaseAuth.getInstance().getUid());
// Toast.makeText(MyPost.this,r2.toString(),Toast.LENGTH_LONG).show();
r2.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
r3=r2.child(dataSnapshot.getKey());
Toast.makeText(MyPost.this, r3.toString(), Toast.LENGTH_LONG).show();
FirebaseRecyclerOptions<RecyclerCropView> options = new FirebaseRecyclerOptions.Builder<RecyclerCropView>().setQuery(r3, RecyclerCropView.class).build();
Fbra1 = new FirebaseRecyclerAdapter<RecyclerCropView, ViewHolder2>(options) {
#Override
protected void onBindViewHolder(#NonNull ViewHolder2 holder, int position, #NonNull RecyclerCropView model) {
holder.setProductImage(model.getProduct_Image());
holder.setProduct(model.getProduct());
holder.setMax(model.getMaximumPrice());
holder.setQuantityUnit(model.getQuantityUnit());
holder.setQuantity(model.getQuantity());
}
#NonNull
#Override
public ViewHolder2 onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_post, parent, false);
return new ViewHolder2(v);
}
};
Fbra1.notifyDataSetChanged();
recyclerView.hasFixedSize();
recyclerView.setLayoutManager(new LinearLayoutManager(MyPost.this));
Fbra1.startListening();
recyclerView.setAdapter(Fbra1);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
#Override
protected void onRestart() {
super.onRestart();
finish();
}
}
Recycler view is only showing very last item that was added to database.
Note:-
The item.xml file has width set to wrap content.
if i remove the childEventListener the recyclerView shows multiple items with
null on the place of TextView(These are those textView on which data retrived was supposed to be set)
Here is the problem: r2.addChildEventListener
Classes implementing this interface can be used to receive events about changes in the child locations of a given DatabaseReference ref. Attach the listener to a location using addChildEventListener(ChildEventListener) and the appropriate method will be triggered when changes occur.
Firebase Docs
What you should use is a r2.addListenerForSingleValueEvent
This returns the children of the specific node. But now since you are using FirebaseUI library, you will not need to listen as it is a listener itself.
So your code should listen to the root of the posts. This r3=r2.child(dataSnapshot.getKey()); is unnecessary because it only gets one entry
Change to this
FirebaseRecyclerOptions<RecyclerCropView> options = new FirebaseRecyclerOptions.Builder<RecyclerCropView>().setQuery(r2, RecyclerCropView.class).build();
Fbra1 = new FirebaseRecyclerAdapter<RecyclerCropView, ViewHolder2>(options) {
#Override
protected void onBindViewHolder(#NonNull ViewHolder2 holder, int position, #NonNull RecyclerCropView model) {
holder.setProductImage(model.getProduct_Image());
holder.setProduct(model.getProduct());
holder.setMax(model.getMaximumPrice());
holder.setQuantityUnit(model.getQuantityUnit());
holder.setQuantity(model.getQuantity());
}
#NonNull
#Override
public ViewHolder2 onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_post, parent, false);
return new ViewHolder2(v);
}
};
Fbra1.notifyDataSetChanged();
recyclerView.hasFixedSize();
recyclerView.setLayoutManager(new LinearLayoutManager(MyPost.this));
Fbra1.startListening();
recyclerView.setAdapter(Fbra1);

How to refresh data in FirebaseAdapter using FirebaseUI when query is changed?

I want to allow users to sort and filter data. I have this implementation of Firebase Adapter and everything is working great. When I want to filter data using e.g. price, data in RecyclerView is not changing. Can you please help and tell what am doing wrong?
This is the query - depends on what user selected from Spinner:
Query query;
if (!priceLower.equals("Any")) { // option from Spinner
query = FirebaseDatabase.getInstance()
.getReference()
.child("Students")
.orderByChild("price")
.startAt(priceLower)
.limitToLast(50);
} else if (!priceHigher.equals("Any")) {
query = FirebaseDatabase.getInstance()
.getReference()
.child("Students")
.orderByChild("price")
.endAt(priceHigher)
.limitToLast(50);
} else {
query = FirebaseDatabase.getInstance()
.getReference()
.child("Students")
.orderByChild("price")
.limitToLast(50);
}
getData(query);
This is the method to fetch data from Firebase:
public void getData(Query query) {
FirebaseRecyclerOptions<Students> firebaseOptions =
new FirebaseRecyclerOptions.Builder<Students>()
.setQuery(query, Students.class)
.build();
FirebaseAdapter = new FirebaseRecyclerAdapter<Students, StudentsHolder>(firebaseOptions) {
#NonNull
#Override
public StudentsHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_students, parent, false);
return new StudentsHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull StudentsHolder holder, int position, #NonNull final Students student) {
holder.setName(student.getName());
holder.setPrice(student.getPrice());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// ...
}
});
}
#Override
public void onDataChanged() {
progressDialog.setVisibility(View.GONE);
mRecyclerView.setAdapter(FirebaseAdapter);
}
};
}
To solve this, you should add at the end of your getData() method, the following line of code:
FirebaseAdapter.startListening();
This means that everytime you change the query, you create and set a new adapter and you also start listening for changes.
#Override
public void onDataChanged() {
progressDialog.setVisibility(View.GONE);
mRecyclerView.setAdapter(FirebaseAdapter);
}
This method is called only when data on firebase database is changed. But according to your requirement, this data remains the same and only your applied filter or sorting method is changing. Therefore this method will never be called and firebaseadapter will reflect original query only.
Try it this way:
public void getData(Query query) {
FirebaseRecyclerOptions<Students> firebaseOptions =
new FirebaseRecyclerOptions.Builder<Students>()
.setQuery(query, Students.class)
.build();
FirebaseAdapter = new FirebaseRecyclerAdapter<Students, StudentsHolder>(firebaseOptions) {
#NonNull
#Override
public StudentsHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_students, parent, false);
return new StudentsHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull StudentsHolder holder, int position, #NonNull final Students student) {
holder.setName(student.getName());
holder.setPrice(student.getPrice());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// ...
}
});
}
#Override
public void onDataChanged() {
}
};
progressDialog.setVisibility(View.GONE);
mRecyclerView.setAdapter(FirebaseAdapter);
}
getData() method should be called whenever your filter is changed.

Categories

Resources