1

I am trying to retrieve data (a product image and the price for it) from Firebase Realtime Database and put it in a recyclerView, but I have some difficulties. In my database I stored the image url for one product and I want to show that image in an ImageView component placed in the cardView. I also managed to retrieve the url and price correctly, but my recyclerView doesn't show them. This is my product class:

public class CustomizeProduct {
    private String productImage;
    private String productName;
    private String productPrice;

    public CustomizeProduct(String image, String name, String price){
        this.productImage = image;
        this.productName = name;
        this.productPrice = price;
    }

    public String getProductImage() {
        return productImage;
    }
    public void setProductImage(String productImage){
        this.productImage = productImage;
    }

    public String getProductName() {
        return productName;
    }
    public void setProductName(String productName){
        this.productName = productName;
    }

    public String getProductPrice() { return productPrice; }
    public void setProductPrice(String productPrice) {
        this.productPrice = productPrice;
    }
}

My Adapter as following:

public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ViewHolder> {
    private ArrayList<CustomizeProduct> customized;
    private Context myContext;

    public static class ViewHolder extends RecyclerView.ViewHolder {

        public ImageView thisProductImage;
        public TextView thisProductName;
        public TextView thisProductPrice;

        public ViewHolder(View itemView) {
            super(itemView);
            thisProductImage = itemView.findViewById(R.id.customProductImage);
            thisProductName = itemView.findViewById(R.id.customProductName);
            thisProductPrice = itemView.findViewById(R.id.customProductPrice);
        }
    }

    public ProductAdapter(Context context, ArrayList<CustomizeProduct> thisCustom) {
        myContext = context;
        customized = thisCustom;
    }

    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.ingredients, parent, false);
        ViewHolder mv = new ViewHolder(v);
        return mv;
    }

    public void onBindViewHolder(ViewHolder holder, int position) {
        CustomizeProduct currentCustom = customized.get(position);
        Picasso.get().load(currentCustom.getProductImage()).placeholder(R.drawable.shrimp_ditali).into(holder.thisProductImage);
        holder.thisProductName.setText(currentCustom.getProductName());
        holder.thisProductPrice.setText(currentCustom.getProductPrice());
    }

    public int getItemCount() {
        return customized.size();
    }
}

And the activity I have placed my recyclerView:

public class BuildProduct extends AppCompatActivity {
    public final static ArrayList<CustomizeProduct> customized = new ArrayList<>();
    private RecyclerView recyclerView;
    private ProductAdapter myAdapter;
    private RecyclerView.LayoutManager layoutManager;
    private static String productImage, productPrice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_build_product);

        buildCustom();
    }

    public void buildCustom(){
        recyclerView = findViewById(R.id.customList);
        recyclerView.setHasFixedSize(true);
        layoutManager = new LinearLayoutManager(this);

        final DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("MENIU");
        ref.addListenerForSingleValueEvent(new ValueEventListener(){
            public void onDataChange(DataSnapshot ds) {
                for (DataSnapshot menu : ds.getChildren()) {
                    String keys = menu.getKey();
                    String productName = (String) ds.child(keys).child("NUME_PRODUS").getValue();
                    if(productName.equals(checkout.thisProductName)){
                        productImage = ds.child(keys).child("IMAGINE").getValue(String.class);
                        Long price = (Long) ds.child(keys).child("PRET_PRODUS").getValue();
                        productPrice = String.valueOf(price);
                        Toast.makeText(getApplicationContext(), productImage + " " + productPrice, Toast.LENGTH_LONG).show();
                    }
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
        for(int i = 0; i < checkout.thisProductQty; i++) {
            customized.add(new CustomizeProduct(productImage,checkout.thisProductName, productPrice));
            myAdapter = new ProductAdapter(getApplicationContext(), customized);
        }
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(myAdapter);
    }
}

The product name is correctly because is a variable, but the image and price don't change at all. The "for" loop serves in creating as much cardViews as my products of that type are in checkout. How can I make it show image and price from database?

This is my Firebase child structure:

Firebase Database

EDIT Updated code:

public class CustomizeProduct {
    private String imagine;
    private String nume_produs;
    private String pret_produs;

    public CustomizeProduct(){}

    public CustomizeProduct(String imagine, String nume_produs, String pret_produs){
        this.imagine = imagine;
        this.nume_produs = nume_produs;
        this.pret_produs = pret_produs;
    }
    @PropertyName("IMAGINE")
    public String getIMAGINE() {
        return imagine;
    }
    @PropertyName("NUME_PRODUS")
    public String getNUME_PRODUS() {
        return nume_produs;
    }
    @PropertyName("PRET_PRODUS")
    public String getPRET_PRODUS() { return pret_produs; }

}

And in onBindViewHolder:

public void onBindViewHolder(ViewHolder holder, int position) {
        CustomizeProduct currentCustom = customized.get(position);
        Picasso.get().load(currentCustom.getIMAGINE()).placeholder(R.drawable.shrimp_ditali).into(holder.thisProductImage);
        holder.thisProductName.setText(currentCustom.getNUME_PRODUS());
        holder.thisProductPrice.setText(currentCustom.getPRET_PRODUS());
    }
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Măria
  • 21
  • 4
  • You have different names of the fields in the database and in your class. Both should match. – Alex Mamo Jun 03 '20 at 13:45
  • How are they different? I don't understand. If that would be the problem, the productImage and productPrice wouldn't store the exact values from IMAGINE and PRET_PRODUS Firebase nodes. – Măria Jun 03 '20 at 13:55
  • The properties in your database should be named exactly like in your class or you can use [annotations](https://stackoverflow.com/questions/53924189/how-to-read-firestore-sub-collection-and-pass-it-to-firestorerecycleroptions/53943559#53943559). Something like `@PropertyName("IMAGINE")`. – Alex Mamo Jun 03 '20 at 13:58
  • I did that. It doesn't work. – Măria Jun 03 '20 at 14:16
  • "It doesn't work" doesn't help. What exactly doesn't work? Do you have an error? Please edit your question with updated code and database schema. – Alex Mamo Jun 03 '20 at 14:28
  • I don't have any error and I have updated my code. It shows in price TextView NULL value. – Măria Jun 03 '20 at 14:35
  • Change `public String getIMAGINE()` to `public String getImagine()`. Besides that, try to use English names rather than Romanian names ;) – Alex Mamo Jun 03 '20 at 14:37

2 Answers2

0

I think you may also have to annotate the private fields, since the SDK uses those to set the values from the database:

public class CustomizeProduct {
    @PropertyName("IMAGINE")
    private String imagine;
    @PropertyName("NUME_PRODUS")
    private String nume_produs;
    @PropertyName("PRET_PRODUS")
    private String pret_produs;

    public CustomizeProduct(){}

    public CustomizeProduct(String imagine, String nume_produs, String pret_produs){
        this.imagine = imagine;
        this.nume_produs = nume_produs;
        this.pret_produs = pret_produs;
    }
    @PropertyName("IMAGINE")
    public String getIMAGINE() {
        return imagine;
    }
    @PropertyName("NUME_PRODUS")
    public String getNUME_PRODUS() {
        return nume_produs;
    }
    @PropertyName("PRET_PRODUS")
    public String getPRET_PRODUS() { return pret_produs; }
}

Note that using getters and setters is completely optional, and you also use this (much shorter) class to bind to the same JSON structure:

public class CustomizeProduct {
    @PropertyName("IMAGINE")
    public String imagine;

    @PropertyName("NUME_PRODUS")
    public String nume_produs;

    @PropertyName("PRET_PRODUS")
    public String pret_produs;
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
0

customized.add(new CustomizeProduct(productImage,checkout.thisProductName, productPrice));

You can see only product name (checkout.thisProductName), but not product image & price because you populate your adapter with product names that is not returned by Firebase in your onDataChange(DataSnapshot ds) callback; there is no clue in your code how do you build product names (checkout.thisProductName) value.

Now product images & prices not shown as you build the adapter in the main thread, while firebase works implicitly in background thread; so you need to sync your adapter to that background thread by transferring the part of code that builds your recyclerView adapter into the firebase callback as follow:

public void buildCustom(){
    recyclerView = findViewById(R.id.customList);
    recyclerView.setHasFixedSize(true);
    layoutManager = new LinearLayoutManager(this);

    final DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("MENIU");
    ref.addListenerForSingleValueEvent(new ValueEventListener(){
        public void onDataChange(DataSnapshot ds) {
            for (DataSnapshot menu : ds.getChildren()) {
                String keys = menu.getKey();
                String productName = (String) ds.child(keys).child("NUME_PRODUS").getValue();
                if(productName.equals(checkout.thisProductName)){
                    productImage = ds.child(keys).child("IMAGINE").getValue(String.class);
                    Long price = (Long) ds.child(keys).child("PRET_PRODUS").getValue();
                    productPrice = String.valueOf(price);
                    Toast.makeText(getApplicationContext(), productImage + " " + productPrice, Toast.LENGTH_LONG).show();
                }
            }

            // here you've received all Firebase data and can now build the recyclerView
            for(int i = 0; i < checkout.thisProductQty; i++) {
                customized.add(new CustomizeProduct(productImage,checkout.thisProductName, productPrice));
                myAdapter = new ProductAdapter(getApplicationContext(), customized);
            }
            recyclerView.setLayoutManager(layoutManager);
            recyclerView.setAdapter(myAdapter);

        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });

}
Zain
  • 37,492
  • 7
  • 60
  • 84