RecyclerView not showing lists yet data is being fed to adapter

Viewed 60

I'm getting data from FireStore into a custom class object, when I use a Toast during the for loop, I can display all the data I want but the list I'm passing to my adapter is empty.

I've tried to pinpoint why exactly it is empty but nothing has worked.

Here's my main class:

products.whereEqualTo("IDliv","kITQ8wiPshsnWqHDlP5D").addSnapshotListener(new EventListener<QuerySnapshot>() {
        @Override
        public void onEvent(@Nullable QuerySnapshot documentSnapshots, @Nullable FirebaseFirestoreException e) {
            assert documentSnapshots != null;
            for (DocumentChange document : documentSnapshots.getDocumentChanges()) {
            generalObject obj=document.getDocument().toObject(generalObject.class);
            for(int i=0;i<obj.getproducts().size();i++){
                String[] body;
                  body=obj.getproducts().get(i).toString().split("\\+");
                ProductModel p = new ProductModel(body[0].toString(), body[1].toString(), body[2].toString());
                prod.add(p);

            }
            }

    }
    });
if(prod.size()>0) {
    adapter = new ProductAdapter(prod);
    RecyclerView recview = findViewById(R.id.items_recycler_view);
    recview.setHasFixedSize(true);
    recview.setLayoutManager(new LinearLayoutManager(this));
    recview.setAdapter(adapter);
}else{
    Toast.makeText(getApplicationContext(),"this list is empty",Toast.LENGTH_LONG).show();
}
    }

and my adapter class is like this :

public class ProductAdapter  extends RecyclerView.Adapter<ProductAdapter.ViewHolder> {

    private ArrayList<ProductModel> products;

    public ProductAdapter(ArrayList<ProductModel> product) {
        this.products = product;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = (View) LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_product, parent, false);

        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

        ProductModel prod = products.get(position);

        holder.name.setText(prod.getGetProductName());
        holder.description.setText(prod.getProductMarque());
        holder.qty.setText(prod.getProductQte());


    }

    @Override
    public int getItemCount() {
        if (products != null) {
            return products.size();
        } else {
            return 0;
        }
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public final View view;
        public final TextView name;
        public final TextView description;
        public final TextView qty;

        public ViewHolder(View view) {
            super(view);
            this.view = view;
            name = view.findViewById(R.id.productsName);
            description = view.findViewById(R.id.marque);
            qty=view.findViewById(R.id.quantity);


        }
    }
}

Any idea what's wrong?

2 Answers

You need to add notifyDataSetChanged() when the event callback is over.

    @Override
    public void onEvent(@Nullable QuerySnapshot documentSnapshots, @Nullable FirebaseFirestoreException e) {
        assert documentSnapshots != null;
        for (DocumentChange document : documentSnapshots.getDocumentChanges()) {
        generalObject obj=document.getDocument().toObject(generalObject.class);
        for(int i=0;i<obj.getproducts().size();i++){
            String[] body;
              body=obj.getproducts().get(i).toString().split("\\+");
            ProductModel p = new ProductModel(body[0].toString(), body[1].toString(), body[2].toString());
            prod.add(p);

        }
        adapter.notifyDataSetChanged(); // <<<<<< here is the change
   }

The other thing: the if(prod.size()>0) condition is too early as it won't match because the data is received on a background thread, so this condition will never met. Change this part to:

adapter = new ProductAdapter(prod);
RecyclerView recview = findViewById(R.id.items_recycler_view);
recview.setHasFixedSize(true);
recview.setLayoutManager(new LinearLayoutManager(this));
recview.setAdapter(adapter);

if (prod.size()==0) {
    Toast.makeText(getApplicationContext(),"this list is empty",Toast.LENGTH_LONG).show();
}

Your query from Firebase is happening asynchronously, so the query data is coming back and being added to the product list after you've instantiated the adapter.

Your if statement is not being met, because the product list is actually empty, because that code is getting executed before the query returns.

// This is false here because this block is getting executed before 
// the Firebase query returns the data. However your not seeing the 
// toast, because you are passing the wrong context. Toast takes 
// an activity context, not application context.

if(prod.size()>0) {
    adapter = new ProductAdapter(prod);
    RecyclerView recview = findViewById(R.id.items_recycler_view);
    recview.setHasFixedSize(true);
    recview.setLayoutManager(new LinearLayoutManager(this));
    recview.setAdapter(adapter);
}else{
    Toast.makeText(getApplicationContext(),"this list is empty",Toast.LENGTH_LONG).show();
}

Try changing your code a little to create the adapter, then add the data once it returns from the query:

adapter = new ProductAdapter();
    RecyclerView recview = findViewById(R.id.items_recycler_view);
    recview.setHasFixedSize(true);
    recview.setLayoutManager(new LinearLayoutManager(this));
    recview.setAdapter(adapter);


products.whereEqualTo("IDliv","kITQ8wiPshsnWqHDlP5D").addSnapshotListener(new EventListener<QuerySnapshot>() {
        @Override
        public void onEvent(@Nullable QuerySnapshot documentSnapshots, @Nullable FirebaseFirestoreException e) {
            assert documentSnapshots != null;
            for (DocumentChange document : documentSnapshots.getDocumentChanges()) {
            generalObject obj=document.getDocument().toObject(generalObject.class);

            for(int i=0;i<obj.getproducts().size();i++){
                String[] body;
                  body=obj.getproducts().get(i).toString().split("\\+");
                ProductModel p = new ProductModel(body[0].toString(), body[1].toString(), body[2].toString());
                prodList.add(p);

            }

            // Tell the adapter we have new data that we want to add
            adapter.addNewData(prodList)

            }

    }
    });

Then change your adapter slightly:

public class ProductAdapter  extends RecyclerView.Adapter<ProductAdapter.ViewHolder> {

    private ArrayList<ProductModel> products;

    public ProductAdapter() {
        this.products = new ArrayList<ProductModel>();
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = (View) LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_product, parent, false);

        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

        ProductModel prod = products.get(position);

        holder.name.setText(prod.getGetProductName());
        holder.description.setText(prod.getProductMarque());
        holder.qty.setText(prod.getProductQte());


    }

    @Override
    public int getItemCount() {
        if (products != null) {
            return products.size();
        } else {
            return 0;
        }
    }

    public void addNewData(data: List<ProductModel>) {
        this.products.clear();
        this.products.addAll(data);
        notifyDatasetChanged(); //This will tell the adapter we have new data and to re-inflate the views
    }
}
Related