How can I use RecyclerView in fragments using Firestore - Android - java

I want to use a recyclerView to show some products, they are inside firebase-firestore and, to get them I use a query. After this, I call the adapter to show but nothing appears.
You can see my adapter where:
public class ProdutoRecyclerAdapter extends FirestoreRecyclerAdapter <Produto, ProdutoRecyclerAdapter.ProdutoHolder> {
public ProdutoRecyclerAdapter(#NonNull FirestoreRecyclerOptions<Produto> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull ProdutoHolder produtoHolder, int i, #NonNull Produto produto) {
produtoHolder.tNome.setText(produto.getNomeP());
produtoHolder.tPontos.setText(String.valueOf(produto.getPontosP()));
}
#NonNull
#Override
public ProdutoHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.lista_produtos,parent,false);
ProdutoHolder holder = new ProdutoHolder(v);
return holder;
}
class ProdutoHolder extends RecyclerView.ViewHolder{
TextView tNome;
TextView tPontos;
public ProdutoHolder(#NonNull View itemView) {
super(itemView);
tNome = itemView.findViewById(R.id.nomeP);
tPontos= itemView.findViewById(R.id.pontosP);
}
}
And here there is my fragment:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.store,null);
toolbar = view.findViewById(R.id.toolbar_loja);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
Query query = produtoRef.orderBy("pontos");
FirestoreRecyclerOptions<Produto> option = new FirestoreRecyclerOptions.Builder<Produto>()
.setQuery(query, Produto.class)
.build();
adapter = new ProdutoRecyclerAdapter(option);
recyclerView = (RecyclerView) view.findViewById(R.id.produtos_list);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(adapter);
return view;
}
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
#Override
public void onStop() {
super.onStop();
adapter.stopListening();
}
I don't have any error, but nothing appears on the screen, and I already did the debug and I know the variables are passing the right value.

At your adapter, you should have the override of getItemCount() and getItem()
/**
* Gets the item at the specified position from the backing snapshot array.
*
* #see ObservableSnapshotArray#get(int)
*/
#NonNull
public T getItem(int position) {
return mSnapshots.get(position);
}
#Override
public int getItemCount() {
return mSnapshots.isListening(this) ? mSnapshots.size() : 0;
}
where mSnapshots is your array of data.
Docuementation here

Related

Data is duplicating when navigate back to previous fragment

I am using navigation component in my app I have 2 fragments one fragments list of items and another shows detail of an item when user clicks on an item in fragments 1 it goes to detail fragment and when I switch back to first fragment then all the listing duplicates again.
Below is my code:
CakeFragment.java
public class CakeFragment extends Fragment {
List<AllCakes> allCakeList = new ArrayList<>();
AllCakesAdapter adapter;
BottomNavigationView navView;
FragmentCakeBinding fragmentCakeBinding;
public CakeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
fragmentCakeBinding = FragmentCakeBinding.inflate(inflater,container,false);
navView = getActivity().findViewById(R.id.navView);
navView.setVisibility(View.GONE);
getAllCakes();
return fragmentCakeBinding.getRoot();
}
private void getAllCakes(){
Retrofit retrofit = RetrofitClient.getInstance();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<AllCakes>> call = apiService.getAllCake();
call.enqueue(new Callback<List<AllCakes>>() {
#Override
public void onResponse(Call<List<AllCakes>> call, Response<List<AllCakes>> response) {
fragmentCakeBinding.cakeProgress.setVisibility(View.INVISIBLE);
fragmentCakeBinding.allCakeRecycler.setHasFixedSize(true);
fragmentCakeBinding.allCakeRecycler.setLayoutManager(new LinearLayoutManager(getActivity()));
allCakeList.addAll(response.body());
adapter = new AllCakesAdapter(getActivity(),allCakeList);
fragmentCakeBinding.allCakeRecycler.setAdapter(adapter);
}
#Override
public void onFailure(Call<List<AllCakes>> call, Throwable t) {
fragmentCakeBinding.cakeProgress.setVisibility(View.INVISIBLE);
TastyToast.makeText(getActivity(),t.getMessage(),TastyToast.LENGTH_SHORT,TastyToast.ERROR).show();
}
});
}
}
AllCakesAdapter.java
public class AllCakesAdapter extends RecyclerView.Adapter<AllCakesAdapter.ViewHolder> {
Context context;
List<AllCakes> allCakeList;
public AllCakesAdapter(Context context, List<AllCakes> allCakeList) {
this.context = context;
this.allCakeList = allCakeList;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.allcakes_row,parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
AllCakes model = allCakeList.get(position);
Glide.with(context).load(model.getImgurl()).into(holder.allCakeImg);
holder.allCakeName.setText(model.getName());
holder.cakeDisPrice.setPaintFlags(holder.cakeDisPrice.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.moreCake.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NavController navController = Navigation.findNavController((Activity) context,R.id.fragment);
navController.navigate(R.id.cakeDetailFragment);
}
});
}
#Override
public int getItemCount() {
return allCakeList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView allCakeImg;
Button moreCake;
TextView allCakeName;
public ViewHolder(#NonNull View itemView) {
super(itemView);
allCakeImg = itemView.findViewById(R.id.allCakeImg);
moreCake = itemView.findViewById(R.id.moreCake);
allCakeName = itemView.findViewById(R.id.allCakeName);
}
}
}
CakeDetailFragment.java
public class CakeDetailFragment extends Fragment {
FragmentCakeDetailBinding fragmentCakeDetailBinding;
public CakeDetailFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
fragmentCakeDetailBinding = FragmentCakeDetailBinding.inflate(inflater,container,false);
return fragmentCakeDetailBinding.getRoot();
}
}
Why is this happening?
private void getAllCakes(){
...
allCakeList.addAll(response.body());
adapter = new AllCakesAdapter(getActivity(),allCakeList);
fragmentCakeBinding.allCakeRecycler.setAdapter(adapter);
}
...
you're calling:
allCakeList.addAll(response.body());
every time without clearing your list out.
you have to clear that list:
allCakeList.clear();
allCakeList.addAll(response.body());
this is something you can easily determine yourself by just putting a breakpoint on your allCakeList to see what's inside it, if you haven't ever done this before, you should consider trying it out
Try the following and see if it solves your issue
CakeFragment.java
allCakeList.clear();
allCakeList.addAll(response.body());
//rest of the logic remains same

showing RecyclerView on fragment with data from Firebase

I'm having a problem with making a RecycleView in a fragment with data from Firebase. I expect the app to show the RecycleView after I clicked on a button to change from one fragment to the RecycleView fragment, but it does change the showed fragment but it does not show anything.
I know there are plenty of questions like this but I don't seem to find the correct solution to this problem.
I've made everything needed for a Firebase RecyclerView, and also tried to build it inside an activity instead fragment and it did work, but not with the fragment.
I've tried to initialize the adapter and recyclerview inside the onCreateView method, onActivityCreated, and onViewCreated method and none of them seem to be working.
Here's my fragment code:
private KidAdapter adapter;
private RecyclerView recyclerView;
Button button;
View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.uangsaku_menu_fragment, container, false);
button = view.findViewById(R.id.btn_add);
button.setOnClickListener(this);
recyclerView = view.findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
setUpRecyclerView();
return view;
}
#Override
public void onClick(View v) {
if(v.getId() == R.id.btn_add){
Intent intent = new Intent(getActivity(), Register.class);
startActivity(intent);
}
}
public void setUpRecyclerView(){
Query query = FirebaseDatabase.getInstance().getReference("kids");
FirebaseRecyclerOptions<kidData> options = new FirebaseRecyclerOptions.Builder<kidData>()
.setQuery(query, kidData.class)
.build();
adapter = new KidAdapter(options);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
#Override
public void onStart() {
super.onStart();
if (adapter != null) {
adapter.startListening();
}
}
#Override
public void onStop() {
super.onStop();
if (adapter != null) {
adapter.stopListening();
}
}
}
The adapter class
public class KidAdapter extends FirebaseRecyclerAdapter<kidData, KidAdapter.KidViewHolder> {
public KidAdapter(#NonNull FirebaseRecyclerOptions<kidData> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull KidViewHolder holder, int position, #NonNull kidData model) {
holder.nama.setText(model.getKidName());
holder.balance.setText(model.getKidBalance());
holder.limit.setText("Limit: "+model.getKidLimit());
holder.spending.setText("Spending xxx.xxx");
}
#NonNull
#Override
public KidViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.kids_card,
viewGroup, false);
return new KidViewHolder(v);
}
public class KidViewHolder extends RecyclerView.ViewHolder {
TextView nama, balance, limit, spending;
public KidViewHolder(#NonNull View itemView) {
super(itemView);
nama = itemView.findViewById(R.id.tv_nama);
balance = itemView.findViewById(R.id.tv_balance);
limit = itemView.findViewById(R.id.tv_dailylimit);
spending = itemView.findViewById(R.id.tv_dailyspending);
}
}
}
The kidData model class
public class kidData {
String kidName, kidEmail, kidDoB, kidLimit, kidBalance;
public kidData(){
}
public kidData(String kidName, String kidEmail, String kidDoB, String kidLimit, String kidBalance) {
this.kidName = kidName;
this.kidEmail = kidEmail;
this.kidDoB = kidDoB;
this.kidLimit = kidLimit;
this.kidBalance = kidBalance;
}
public String getKidName() {
return kidName;
}
public String getKidEmail() {
return kidEmail;
}
public String getKidDoB() {
return kidDoB;
}
public String getKidLimit() {
return kidLimit;
}
public String getKidBalance() {
return kidBalance;
}
}
The problem in your code is the use of the following line of code:
recyclerView.setHasFixedSize(true);
And this is because when using the latest version of Firebase-UI library, there is no need to set the size of the RecyclerView as fixed. The solution for solving this problem is to simply remove/comment the above line of code. That's it!

notifyDataSetChanged didn't work on adapter

I have activity and 3 fragments in ViewPage.
In last fragment I have recycleView, if i move to this page i want to refresh recycle view and its work only if I call:
mAdapter = new LocationAdapter(mListener.loadLocationList());
mRecyclerView.setAdapter(mAdapter);
This should work aswell after call notifyDataSetChanged but didn't.
What can we wrong ?
in last fragment:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mRecyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new LocationAdapter(mListener.loadLocationList());
mRecyclerView.setAdapter(mAdapter);
}
private class HistoryFragmentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (HISTORY_FRAGMENT_SELECTED.equals(intent.getAction())){
updateLocationList();
}
}
}
private void updateLocationList() {
mAdapter = new LocationAdapter(mListener.loadLocationList());
mRecyclerView.setAdapter(mAdapter);
}
i wanted notifydatasetchanged instead of 2 lines in updatelocationlist()
LocationAdapter:
public class LocationAdapter extends RecyclerView.Adapter<LocationAdapter.LocationViewHolder> {
private LinkedList<LatLng> mDataset;
public static class LocationViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public View mTextView;
public LocationViewHolder(View v) {
super(v);
mTextView = v;
}
}
public LocationAdapter(LinkedList<LatLng> myDataset) {
mDataset = myDataset;
}
#NonNull
#Override
public LocationAdapter.LocationViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int i) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View v = layoutInflater.inflate(R.layout.my_text_view, parent, false);
LocationViewHolder vh = new LocationViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(#NonNull LocationAdapter.LocationViewHolder locationViewHolder, int position) {
TextView textView = locationViewHolder.mTextView.findViewById(R.id.textView3);
textView.setText(String.valueOf(mDataset.get(position)));
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
The problem is you set data only in your adapter's constructor.
When you call notifyDataSetChanged you can't expect that your adapter will take new data magically. You have to set it by yourself.
My suggestion would be add a setData method in your adapter and call it before notifyDataSetChanged.
Something like this in your adapter:
public void setData(LinkedList<LatLng> myDataset) {
mDataset = myDataset
}
and in your caller:
private void updateLocationList() {
mAdapter.setData(mListener.loadLocationList());
mAdapter.notifyDataSetChanged();
}

RecyclerView in Fragment – How to retrieve data from an Array List

So I saw this tutorial that creates a recyclerview in fragments of navigation drawer. In this tutorial, the data is stored in an array. What I am trying to do right now is to store the data in an ArrayList.
Here is my code in ‘Tutor’ Fragment:
public class Tutors extends Fragment
{
View myView;
private ArrayList<TutorModel> tutorModelList = new ArrayList<>();
private RecyclerView recyclerView;
private MyAdapter tutorAdapter;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getTutorData();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
myView = inflater.inflate(R.layout.admin_manage_tutors,
container, false);
recyclerView = myView.findViewById(R.id.recycler_view);
tutorAdapter = new MyAdapter(tutorModelList);
RecyclerView.LayoutManager mLayoutManager = new
LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(tutorAdapter);
return myView;
}
public void getTutorData()
{
TutorModel tutor = new TutorModel("Juan", "Computer Programming");
tutorModelList.add(tutor);
tutor = new TutorModel("Santiago", "French History");
tutorModelList.add(tutor);
tutor = new TutorModel("Jose", "Philippine History");
tutorModelList.add(tutor);
tutorAdapter.notifyDataSetChanged();
}
}
This is the code of my Adapter:
public class MyAdapter extends
RecyclerView.Adapter<MyAdapter.MyViewHolder> {
public List<TutorModel> tutorList;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView name, subjects;
public MyViewHolder(View view) {
super(view);
name = view.findViewById(R.id.name_tutor);
subjects = view.findViewById(R.id.subjects_tutor);
}
}
public MyAdapter(List<TutorModel> tutorList) {
this.tutorList = tutorList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.tutor_list, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
TutorModel tutor = tutorList.get(position);
holder.name.setText(tutor.getTutorName());
holder.subjects.setText(tutor.getTutorSubject());
}
#Override
public int getItemCount() {
return tutorList.size();
}
}
And this is my getter/setter code:
public class TutorModel
{
String tutorName, tutorSubject;
public String getTutorName() {
return tutorName;
}
public void setTutorName(String tutorName) {
this.tutorName = tutorName;
}
public String getTutorSubject() {
return tutorSubject;
}
public void setTutorSubject(String tutorSubject) {
this.tutorSubject = tutorSubject;
}
public TutorModel(String tutorName, String tutorSubject) {
this.tutorName = tutorName;
this.tutorSubject = tutorSubject;
}
public TutorModel() {
}
}
When I try to run the app, it crashes.
onCreateView will be invoked after onCreate but getTutorData(); is notifying the adapter which is yet to be initialized by onCreateView so initialize your adapter before getTutorData(); method call
or better way is call getTutorData inside onCreateView
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
myView = inflater.inflate(R.layout.admin_manage_tutors,
container, false);
recyclerView = myView.findViewById(R.id.recycler_view);
tutorAdapter = new MyAdapter(tutorModelList);
RecyclerView.LayoutManager mLayoutManager = new
LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(tutorAdapter);
getTutorData();
return myView;
}
You have initialized your tutorAdapter in onCreateView() but onCreate gets called before onCreateView so your adapter is null..just edit your onCreate like the code below..
#Override
public void onCreate(#Nullable Bundle savedInstanceState)
{ super.onCreate(savedInstanceState);
tutorAdapter = new MyAdapter(tutorModelList);
getTutorData(); }

FireBaseRecyclerView in FirebaseRecyclerView can not be applied to : (arguments)

I'm trying to retrieve data from Firebase to Recyclerview. I've already implement all other steps but now i'm getting error at the last stage when i'm implementing the adapter(FirebaseRecyclerAdapter).
My Fragment
public class Journal extends Fragment {
RecyclerView recyclerView;
DatabaseReference myref;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_journal,container,false);
myref= FirebaseDatabase.getInstance().getReference("/Journal");
recyclerView = (RecyclerView) v.findViewById(R.id.journal_rv);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
FirebaseRecyclerAdapter<JournalEntry, JournalHolder> adapter = new FirebaseRecyclerAdapter<JournalEntry, JournalHolder>(
JournalEntry.class,
R.layout.journal_list_item,
JournalHolder.class,
myref) {
#Override
protected void onBindViewHolder(JournalHolder holder, int position, JournalEntry model) {
holder.setTitle(model.getEvent_title());
holder.setContent(model.getEvent_content());
holder.setSchool(model.getSchool_name());
holder.setDate(model.getDate());
holder.setStudents(model.getNumber_of_students());
holder.setSchoolIv(model.getImg_url());
}
#Override
public JournalHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
};
return v;
}
public static class JournalHolder extends RecyclerView.ViewHolder{
TextView Jtitle , Jcontent,Jschool,Jstudents,jdate;
ImageView schoolIV;
public JournalHolder(View itemView) {
super(itemView);
Jtitle =(TextView)itemView.findViewById(R.id.journal_title);
Jcontent =(TextView)itemView.findViewById(R.id.event_content);
Jschool =(TextView)itemView.findViewById(R.id.journal_school);
Jstudents =(TextView)itemView.findViewById(R.id.journal_students);
jdate = (TextView)itemView.findViewById(R.id.journal_dates) ;
schoolIV = (ImageView)itemView.findViewById(R.id.journal_image);
}
public void setTitle(String title) {
Jtitle.setText(title);
}
public void setContent(String content) {
Jcontent.setText(content);
}
public void setSchool(String school) {
Jschool.setText(school);
}
public void setDate(String date) {
jdate.setText(date);
}
public void setStudents(String students) {
Jstudents.setText(students);
}
public void setSchoolIv(String schoolIv) {
Picasso.with(itemView.getContext())
.load(schoolIv)
.into(schoolIV);
}
}
}
This is what is shown as error
FireBaseRecyclerView in FirebaseRecyclerView can not be applied to :
JournalEntry.class (java...consti.last.database.JournalEntry>)
R.layout.journal_list_item (int)
BlogViewHolder.class (java...consti.last.Journal.BlogViewHolder>)
myref (com...firebase.database.DatabaseReference)
Please help me. I tried all the methods but nothing works.
Or if you have any advice...thank you in advance.
PS : I'm using Fragments
under Activity declaration write:-
private FirebaseRecyclerAdapter<JournalEntry, JournalHolder> adapter;
then inside onCreateView():
adapter = new FirebaseRecyclerAdapter<JournalEntry, JournalHolder>(
JournalEntry.class,
R.layout.journal_list_item,
JournalHolder.class,
myref) { //be sure that these classes and parameters are correct..
above return v;:
recyclerView.setAdapter(adapter);
Use this method instead of onBindViewHolder:
#Override
protected void populateViewHolder(JournalHolder holder,JournalEntry model, int position) {
Be sure you have all the correct methods in the model class JournalEntry.class and holder class JournalHolder.class and that its the right location and right layout.
I finally found solution here:Firebase RecyclerView
I had the same problem.
In this version FirebaseResycleView use the following:
Query query = FirebaseDatabase.getInstance()
.getReference(); // you can add child as .child("child_name");
FirebaseRecyclerOptions<JournalEntry> options = new FirebaseRecyclerOptions.Builder<JournalEntry>()
.setQuery(query, JournalEntry.class)
.build();
FirebaseRecyclerAdapter<JournalEntry, JournalHolder> adapter = new FirebaseRecyclerAdapter<JournalEntry, JournalHolder>(
options) {
#Override
protected void onBindViewHolder(JournalHolder holder, int position, JournalEntry model) {
holder.setTitle(model.getEvent_title());
holder.setContent(model.getEvent_content());
holder.setSchool(model.getSchool_name());
holder.setDate(model.getDate());
holder.setStudents(model.getNumber_of_students());
holder.setSchoolIv(model.getImg_url());
}
#Override
public JournalHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.journal_list_item, parent, false);
return new JournalHolder(view);
}
};
And after don't forget:
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(adapter);
adapter.startListening();

Categories