Retrofit2 in Android Fragment how to implement correctly? - android

I'm using Retrofit 2 to consume an API. I have a service (interface) which returns a list:
#GET("atenciones")
Call<List<Atencion>> getAtenciones(#Query("medico_id") int id, #Query("date1") String date1, #Query("date2") String date2)
Where should I do the request? In the MainActivity which contains the Fragment and send the result list using Bundle? or should do the request in the Fragment? this is a list not a single object. Which is the correct way?
Edit
Try to call retrofit in Fragment, this is my code:
public class FragmentRendicion extends Fragment {
private LinearLayoutManager layoutManager;
View rootView;
APIService api;
public FragmentRendicion() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_rendicion, container, false);
api= ApiUtils.getAPIService();
getAtenciones();
return rootView;
}
private void getAtenciones() {
//using static parameters
Call<List<Atencion>> call= api.getAtenciones(293,"2014-10-13","2014-10-13");
call.enqueue(new Callback<List<Atencion>>() {
#Override
public void onResponse(Call<List<Atencion>> call, Response<List<Atencion>> response) {
System.out.println("estamos aquiiii "+response.message());
List<Atencion> atenciones= response.body();
layoutManager= new LinearLayoutManager(getActivity());
RecyclerView recycler= (RecyclerView)rootView.findViewById(R.id.rvRendicion);
recycler.setLayoutManager(layoutManager);
RvRendicionAdapter rvAdapter= new RvRendicionAdapter(atenciones);
recycler.setAdapter(rvAdapter);
}
#Override
public void onFailure(Call<List<Atencion>> call, Throwable t) {
System.out.println("FALLOOOOO: "+t.getMessage());//HERE RETUNRS NULL
}
});
}
}
Can someone tell me is if it correct way to call retrofit2 in fragment?

It depends on the structure of your code but since you are using fragments my best guess is you should do it in the Fragment.
In the onResponseOK of the Retrofit call you will have something like this:
#Override
public void onResponseOK(Response<List<Atencion>> response) {
...
//access data here
}
In the callback you get your list. Pass the list to the adapter of your (I suppose) Recycler/Listview. Access the list like this:
List<Atencion> myList = response.body();

Related

Why can't I access adapter submitList() method from within onChanged()?

Can someone tell me why the submitList() method can't be accessed in the following code? Similar code is working fine in another test app, although that's in an Activity.
public class MyFFBooksFragment extends Fragment {
private int currentBook;
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private GamebookViewModel gamebookViewModel;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable
ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.myffbooks_fragment, container, false);
mLayoutManager = new LinearLayoutManager(getActivity());
final ArrayList<BookItem> bookList = new ArrayList<>();
final BookItemAdapter maAdapter = new BookItemAdapter(bookList);
mRecyclerView = view.findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(maAdapter);
gamebookViewModel = ViewModelProviders.of(this).get(GamebookViewModel.class);
gamebookViewModel.getAllGamebooks().observe(this, new Observer<List<Gamebook>>() {
#Override
public void onChanged(#Nullable List<Gamebook> gamebooks) {
maAdapter.submitList(bookList);
}
});
return view;
}
}
Perhaps you should not use it in onCreateView(). Try use call method that triggers observation of VM after an Activity had been created, near onStart(). Maybe it will help.
When a new page is available, we call submitList() method of the PagedListAdapter class
override fun onStart() {
super.onStart()
gamebookViewModel.callFunc()
}
Also as I understand, you call getAllGamebooks() which triggers some actions and return you LiveData<*> and you subscribe on it with observe(). I recommend you to separate this on 2 methods:
subscribe
action
Use PagedList instead of list like below:
gamebookViewModel.getAllGamebooks().observe(this, new Observer<PagedList<Gamebook>>() {
#Override
public void onChanged(#Nullable PagedList<Gamebook> gamebooks) {
maAdapter.submitList(bookList);
}
});
And in your viewmodel too.
Found the problem. My adapter wasn't extending ListAdapter, so no wonder it couldn't find the submitList() method. Rookie error.

RecyclerView is a null in fragment

I have MainActivity,which contain viewpager,which contain 2 fragments:
MainActivity
ViewPager viewPager = findViewById(R.id.viewPager);
MyPagerAdapter adapter = new MyPagerAdapter(this,
getSupportFragmentManager());
viewPager.setAdapter(adapter);
TabLayout tabLayout = findViewById(R.id.TabLayout);
tabLayout.setupWithViewPager(viewPager);
Fragment fragmentHour=adapter.getItem(1);
onChangeDayListener2= (OnChangeDayListener) fragmentHour;
I am also have interface for send data from main activity in fragment:
public interface OnChangeDayListener{
void OnChange(WeatherList list,int pos);
}
but when i try to work with this callback in interface my recycler adapter always is null:
HourWeatherFragment:
public class HourWeatherFragment extends Fragment implements MainScreen.OnChangeDayListener{
private RecyclerView recyclerView;
private AdapterHour adapter;
private ArrayList<Weather> days;
Context context;
static HttpClient httpClient;
public HourWeatherFragment() {
// Required empty public constructor
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
days=new ArrayList<>();
adapter = new AdapterHour(days,getActivity());
LinearLayoutManager LayoutManager = new LinearLayoutManager(getActivity().getBaseContext(),
LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(LayoutManager);
recyclerView.setAdapter(adapter);
days.add(new Weather());
recyclerView.getAdapter().notifyDataSetChanged();
Log.d("recycler","adapter seted!");
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_hour_weather, container, false);
recyclerView = rootView.findViewById(R.id.hour_list);
httpClient=new HttpClient();
MyAsyncTask task=new MyAsyncTask();
// task.execute("lviv");
return rootView;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context=context;
}
#Override
public void OnChange(WeatherList list,int pos) {
String date=list.getAverageWeather().get(pos).getDate();
ArrayList<Weather> weather=list.getByDate(date);
Log.d("ldata","size of weather for adding="+weather.size());
// days.add(new Weather());
if(adapter!=null){
adapter.notifyDataSetChanged();
Log.d("tag","first null!");}
else{
try{
recyclerView.getAdapter();}
catch (NullPointerException ex){
Log.d("tag","null!");//THERE ALWAYS NULL
}}
So,firstly creating activity,then i start load data and when data loaded i send data from MainActivity to HourWeatherFragment throught callback,
but when i receive data in fragment,my adapter is null,how i can fix it?
You can create a static singleton class to hold data and retrieve that data in the fragment.
public class DataHolder {
//create static variables here.
}
In your fragment simply retrieve the data and make it null after that.
Alternative solution
Another solution is to use Bundles. Follow the bundle solution here.
You must cast RecyclerView.
in your fragment and onCreateView method :
recyclerView = (RecyclerView) rootView.findViewById(R.id.hour_list);
I know this is an old question, but contrary to the accepted answer I think the issue is that the handle to the fragment's widget such as recyclerView is not available in the OnCreateView function.
The handles (or pointers if you wish) to the widgets are only guaranteed to exist in the OnViewCreated function.
This documentation has good description of the lifecycle of fragments:
https://guides.codepath.com/android/Creating-and-Using-Fragments
an extract is:
// This event is triggered soon after onCreateView().
// onViewCreated() is only called if the view returned from onCreateView() is non-null.
// Any view setup should occur here. E.g., view lookups and attaching view listeners.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ListView lv = (ListView) view.findViewById(R.id.lvSome);
lv.setAdapter(adapter);
}
Hope this helps.

How to select a person from recyclerview and send it to the activity

I am working on a app that controls tasks. I have to choose a person from a list to be the handler of the task. I have made a TextView that when clicked opens a DialogFragment that contains a recyclerview. How do I select a person from the recyclerView and send it back to the activity. I have tried working with interfaces but that hasn't worked yet.
Below are DialogFragment and the MainActivity:
AlertDialogFragment
RecyclerView recyclerViewPersons;
List<PERSON> personList = new ArrayList<>();
PersonsAdapter personsAdapter;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_persons_list, container);
recyclerViewPersons = v.findViewById(R.id.recyclerViewPersons);
recyclerViewPersons.setLayoutManager(new LinearLayoutManager(this.getActivity()));
personsAdapter = new PersonsAdapter(getActivity());
personsAdapter.setData(personList);
recyclerViewPersons.setAdapter(personsAdapter);
return v;
}
public void setAdapterData(List<PERSON> personList) {
this.personList = personList;
}
}
MainActivity
PersonsAdapter personsAdapter;
final FragmentManager fragmentManager = getSupportFragmentManager();
final AlertDialogFragment alertDialogFragment = new AlertDialogFragment();
protected void onCreate(Bundle savedInstanceState) {
txtPersons.setOnClickListener(v -> {
alertDialogFragment.setAdapterData(personList);
alertDialogFragment.show(fragmentManager, "dialog recycler");
});
}
You said that you managed to send the data from Adapter to DialogFragment. Now you can send this data from DialogFragment to Activity.
For this you can create another interface inside or outside the DialogFragment. Then you can implements this fragment with the activity and override this interface with its body.
Now, inside the DialogFragment override the onAttach method and instantiate this inferface instance.
Interface
public interface OnMyInterface {
public void onMyData(your data);
}
DialogFragment
private OnMyInterface onMyInterface;
public ForgotAndResetPasswordFragment() {}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_your_layout, container, false);
}
#Override
public View onViewCreated(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//send the data to activity using on onMyInterface
onMyInterface.onMyData(your data);
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
onMyInterface = (OnMyInterface) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString());
}
}
Activity
public class MyActivity implements OnMyInterface {
#Override
public void onMyData(your data) {
//get this data
}
}
Or you can try this approach also:
https://camposha.info/source/android-data-passing-fragment-activity-via-intent
First define an interface(which sends Person data) in Fragment & let it be implemented in the Activity.
Then, define one more interface in Recycler Adapter & let it be implemented in the Fragment. This interface will get the information after clicking on Recycler item. Invoke this interface in Adapter's layout on click.
Then finally whatever data you get in fragment, pass it back to activity via first interface.
Hope this helps?

Android Studio - Update RecyclerView from Dialog Fragment in Menu Item

Hey guys I really need your help. I've spent like 5 days trying to get my recyclerview to update only when the user presses OK on a dialogbox, that appears from the menu actionbar. I've tried every possible method I could think of, every method I've seen on stackoverflow, YouTube, etc. and none of them worked for me.
How do I get the recyclerview in a fragment to update after dialogbox closes? I know there are similar questions regarding updating the menu, and (recyclerviews with dialogfragments), but none of them have a combination.
Out of the countless attempts, the current code configuration posted below isn't causing any errors, however, the recyclerview remains blank. The closest attempt I had to finding a solution, was creating an adapter and setting up the recycler in onOptionsItemSelected. But obviously, it updates only when the user clicks the button, and the initial click would create a blank recyclerview.
Fragment:
(There's a lot of repeated commented code from different attempts)
public class ExerciseRoutine extends Fragment implements ExerciseRoutine_Dialog.RoutineDialogListener{
private String Routine_name, Routine_split;
private ArrayList<ExerciseRoutine_Information> Routine_information = new ArrayList<>();
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
#Override
public void sendInput(String name, String split, RecyclerView.Adapter DialogAdapter) {
Routine_name = name;
Routine_split = split;
adapter = DialogAdapter;
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_fragment, container, false);
//Report that this fragment would like to participate in populating the options menu by
//receiving a call to onCreateOptionsMenu(Menu, MenuInflater) and related methods.
//If true, the fragment has menu items to contribute.
setHasOptionsMenu(true);
recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
//BuildRecyclerView();
//recyclerView.setHasFixedSize(true); //If the Recyclerview is static
/*Routine_information.add(new ExerciseRoutine_Information(Routine_name, Routine_split));
recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
//recyclerView.setHasFixedSize(true); //If the Recyclerview is static
layoutManager = new LinearLayoutManager(getActivity());
adapter = new ExerciseRoutineAdapter(Routine_information);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);*/
return view;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.exercise_routine_menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.action_addRoutine:
ExerciseRoutine_Dialog routineDialog = new ExerciseRoutine_Dialog();
routineDialog.setTargetFragment(ExerciseRoutine.this, 1);
routineDialog.show(getFragmentManager(), "Routine Dialog");
//Routine_information.add(new ExerciseRoutine_Information(Routine_name, Routine_split));
BuildRecyclerView();
//adapter.notifyItemInserted(Routine_information.size());
//if(!Routine_name.equals("") && !Routine_split.equals("")) {
//}
}
return super.onOptionsItemSelected(item);
}
public void BuildRecyclerView(){
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
}
public void BuildAdapter(){
//adapter = new ExerciseRoutineAdapter(getContext(),Routine_information);
adapter.notifyItemInserted(Routine_information.size());
}
}
My Dialog Fragment:
public class ExerciseRoutine_Dialog extends DialogFragment{
private TextView ActionOK, ActionCANCEL;
private EditText Routine_name, Routine_split;
private RoutineDialogListener activityCommander;
private ArrayList<ExerciseRoutine_Information> Routine_information = new ArrayList<>();
private RecyclerView.Adapter adapter;
public interface RoutineDialogListener{
void sendInput(String name, String split, RecyclerView.Adapter DialogAdapter);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
activityCommander = (RoutineDialogListener) getTargetFragment();
}catch(ClassCastException e){
throw new ClassCastException(context.toString() + "Must Implement RoutineDialogListener");
}
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_dialog, container, false);
Routine_name = view.findViewById(R.id.ExerciseRoutine_DialogNameInput);
Routine_split = view.findViewById(R.id.ExerciseRoutine_DialogSplitInput);
ActionOK = view.findViewById(R.id.ExerciseRoutine_DialogAction_OK);
ActionCANCEL = view.findViewById(R.id.ExerciseRoutine_DialogAction_CANCEL);
//recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
ActionCANCEL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getDialog().dismiss();
}
});
ActionOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String name = Routine_name.getText().toString();
String split = Routine_split.getText().toString();
if(!name.equals("") && !split.equals("")) {
Routine_information.add(new ExerciseRoutine_Information(name, split));
adapter = new ExerciseRoutineAdapter(getContext(), Routine_information);
activityCommander.sendInput(name, split, adapter);
adapter.notifyItemInserted(Routine_information.size());
}
getDialog().dismiss();
}
});
return view;
}
}
Your current approach seems to be to pass the RecyclerView.Adapter to the DialogFragment and try to insert the new data on-the-spot. I think this is a problematic setup. The dialog's purpose is to offer some means for the users to enter the required data, period. It should not be tasked with the job of managing the RecyclerView or its Adapter because that way your components will be very tightly coupled:
Imagine that first you use a ListView in your implementation, and suddenly someone decides to ban every ListView from your app (maybe for performance reasons) and has you exchange them all for RecyclerViews. Then your approach would force you to change the code for the DialogFragment (it would have to cater to a different type of Adapter). A more loosely coupled implementation would enable you to make changes to one component without having to rewrite too many others.
That's why I won't try to make your code work as-is, instead I'd like to show you another way:
Because the RecyclerView is part of the Fragment's UI, the Fragment is the place where code related to managing the RecyclerView belongs. It is basically possible to have the Adapter as an inner class of the Fragment but I prefer having it as a standalone class if the code gets a little longer, and also because it enforces "decoupling".
Interfaces play a very important part in good architecture, so the DialogFragment will still make use of an interface to send its data. But it's up to the class which actually implements the interface (here: the Fragment) to pass the data to any interested third parties, e.g. the RecyclerView.Adapter (The Adapter in turn could have its own interface to publish important events like clicks on list items).
Having said that, here are some code snippets:
The DialogFragment
public class ExerciseRoutine_Dialog extends DialogFragment {
private EditText Routine_name, Routine_split;
public interface RoutineDialogListener{
/**
* There is some new ExerciseRoutine_Information
*/
void sendInput(String name, String split);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_dialog, container, false);
Routine_name = view.findViewById(R.id.ExerciseRoutine_DialogNameInput);
Routine_split = view.findViewById(R.id.ExerciseRoutine_DialogSplitInput);
TextView actionOK = view.findViewById(R.id.ExerciseRoutine_DialogAction_OK);
TextView actionCANCEL = view.findViewById(R.id.ExerciseRoutine_DialogAction_CANCEL);
actionCANCEL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getDialog().dismiss();
}
});
actionOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String name = Routine_name.getText().toString();
String split = Routine_split.getText().toString();
if(!name.equals("") && !split.equals("")) {
// just send the input to the main Fragment
RoutineDialogListener listener = getListener();
if(listener != null) {
listener.sendInput(name, split);
}
}
getDialog().dismiss();
}
});
return view;
}
/**
* Tries to find a suitable listener, examining first the hosting Fragment (if any) and then the Activity.
* Will return null if this fails
* #return x
*/
private RoutineDialogListener getListener(){
RoutineDialogListener listener;
try{
Fragment onInputSelected_Fragment = getTargetFragment();
if (onInputSelected_Fragment != null){
listener = (RoutineDialogListener) onInputSelected_Fragment;
}
else {
Activity onInputSelected_Activity = getActivity();
listener = (RoutineDialogListener) onInputSelected_Activity;
}
return listener;
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
return null;
}
}
The Fragment:
public class ExerciseRoutine extends Fragment implements ExerciseRoutine_Dialog.RoutineDialogListener{
public static final String ROUTINE_DIALOG = "Routine Dialog";
private ArrayList<ExerciseRoutine_Information> routineInformations = new ArrayList<>();
private RecyclerView.Adapter adapter;
public static ExerciseRoutine instance(){
return new ExerciseRoutine();
}
#Override
public void sendInput(String name, String split) {
routineInformations.add(new ExerciseRoutine_Information(name, split));
adapter.notifyItemInserted(routineInformations.size());
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.exercise_routine_fragment, container, false);
setHasOptionsMenu(true);
RecyclerView recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter = new ExerciseRoutineAdapter(getContext(), routineInformations);
// So far you have a RecyclerView with an empty List.
recyclerView.setAdapter(adapter);
return view;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.exercise_routine_menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.action_addRoutine:
showDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
private void showDialog(){
ExerciseRoutine_Dialog routineDialog = new ExerciseRoutine_Dialog();
routineDialog.setTargetFragment(ExerciseRoutine.this, 1);
routineDialog.show(getFragmentManager(), ROUTINE_DIALOG);
}
}

Can't get FirestoreRecyclerAdapter to show items

I'm updating my app to use Firebase's Firestore database. I'm struggling to get the app to show the data that's been retrieved from the database. The data is retrieved ok, but doesn't show up. By setting breakpoints, I've established that the ViewHolder isn't being bound to the adapter at any point.
The data is being shown in a Fragment. The Fragment layout is (I've taken out irrelevant stuff like padding, sizes etc):
<android.support.constraint.ConstraintLayout
android:id="#+id/layout_charts_list"
tools:context="apppath.fragments.ChartListFragment">
<TextView
android:id="#+id/loading_list"
android:text="#string/loading_my_charts" />
<TextView
android:id="#+id/empty_list"
android:text="#string/my_charts_empty"
android:visibility="gone"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/charts_list" />
</android.support.constraint.ConstraintLayout>
The Fragment code itself is:
public abstract class ChartListFragment extends Fragment {
private FirebaseFirestore mDatabaseRef;
private FirestoreRecyclerAdapter<Chart, ChartViewHolder> mAdapter;
private Query mChartsQuery;
private RecyclerView mRecycler;
public ChartListFragment() {}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(
R.layout.fragment_charts_list, container, false);
mRecycler = rootView.findViewById(R.id.charts_list);
mRecycler.setHasFixedSize(true);
// Set up Layout Manager, and set Recycler View to use it
LinearLayoutManager mManager = new LinearLayoutManager(getActivity());
mManager.setReverseLayout(true);
mManager.setStackFromEnd(true);
mRecycler.setLayoutManager(mManager);
// Connect to the database, and get the appropriate query (as set in the actual fragment)
mDatabaseRef = FirebaseFirestore.getInstance();
mChartsQuery = getQuery(mDatabaseRef);
// Set up Recycler Adapter
FirestoreRecyclerOptions<Chart> recyclerOptions = new FirestoreRecyclerOptions.Builder<Chart>()
.setQuery(mChartsQuery, Chart.class)
.build();
mAdapter = new ChartListAdapter(recyclerOptions);
// Use Recycler Adapter in RecyclerView
mRecycler.setAdapter(mAdapter);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Add listener to charts collection, and deal with any changes by re-showing the list
mChartsQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots,
#Nullable FirebaseFirestoreException e) {
if (queryDocumentSnapshots != null && queryDocumentSnapshots.isEmpty()) {
((MainActivity) getActivity()).setPage(1);
mRecycler.setVisibility(View.GONE);
}else {
mRecycler.setVisibility(View.VISIBLE);
}
}
});
}
// HELPER FUNCTIONS
public abstract Query getQuery(FirebaseFirestore databaseReference);
}
ChartListAdapter is as follows:
public class ChartListAdapter
extends FirestoreRecyclerAdapter<Chart, ChartViewHolder> {
public ChartListAdapter(FirestoreRecyclerOptions recyclerOptions) {
super(recyclerOptions);
}
#Override
protected void onBindViewHolder(ChartViewHolder holder, int position, Chart model) {
holder.setChartName(model.getName());
// Bind Chart to ViewHolder
holder.bindToChart(model);
}
#Override
public ChartViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_chart, parent, false);
return new ChartViewHolder(view);
}
}
ChartViewHolder:
public class ChartViewHolder extends RecyclerView.ViewHolder {
private TextView chartNameView;
private String chartKey;
public ChartViewHolder(View itemView) {
super(itemView);
chartNameView = itemView.findViewById(R.id.chart_name);
}
public void setChartName(String chartName) {
chartNameView.setText(chartName);
}
public void bindToChart(Chart chart) {
chartKey = chart.getKey();
chartNameView.setText(chart.getName());
}
public String getChartKey() {
return chartKey;
}
}
The ChartListAdapter constructor is called, but onBindViewHolder and onCreateViewHolder are never called, and ChartViewHolder is never accessed at all. Am I missing a line of code? Or doing this completely wrong? I'm not all that familiar with Adapters and RecyclerViews, so I've found it quite hard to get to grips with putting it all together.
For those who came here from Google, set the lifecycle owner on the options so that start/stop listening is called automatically.
FirestoreRecyclerOptions<Chart> recyclerOptions = FirestoreRecyclerOptions.Builder<Chart>()
.setQuery(mChartsQuery, Chart.class)
.setLifecycleOwner(this)
.build();
You need to add the following, to begin listening for data, call the startListening() method. You may want to call this in your onStart() method. Make sure you have finished any authentication necessary to read the data before calling startListening() or your query will fail:
#Override
protected void onStart() {
super.onStart();
adapter.startListening();
}
more info here:
https://github.com/firebase/FirebaseUI-Android/tree/master/firestore

Categories

Resources