RecyclerView not showing data fetched from API - java

Im creating an app that shows user recipe based on ingredient they have using SpoonAcular API
The app fetched the ingredient user currently have on Firebase Realtime Database and using the data to fetch the recipe. I've created all the adapter needed to fetch the data but the app not showing the recyclerView as its should. Here's my code
RequestManager.java
public class RequestManager {
Context context;
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.spoonacular.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
public RequestManager(Context context){
this.context = context;
}
public void getRecipeByIngredient(RecipeByIngredientListener listener, List <String> IngredientList){
CallRecipeByIngredient callRecipeByIngredient = retrofit.create(CallRecipeByIngredient.class);
Call <List<RecipeIngredResponse>> call = callRecipeByIngredient.callRecipeByIngredient(context.getString(R.string.API), IngredientList, "20");
call.enqueue(new Callback<List<RecipeIngredResponse>>() {
#Override
public void onResponse(Call<List<RecipeIngredResponse>> call, Response<List<RecipeIngredResponse>> response) {
if(!response.isSuccessful()){
listener.didError(response.message());
return;
}
listener.didFetch(response.body(), response.message());
}
#Override
public void onFailure(Call<List<RecipeIngredResponse>> call, Throwable t) {
listener.didError(t.getMessage());
}
});
}
private interface CallRecipeByIngredient{
#GET("recipes/findByIngredients")
Call<List<RecipeIngredResponse>> callRecipeByIngredient(
#Query("apiKey") String apiKey,
#Query("ingredients") List <String> Ingredient,
#Query("number") String number
);
}
}
RecipeByIngredientListener
public interface RecipeByIngredientListener {
void didFetch(List<RecipeIngredResponse> response, String message);
void didError(String message);
}
RecipeByIngredientAdapter
public class RecipeByIngredientAdapter extends RecyclerView.Adapter<RecipeByIngredientViewHolder> {
Context context;
List<RecipeIngredResponse> list;
public RecipeByIngredientAdapter(Context context, List<RecipeIngredResponse> list) {
this.context = context;
this.list = list;
}
#NonNull
#Override
public RecipeByIngredientViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new RecipeByIngredientViewHolder(LayoutInflater.from(context).inflate(R.layout.list_recipebyingred , parent, false));
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(#NonNull RecipeByIngredientViewHolder holder, int position) {
holder.recipeName.setText(list.get(position).title);
holder.missedIngred.setText(list.get(position).missedIngredientCount + " Missed Ingredient");
holder.likes.setText(list.get(position).likes + " Likes");
Picasso.get().load(list.get(position).image).into(holder.recipeImage);
}
#Override
public int getItemCount() {
return list.size();
}
}
class RecipeByIngredientViewHolder extends RecyclerView.ViewHolder{
ImageView recipeImage;
TextView recipeName, missedIngred, likes;
public RecipeByIngredientViewHolder(#NonNull View itemView) {
super(itemView);
recipeImage = itemView.findViewById(R.id.recipeImage);
recipeName = itemView.findViewById(R.id.recipeName);
missedIngred = itemView.findViewById(R.id.missedIngred);
likes = itemView.findViewById(R.id.likes);
}
}
Home.java (Fragment)
RandomRecipeAdapter randomRecipeAdapter;
RequestManager manager;
RecyclerView recyclerView, recyclerFromYourFridge;
RecipeByIngredientAdapter recipeByIngredientAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_home, container, false);
manager = new RequestManager(getContext());
manager.getRandomRecipe(randomRecipeResponseListener);
recyclerFromYourFridge = (RecyclerView) v.findViewById(R.id.recyclerFromYourFridge);
//get ingredient from database
List<String> Ingredient = new ArrayList<>();
FirebaseAuth mAuth = FirebaseAuth.getInstance();
String currentUser = mAuth.getCurrentUser().getUid();
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Ingredient").child(currentUser);
reference.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if(task.isSuccessful()){
for (DataSnapshot userSnapshot : task.getResult().getChildren()){
Ingredient.add(userSnapshot.getKey());
}
}
}
});
manager.getRecipeByIngredient(recipeByIngredientListener , Ingredient);
recyclerView = (RecyclerView) v.findViewById(R.id.randomRecipeRecycler);
return v;
}
private final RecipeByIngredientListener recipeByIngredientListener = new RecipeByIngredientListener() {
#Override
public void didFetch(List<RecipeIngredResponse> response, String message) {
recyclerFromYourFridge.setHasFixedSize(true);
recyclerFromYourFridge.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
recipeByIngredientAdapter = new RecipeByIngredientAdapter(getContext(), response);
recyclerFromYourFridge.setAdapter(recipeByIngredientAdapter);
}
#Override
public void didError(String message) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
}
};
Pretty sure there's nothing wrong with the app fetching ingredient from Firebase cause i've tested it and the api capable to get recipe by using ArrayList. Is there anything wrong with this code?

The issue is because calls to the Firebase database are asynchronous, if you call the value outside of addOnCompleteListener can be empty
Change the reference.get().addOnCompleteListener to:
reference.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if(task.isSuccessful()){
List<String> Ingredient = new ArrayList<>();
for (DataSnapshot userSnapshot : task.getResult().getChildren()){
Ingredient.add(userSnapshot.getKey());
}
manager.getRecipeByIngredient(recipeByIngredientListener , Ingredient);
}
}
});

Related

notifyDataSetChanged() blanks out recyclerview

I have an issue where notifyDataSetChanged() in a response call will blank out the recyclerview but if the Adapter is initiated manually with a onClick, the recyclerview works. I have tested that the List has the items inside before calling notifyDataSetChanged() so i'm not sure what's wrong here.
[Main Activity] This works but i have to manually click the bnQuery.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiInterface = API_client.getClient().create(APIInterface.class);
etCoin = (EditText) findViewById(R.id.etCoin);
bnQuery = (Button) findViewById(R.id.bnQuery);
rcvMain = findViewById(R.id.rcvMain);
getCoinData("2");
//initRCV_Main();
bnQuery.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//getCoinData("2");
initRCV_Main();
}
});
}
private void initRCV_Main() {
rcvMainAdp = new rcvMainAdapter(cList);
rcvMain.setAdapter(rcvMainAdp);
rcvMain.setLayoutManager(new LinearLayoutManager(this));
}
private void getCoinData(String coinLimit){
Call<cInfoPack> call = apiInterface.doGetCoinData(coinLimit);
call.enqueue(new Callback<cInfoPack>() {
#Override
public void onResponse(Call<cInfoPack> call, Response<cInfoPack> response) {
cInfoPack list = response.body();
List<cData> listSorter = new ArrayList<>();
listSorter.addAll(list.getData());
Collections.sort(listSorter, new SortbyVolChg());
cList.clear();
cList = listSorter;
System.out.println("list " + list.getData().get(0).getQuote());
System.out.println("listSorter " + listSorter.get(0).getQuote());
System.out.println("cList " + cList.get(0).getQuote());
//rcvMainAdp.notifyDataSetChanged();
}
#Override
public void onFailure(Call<cInfoPack> call, Throwable t) {
Toast.makeText(MainActivity.this, "onFailure", Toast.LENGTH_SHORT).show();
Log.d("XXXX", t.getLocalizedMessage());
call.cancel();
}
});
}
[Main Activity] If i initiate the recyclerview during onCreate and use the notifyDataSetChanged() during getCoinData, I get a blank recycleview. system.out shows that the lists all contain information in them.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiInterface = API_client.getClient().create(APIInterface.class);
etCoin = (EditText) findViewById(R.id.etCoin);
bnQuery = (Button) findViewById(R.id.bnQuery);
rcvMain = findViewById(R.id.rcvMain);
getCoinData("2");
initRCV_Main();
bnQuery.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//getCoinData("2");
//initRCV_Main();
}
});
}
private void initRCV_Main() {
rcvMainAdp = new rcvMainAdapter(cList);
rcvMain.setAdapter(rcvMainAdp);
rcvMain.setLayoutManager(new LinearLayoutManager(this));
}
private void getCoinData(String coinLimit){
Call<cInfoPack> call = apiInterface.doGetCoinData(coinLimit);
call.enqueue(new Callback<cInfoPack>() {
#Override
public void onResponse(Call<cInfoPack> call, Response<cInfoPack> response) {
cInfoPack list = response.body();
List<cData> listSorter = new ArrayList<>();
listSorter.addAll(list.getData());
Collections.sort(listSorter, new SortbyVolChg());
cList.clear();
cList = listSorter;
System.out.println("list " + list.getData().get(0).getQuote());
System.out.println("listSorter " + listSorter.get(0).getQuote());
System.out.println("cList " + cList.get(0).getQuote());
rcvMainAdp.notifyDataSetChanged();
}
#Override
public void onFailure(Call<cInfoPack> call, Throwable t) {
Toast.makeText(MainActivity.this, "onFailure", Toast.LENGTH_SHORT).show();
Log.d("XXXX", t.getLocalizedMessage());
call.cancel();
}
});
}
[Adapter]
public class rcvMainAdapter extends RecyclerView.Adapter<rcvMainAdapter.ViewHolder> {
private List<cData> idxCoin;
//ItemClickListener itemClickListener;
rcvMainAdapter(List<cData> data) {this.idxCoin = data;}
#NonNull
#NotNull
#Override
public ViewHolder onCreateViewHolder(#NonNull #NotNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.rcv_main,parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
cData cdata = idxCoin.get(position);
TextView tvSym = holder.tvSymbol;
tvSym.setText(cdata.getSymbol());
TextView tvQuo = holder.tvQuote;
BigDecimal tvQuote_BD = new BigDecimal(cdata.getQuote().getuSD().getPrice().toString());
tvQuote_BD.setScale(6, RoundingMode.DOWN);
tvQuo.setText(tvQuote_BD.toString());
TextView tvV24 = holder.tvVolume24;
BigDecimal tvVolume24_BD = new BigDecimal(cdata.getQuote().getuSD().getVolume24h().toString());
BigInteger tvVolume24_BI = tvVolume24_BD.toBigInteger();
tvV24.setText(tvVolume24_BI.toString());
}
#Override
public int getItemCount() {
return idxCoin.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView tvSymbol, tvQuote, tvVolume24;
public ViewHolder(#NonNull #NotNull View itemView) {
super(itemView);
tvSymbol = itemView.findViewById(R.id.tvSymbol);
tvQuote = itemView.findViewById(R.id.tvQuote);
tvVolume24 = itemView.findViewById(R.id.tvVolume24);
//itemView.setOnClickListener(this);
}
}
/*
public interface ItemClickListener{
void onItemClick(View view, int position);
}
*/
}
PS: apologies for the rubbish coding as this is self taught and modifying some codes found online.
Remove this in response.
cList.clear();
Add This line in response
rcvMainAdp.setdata(listSorter);
In rcvMainAdp Adapter, Create a Method setdata()
public void setdata(ArrayList<cData> data) {
this.idxCoin = data;
notifyDataSetChanged();
}
Problem most likely is that when you call initRCV_Main() You set the adapter to the list as in rcvMainAdp = new rcvMainAdapter(cList); And when list is changed and you set it to adapter it functions.
But when you call getCoinData() and rcvMainAdp.notifyDataSetChanged(); at the end you never set the changed list to the adapter until you click initRCV_Main() again.
So maybe the fix is calling rcvMainAdp = new rcvMainAdapter(cList) and then
rcvMainAdp.notifyDataSetChanged();

Android Studio ArrayList not filling into onCompleteListener

I have an ArrayList of product that has to be filled with some data like quantity, name , expiration and id.
I used two firebase queries because I need to get the "wishlist" productids from the collection wishlist for the user, and then retrieve the product data from the collection "annuncio" for every wishlisted product.
The code seems working, when I try to fill the "prod" ArrayList I see that the data are coming in from the debugger but once I check prod after the for it looks like the array list is empty.
Here's the code of the fragment:
public class FavouriteAdvertisement extends Fragment {
public static final String ARG_OBJECT = "object2";
public RecyclerView recyclerView ;
private ArrayList<Product> products;
// creates the view and calls the function favouriteProductsToRecycleview to load the cards
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_favourite, container, false);
products = new ArrayList<Product>();
favouriteProductsToRecycleview(root,products);
return root;
}
#SuppressLint("SetTextI18n")
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
}
private void favouriteProductsToRecycleview(View v, ArrayList<Product>prod) {
FirebaseFirestore db;
FirebaseAuth auth;
auth = FirebaseAuth.getInstance();
db = FirebaseFirestore.getInstance();
//first query that gets every favourite item of the user
Task<QuerySnapshot> documenti = db.collection("watchlist")
.whereEqualTo("User", auth.getUid())
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
//second query that uses the favourite items to retrieve their data
FirebaseFirestore db2 = FirebaseFirestore.getInstance();
DocumentReference doc = db2.collection("annuncio")
.document(Objects.requireNonNull(document.getString("Product")));
doc.get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
DocumentSnapshot doc2;
if (task.getResult().exists()) {
doc2 = task.getResult();
} else {
return;
}
doc2 = task.getResult();
prod.add(new Product(doc2.getString("name"),
doc2.getString("quantity"),
doc2.getString("expiration"),
doc2.getString("UId"),
doc2.getId()));
}
//until here prod fills correctly , but when it goes to the recycle view it's empty??? TODO
});
}
recyclerView = v.findViewById(R.id.result_favourite);
MyAdapter myAdapter = new MyAdapter(v.getContext(), prod);
recyclerView.setAdapter(myAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(v.getContext()));
}
}
});
}
}
here's myAdapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList< Product> products;
Context context;
public MyAdapter(Context ct , ArrayList<Product> prodotti){
products=prodotti;
context=ct;
}
// here i create my card
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater =LayoutInflater.from(context);
View view =inflater.inflate(R.layout.home_card,parent,false);
return new MyViewHolder(view);
}
// here i add information to my card
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
holder.name.setText(products.get(position).getName());
holder.quantity.setText(products.get(position).getQuantity());
holder.expire.setText(products.get(position).getExpiration());
holder.userid.setText(products.get(position).getUserId());
holder.productId =products.get(position).getProduct();
}
#Override
public int getItemCount() {
return products.size();
}
// here i declare what are the attributes of the card and how it behaves
public static class MyViewHolder extends RecyclerView.ViewHolder{
TextView name,quantity,expire,userid;
Button goToUser;
FloatingActionButton addToFavorite;
String productId;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
expire = itemView.findViewById(R.id.productexpire);
name = itemView.findViewById(R.id.productname);
quantity = itemView.findViewById(R.id.productquantity);
userid = itemView.findViewById(R.id.productauthor);
addToFavorite = itemView.findViewById(R.id.add_to_fav_btn);
addToFavorite.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FirebaseFirestore db = FirebaseFirestore.getInstance();
Date date = new Date();
SimpleDateFormat ft =
new SimpleDateFormat ("E yyyy.MM.dd 'at' hh:mm:ss a zzz");
Map<String, Object> prod = new HashMap<>();
prod.put("User",userid.getText());
prod.put("Product",productId);
prod.put("date",ft.format(date));
db.collection("watchlist")
.document()
.set(prod, SetOptions.merge())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(view.getContext(),"added to favourites",Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(view.getContext(),"cannot add to favourites",Toast.LENGTH_SHORT).show();
}
});
}
});
goToUser =itemView.findViewById(R.id.btngotouser);
goToUser.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(view.getContext(),"bla",Toast.LENGTH_SHORT).show();
UserFragment userFragment = new UserFragment();
FragmentManager fragmentManager = new FragmentManager(){} ;
fragmentManager.beginTransaction().replace(R.id.list_home,userFragment);
}
});
}
}
}
and my Product object
public class Product {
private String Name;
private String Quantity;
private String Expiration;
private String UserId;
private String product;
public Product(String name, String quantity, String expiration,String Uid, String productId) {
Name = name;
Quantity = quantity;
Expiration = expiration;
UserId = Uid;
product = productId;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public String getUserId() {
return UserId;
}
public void setUserId(String userId) {
UserId = userId;
}
public String getQuantity() {
return Quantity;
}
public String getExpiration() {
return Expiration;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public void setQuantity(String quantity) {
Quantity = quantity;
}
public void setExpiration(String expiration) {
Expiration = expiration;
}
}
I would remove "prod" in the Fragment as you already have access to the products variable
Also these 2 lines should be in onViewCreated
products = new ArrayList<Product>();
favouriteProductsToRecycleview(root,products);
If it's still the same afterwards really check what is added before adding it, because ArrayList probably isn't the source of the error
Your onclicklistener needs to be written in the onbindviewholder.That is where your recycler view is loaded.The view holder is just a class to hold the view of the recycler view.
addToFavorite.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FirebaseFirestore db = FirebaseFirestore.getInstance();
Date date = new Date();
SimpleDateFormat ft =
new SimpleDateFormat ("E yyyy.MM.dd 'at' hh:mm:ss a zzz");
Map<String, Object> prod = new HashMap<>();
prod.put("User",userid.getText());
prod.put("Product",productId);
prod.put("date",ft.format(date));
db.collection("watchlist")
.document()
.set(prod, SetOptions.merge())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(view.getContext(),"added to favourites",Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(view.getContext(),"cannot add to favourites",Toast.LENGTH_SHORT).show();
}
});
}
});

Why aren't my images being displayed after logging in with different accounts?

I am trying to retrieve images and text from Firebase to my Recycle view and this works well. The only problem I'm experiencing is the images are not being loaded if I login with different phones but the text data are being loaded. Any help would be appreciated.
Gets data from Firebase
//Retrieves information stored inside Post node...
public void fetchUserInfo() {
postRef = FirebaseDatabase.getInstance().getReference().child("Post");
postRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
value = ds.getValue(Post.class);
postList.add(value);
}
adapter = new Adapter(Shop_Activity.this, postList);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i("Error", databaseError.toString());
}
});
}
}
In this method I saved the necessary information inside a dictionary and uploaded it to Firebase
//saves user image and description inside firebase
public void saveToFirebase(){
String userId = mAuth.getCurrentUser().getUid();
postDictionary.put("desc", descriptionEditText.getText().toString());
postDictionary.put("image", selectedImageUri.toString());
postDictionary.put( "id",userId);
productsDatabaseRef.child("Post").push().setValue(postDictionary);
Intent intent = new Intent(Upload_Post.this, Shop_Activity.class);
startActivity(intent);
}
Adapter
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
Context context;
ArrayList<Post> userPost;
public Adapter(Context context, ArrayList<Post> userPost){
this.context = context;
this.userPost = userPost;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.shop_layout_design,viewGroup, false));
}
//this is where you set the value for the ui elements
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
viewHolder.desc.setText(userPost.get(i).getdesc());
Glide.with(this.context).load(userPost.get(i).getimage()).into(viewHolder.image);
//Picasso.get().load(userPost.get(i).getimage()).into(viewHolder.image);
}
#Override
public int getItemCount() {
return userPost.size();
}
//links up ui elements
class ViewHolder extends RecyclerView.ViewHolder{
TextView desc;
TextView id;
ImageView image;
public ViewHolder(#NonNull View itemView) {
super(itemView);
id = itemView.findViewById(R.id.post_title);
desc = itemView.findViewById(R.id.post_desc);
image = itemView.findViewById(R.id.post_image);
}
}
}

Changing the UI in the onComplete method of the OnCompleteListener for a Task

I'm using a Cloud Firestore database to populate a RecyclerView in an Android app. I'm getting the data by using a Task in the onAttach method of a Fragment. I need to be able to update the UI, the RecyclerView with data from the Cloud Firestore.
I populated the RecyclerView with dummy data in the onAttach method of the Fragment and that worked, but when I put the same loop that inserts dummy data in the onComplete method of a OnCompleteListener that's used in the Task that pulls data from the Cloud Firestore, the RecyclerView doesn't update and the list stays blank. I need to do it there to eventually insert data from the Cloud Firestore.
Within the Fragment. The data coming back from the Firestore database is correct and I see all of the Log statements in the onComplete method in the Logcat.
ChatListFragment:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_chat_list, container, false);
// Set the adapter
if (view instanceof RecyclerView) {
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
if (mColumnCount <= 1) {
recyclerView.setLayoutManager(new LinearLayoutManager(context));
} else {
recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
}
chatRecyclerViewAdapter = new ChatRecyclerViewAdapter(ChatList.ITEMS, mListener);
recyclerView.setAdapter(chatRecyclerViewAdapter);
}
return view;
}
...
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnListFragmentInteractionListener) {
mListener = (OnListFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnListFragmentInteractionListener");
}
Log.d(LOG_TAG, "activity attached, creating Firestore instance");
FirebaseFirestore db = FirebaseFirestore.getInstance();
//Worked, but doesn't in OnCompleteListener
/*for (int i = 1; i <= 10; i++) {
ChatList.addItem(ChatList.createDummyItem(i));
}*/
Task<QuerySnapshot> task = db.collection("chats").get();
task.addOnCompleteListener(getActivity(), new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(LOG_TAG, "ID = " + document.getId() + " => " + document.getData());
ChatListMessage chatListMessage = document.toObject(ChatListMessage.class);
for (int i = 1; i <= 10; i++) {
Log.d(LOG_TAG, "adding message");
ChatList.addItem(ChatList.createDummyItem(i));
}
Log.d(LOG_TAG, "ChatListMessage members " + chatListMessage.getLastMessage());
}
} else {
Log.w(LOG_TAG, "Error getting documents.", task.getException());
}
}
});
}
Within the ChatList class
public static void addItem(ChatListItem item) {
ITEMS.add(item);
ITEM_MAP.put(item.userId, item);
}
public static ChatListItem createDummyItem(int position) {
return new ChatListItem(String.valueOf(position), R.drawable.profile_circle, makeDetails(position),
new Timestamp(System.currentTimeMillis()));
}
public static class ChatListItem {
public final String userId;
public final int pictureUrl;
public final String lastMessage;
public final Timestamp timeStamp;
public ChatListItem(String userId, int pictureUrl, String details, Timestamp timeStamp) {
this.userId = userId;
this.pictureUrl = pictureUrl;
this.lastMessage = details;
this.timeStamp = timeStamp;
}
#Override
public String toString() {
return userId;
}
public Timestamp getTimeStamp() {
return timeStamp;
}
public String getTLastMessage() {
return lastMessage;
}
}
The custom RecyclerViewAdapter
public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerViewAdapter.ViewHolder> {
private final List<ChatListItem> mValues;
private final OnListFragmentInteractionListener mListener;
public ChatRecyclerViewAdapter(List<ChatListItem> items, OnListFragmentInteractionListener listener) {
mValues = items;
mListener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_chat, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.contactImageView.setImageResource(mValues.get(position).pictureUrl);
holder.contactImageView.setScaleType(ImageView.ScaleType.FIT_XY);
holder.mContentView.setText(mValues.get(position).lastMessage);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onListFragmentInteraction(holder.mItem);
}
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
public final ImageView contactImageView;
public final TextView messageMembersTextView;
public final TextView mContentView;
public final TextView timestampView;
public ChatListItem mItem;
public ViewHolder(View view) {
super(view);
mView = view;
messageMembersTextView = view.findViewById(R.id.message_members);
contactImageView = view.findViewById(R.id.contact_imageView);
mContentView = view.findViewById(R.id.content_textView);
timestampView = view.findViewById(R.id.timestamp_textView);
}
#Override
public String toString() {
return super.toString() + " '" + mContentView.getText() + "'";
}
}
}
How can I get the UI to be updated with the onComplete method of the OnCompleteListener?
For this, chatRecyclerViewAdapter.notifyDataSetChanged() needs to be called in the onComplete method of the OnCompleteListener. I forgot to do this outside of the listener since it looks like the list items are pulled in after the onAttach method is called.

SnapshotParser using FirestorePagingAdapter

I tried to have in my model the id of the document but when I access it the value of the id is null.
In fragment/activity:
public class MyEventsFragment extends Fragment {
private OnFragmentInteractionListener mListener;
private FirestorePagingAdapter mAdapter;
private FirebaseFirestore mFirebaseFirestore;
public MyEventsFragment() {
// Required empty public constructor
}
public static MyEventsFragment newInstance() {
return new MyEventsFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFirebaseFirestore = FirebaseFirestore.getInstance();
Query query = mFirebaseFirestore.collection("events");
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(10)
.setPageSize(20)
.build();
FirestorePagingOptions<Event> options = new FirestorePagingOptions.Builder<Event>()
.setLifecycleOwner(this)
.setQuery(query, config, new SnapshotParser<Event>() {
#NonNull
#Override
public Event parseSnapshot(#NonNull DocumentSnapshot snapshot) {
Event evt = new Event(
snapshot.getId(),
snapshot.getString("titre"),
snapshot.getString("sport"),
snapshot.getString("lieu"),
snapshot.get("date", Timestamp.class),
snapshot.get("dateLimit", Timestamp.class));
Log.i("TEST", evt.toString());
return evt;
}
})
.build();
mAdapter = new EventFirestorePagingAdapter(options, getContext());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list_events, container, false);
RecyclerView mRecyclerView = view.findViewById(R.id.recyclerViewListEvent);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getContext());
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
return view;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
#Override
public void onStart() {
super.onStart();
mAdapter.startListening();
}
#Override
public void onStop() {
super.onStop();
mAdapter.stopListening();
}
public interface OnFragmentInteractionListener {
void onFragmentInteraction(Uri uri);
}
}
EventFirestorePagingAdapter (extends FirestorePagingAdapter):
public class EventFirestorePagingAdapter extends FirestorePagingAdapter<Event, EventFirestorePagingAdapter.EventViewHolder> {
private Context mContext;
public EventFirestorePagingAdapter(#NonNull FirestorePagingOptions<Event> options, Context context) {
super(options);
this.mContext = context;
}
public class EventViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView titre, sport, lieu, date;
public EventViewHolder(#NonNull View itemView) {
super(itemView);
titre = itemView.findViewById(R.id.titreRecyclerView);
sport = itemView.findViewById(R.id.sportRecyclerView);
lieu = itemView.findViewById(R.id.lieuRecyclerView);
date = itemView.findViewById(R.id.dateRecyclerView);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
/*for(Map.Entry<String, Object> map: getItem(getAdapterPosition()).getData().entrySet()){
Log.i("TEST", map.getKey());
}*/
Event event = getItem(getAdapterPosition()).toObject(Event.class);
Log.i("TEST", "id = " + event.getId());
Toast.makeText(mContext, "OnClick on position : " + getAdapterPosition() + " \nTest : " + event.getTitre() + "\nId : " + event.getId(), Toast.LENGTH_LONG).show();
}
}
#Override
protected void onBindViewHolder(#NonNull EventViewHolder holder, int position, #NonNull Event model) {
holder.titre.setText(model.getTitre());
holder.sport.setText(model.getSport());
holder.lieu.setText(model.getLieu());
holder.date.setText(model.getDate().toDate().toString());
}
#NonNull
#Override
public EventViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(mContext).inflate(R.layout.recyclerview_card_row, viewGroup, false);
return new EventViewHolder(view);
}
}
Event class :
public class Event {
private String id;
private String titre;
private String sport;
private String lieu;
private Timestamp date;
private Timestamp dateLimit;
public Event(){ } // Needed for Firebase
public Event(String id, String titre, String sport, String lieu, Timestamp date, Timestamp dateLimit) {
this.id = id;
this.titre = titre;
this.sport = sport;
this.lieu = lieu;
this.date = date;
this.dateLimit = dateLimit;
}
// ... getters & setters
}
Debug :
1) TEST: Event{id='c5jUGQc4c7SHzbpT6N01', titre='Final PSG/MARSEILLE', sport='Football', lieu='Bercy', date=Timestamp(seconds=1550844000, nanoseconds=0), dateLimit=Timestamp(seconds=1551009600, nanoseconds=0)}
2) TEST: id = null
Firestore database:
Firestore Database
Gist with the same issue
Answer :
SnapshotParser did not work as i expected but i find out how to access to the id of the document in the onClick(). I replaced this line by this following code :
DocumentSnapshot documentSnapshot = getCurrentList().get(getAdapterPosition());
Chat chat = documentSnapshot.toObject(Chat.class);
chat.setId(documentSnapshot.getId());
According to your comment:
yes, but i need to retrieve it later when i click on an item from my recyclerview
Because you are using a FirestorePagingAdapter and you aren't able to use something like this:
String id = getSnapshots().getSnapshot(position).getId(); //Works only with FirestoreRecyclerAdapter
You need to store the id of the document as property of your document. As I see in your Event class, the property id exists but in the database there is no id property that holds the document id. To solve this, you need to pass the id of the document to the Event constructor when you are creating a new object like this:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference eventsRef = rootRef.collection("events");
String id = eventsRef.document().getId();
Event event = new Event(id, titre, sport, lieu, date, dateLimit);
eventsRef.document(id).set(event);

Categories