0

I have a RecyclerView showing details about some products. Each recyclerview item contains the details like, name of the product, quantity in stock and selling price. There is also a button for selling the product.

When I click on the button, an alertDialog open asking for the quantity to be sold, after selling a certain quantity, the stock in the database changes as expected but the recyclerview item is not updated after the alertDialog is closed. If I go to some other activity and come back, the recyclerview item will update, but I want it to update as soon as the alertDialog is closed.

I checked other similar questions but I couldn't work it out. The most common answer is to just put

adapter.notifyDatasetChanged()

in the activity where the recyclerview object is created...but where ? because the code for the alert dialog which takes in the quantity to sell is in the ViewHolder class. How can I call the above from the activity class ?

Below is my code for the activity which has the recyclerview.

public class Inventory extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    RecyclerView recyclerInventory;
    RecyclerView.LayoutManager layoutManager;
    List<Product> inventory = new ArrayList<>();
    InventoryAdapter adapter;
    TextView emptyView;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inventory);
        Toolbar toolbar = findViewById(R.id.toolbar);
        toolbar.setTitle(R.string.title_activity_inventory);
        setSupportActionBar(toolbar);
        FloatingActionButton fab = findViewById(R.id.add_item);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(Inventory.this, AddItem.class));
            }
        });
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.inventory_navigation_drawer_open, R.string.inventory_navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();
        navigationView.setNavigationItemSelectedListener(this);

        recyclerInventory = (RecyclerView) findViewById(R.id.recycler_home);
        emptyView = (TextView) findViewById(R.id.empty_image);

        layoutManager = new GridLayoutManager(this, 1, RecyclerView.VERTICAL, false);
        recyclerInventory.setLayoutManager(layoutManager);

        loadProductList();

        //InventoryAdapter adapter = new InventoryAdapter(new MSDatabase(this).getProducts(), this);
    }

    private void loadProductList() {
        inventory = new MSDatabase(this).getProducts();

            adapter = new InventoryAdapter(inventory, this,new ItemClickListener() {
                @Override
                public void onItemClick(int position) {

                }
            });
            adapter.notifyDataSetChanged();
            recyclerInventory.setAdapter(adapter);

            if(adapter.getItemCount() > 0) {
                recyclerInventory.setVisibility(View.VISIBLE);
                emptyView.setVisibility(View.GONE);
            }
            else {
                recyclerInventory.setVisibility(View.GONE);
                emptyView.setVisibility(View.VISIBLE);
            }
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.inventory, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Inventory/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

And my InventoryAdapter and ViewHolder class

class InventoryViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,
View.OnCreateContextMenuListener {
    TextView productName, inStock, sellingPrice;
    FButton sellButton, purchaseButton, deleteButton;
    private WeakReference<ItemClickListener> listenerRef;
    EditText sellQuantity;
    List<Product> inventory = new ArrayList<>();
    InventoryAdapter adapter;


    public InventoryViewHolder(View prodView) {
        super(prodView);

        productName = (TextView) prodView.findViewById(R.id.product_name);
        inStock = (TextView) prodView.findViewById(R.id.product_in_stock);
        sellingPrice = (TextView) prodView.findViewById(R.id.product_sellingprice);
        sellButton = (FButton) prodView.findViewById(R.id.btn_sell);
        purchaseButton = (FButton) prodView.findViewById(R.id.btn_purchase);
        deleteButton = (FButton) prodView.findViewById(R.id.btn_delete);

        sellButton.setOnClickListener(this);
        purchaseButton.setOnClickListener(this);
        deleteButton.setOnClickListener(this);

        prodView.setOnCreateContextMenuListener(this);
    }

    @Override
    public void onClick(View v) {

        if(v.getId() == sellButton.getId())
        {
            AlertDialog.Builder builder1 = new AlertDialog.Builder(v.getContext());
            builder1.setMessage("Select quantity");
            builder1.setCancelable(true);
            Context context = v.getContext();
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View sell_dialog_layout = inflater.inflate(R.layout.sell_product_layout,null);
            builder1.setView(sell_dialog_layout);
            builder1.setIcon(R.drawable.ic_warning_black_24dp);
            sellQuantity = (EditText) sell_dialog_layout.findViewById(R.id.elegent_number_button);
                    builder1.setPositiveButton(
                            "OK",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id)
                                {
                                    //Toast.makeText(v.getContext(),"Qty:" + sellQuantity.getText().toString() + " sell position:" + String.valueOf(getAdapterPosition()+1) , Toast.LENGTH_SHORT).show();
                                    new MSDatabase(v.getContext()).sellProduct(getAdapterPosition(), Double.parseDouble(sellQuantity.getText().toString())), v.getContext());

                                }
                            });

                    builder1.setNegativeButton(
                            "Cancel",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    dialog.cancel();

                                }
                            });

                    AlertDialog alert11 = builder1.create();
                    alert11.show();
        }
        else if(v.getId() == purchaseButton.getId())
        {
            //Toast.makeText(v.getContext(), "purchase position:" + String.valueOf(getAdapterPosition()+1), Toast.LENGTH_SHORT).show();
// Yet to be done
            AlertDialog.Builder builder1 = new AlertDialog.Builder(v.getContext());
            builder1.setMessage("Select quantity");
            builder1.setCancelable(true);
            Context context = v.getContext();
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View sell_dialog_layout = inflater.inflate(R.layout.sell_product_layout,null);
            builder1.setView(sell_dialog_layout);
            builder1.setIcon(R.drawable.ic_warning_black_24dp);
            sellQuantity = (EditText) sell_dialog_layout.findViewById(R.id.elegent_number_button);
            builder1.setPositiveButton(
                    "OK",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id)
                        {
                            Toast.makeText(v.getContext(),"Qty:" + sellQuantity.getText().toString() + " sell position:" + String.valueOf(getAdapterPosition()+1) , Toast.LENGTH_SHORT).show();
                        }
                    });

            builder1.setNegativeButton(
                    "Cancel",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });

            AlertDialog alert11 = builder1.create();
            alert11.show();


        }
        else if(v.getId() == deleteButton.getId())
        {
            Toast.makeText(v.getContext(), "delete position:" + String.valueOf(getAdapterPosition()+1), Toast.LENGTH_SHORT).show();
// Yet to be done
        }

        try {
            ItemClickListener checknull = listenerRef.get();
        }

       catch (Exception e){
            Log.v("InventoryViewHolder: ","WeakReference.get() is null");
        }
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        menu.setHeaderTitle(R.string.select_an_action);
        menu.add(0, 0, getAdapterPosition(), Common.UPDATE);
        menu.add(0,0,getAdapterPosition(),Common.DELETE);
    }
}

    public class InventoryAdapter extends RecyclerView.Adapter<InventoryViewHolder> {

        private List<Product> listData = new ArrayList<>();
        private Context context;
        private ItemClickListener listener;

        public InventoryAdapter(List<Product> listData, Context context, ItemClickListener listener) {
            this.listData = listData;
            this.context = context;
            this.listener = listener;
        }

        @NonNull
        @Override
        public InventoryViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {

            View prodView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.inventory_product_layout, viewGroup, false);
            return new InventoryViewHolder(prodView);
        }

        @Override
        public void onBindViewHolder(@NonNull final InventoryViewHolder inventoryViewHolder, int i) {

            inventoryViewHolder.productName.setText(listData.get(i).getProductName());
            inventoryViewHolder.inStock.setText(String.valueOf(listData.get(i).getProductInStock()));
            inventoryViewHolder.sellingPrice.setText(String.valueOf(listData.get(i).getProductSellingPrice()));
        }

        @Override
        public int getItemCount() {
            return listData.size();
        }

        public void setData(List<Product> data){
            this.listData = data;
            notifyDataSetChanged();
            // where this.data is the recyclerView's dataset you are
            // setting in adapter=new Adapter(this,db.getData());
        }
    }

The method to sell the product in the database class is below.

    public void sellProduct(int rowId, double quantityChange, Context context) {
        SQLiteDatabase db = getReadableDatabase();

        Product selectedProduct = getSelectedProduct(String.valueOf(rowId));
        Log.v("Check name: ", selectedProduct.getProductName());
        //Log.v("Check inStock: ", String.valueOf(selectedProduct.getProductInStock()));
        ContentValues cv = new ContentValues();
        cv.put("ProductName", selectedProduct.getProductName());
        cv.put("InStock", selectedProduct.getProductInStock() - quantityChange);
        cv.put("CostPrice", selectedProduct.getProductCostPrice());
        cv.put("SellingPrice", selectedProduct.getProductSellingPrice());
        cv.put("Description", selectedProduct.getProductDescription());
        cv.put("PurchaseDate", selectedProduct.getDateOfPurchase());
        cv.put("ExpiryDate", selectedProduct.getDateOfExpiry());
        cv.put("GstRate", selectedProduct.getGstRate());
        cv.put("GstAmount", selectedProduct.getGstAmount());

        int result = db.update("Inventory", cv, "rowid = ?", new String[]{String.valueOf(rowId+1)});
        if(result > 0) {
            Toast.makeText( context, "Sold successfully !", Toast.LENGTH_LONG).show();
        }
        else {
            Toast.makeText( context, "Try again !", Toast.LENGTH_LONG).show();
        }
    }

I admit there are similar questions but the answers are simply not working for me, please help.

UPDATE:

I used Listeners in the onBindViewHolder

@Override
        public void onBindViewHolder(@NonNull final InventoryViewHolder inventoryViewHolder, int i) {

            inventoryViewHolder.productName.setText(listData.get(i).getProductName());
            inventoryViewHolder.inStock.setText(String.valueOf(listData.get(i).getProductInStock()));
            inventoryViewHolder.sellingPrice.setText(String.valueOf(listData.get(i).getProductSellingPrice()));

            inventoryViewHolder.sellButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    AlertDialog.Builder builder1 = new AlertDialog.Builder(v.getContext());
                    builder1.setMessage("Select quantity");
                    builder1.setCancelable(true);
                    Context context = v.getContext();
                    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    View sell_dialog_layout = inflater.inflate(R.layout.sell_product_layout,null);
                    builder1.setView(sell_dialog_layout);
                    builder1.setIcon(R.drawable.ic_warning_black_24dp);
                    sellQuantity = (EditText) sell_dialog_layout.findViewById(R.id.elegent_number_button);
                    builder1.setPositiveButton(
                            "OK",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id)
                                {
                                    //Toast.makeText(v.getContext(),"Qty:" + sellQuantity.getText().toString() + " sell position:" + String.valueOf(getAdapterPosition()+1) , Toast.LENGTH_SHORT).show();
                                    new MSDatabase(v.getContext()).sellProduct(i, Double.parseDouble(sellQuantity.getText().toString())));
                                    notifyItemChanged(i);
                                    Log.v("Position: ", String.valueOf(i));
                                }
                            });

                    builder1.setNegativeButton(
                            "Cancel",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int id) {
                                    dialog.cancel();

                                }
                            });

                    AlertDialog alert11 = builder1.create();
                    alert11.show();


                }
            });
        }
ShashankAC
  • 1,016
  • 11
  • 25
  • If there is a need to update the clicked item you could use `notifyItemChanged()` to update just one view and not to rebuild all list or `notifyItemRemoved()` if item was removed from your list. Then you just need to pass the count of item that have been clicked and it will update/remove with animation. – Blind Kai Aug 10 '19 at 14:10
  • In your onResume() method write adapter.notifyDataSetChanged(); – Rohit Suthar Aug 10 '19 at 14:29
  • @BlindKai where exactly should I call that ? – ShashankAC Aug 10 '19 at 14:35
  • @Rohit Suthar , I tried it, it didn't make any difference. – ShashankAC Aug 10 '19 at 14:36
  • @ShashankAC if it's possible you could use listeners directly in `onBindView` and you would be able to access the view by viewHolder.*view_name* and then use `getAdapterPosition()`. – Blind Kai Aug 10 '19 at 14:46
  • @BlindKai, I transferred the onClickListener to the onBindViewHolder as you suggested and called the notifyItemChanged() after updating the database, and the problem remains as it is. If I go to another activity and come back, the recyclerview updates but not immediately after the alertdialog. – ShashankAC Aug 10 '19 at 15:21
  • 1
    @ShashankAC could you update your question code so I could get a closer look? – Blind Kai Aug 10 '19 at 15:49
  • @BlindKai I have updated, please check – ShashankAC Aug 10 '19 at 15:55
  • I've tested the code and it works. Pay attention for variable names cause I coded that in my project. I can say that it works and it changes the item after pressing "Ok". You just need to perform changes that happen when you click "Ok". – Blind Kai Aug 10 '19 at 16:26

2 Answers2

1

If there is a need to update the clicked item you could use notifyItemChanged() to update just one view and not to rebuild all list or notifyItemRemoved() if item was removed from your list.

So if you need to update item values and rebuild only this item after alert you could try to insert something like that inside the dialog (description provided below):

builder1.setPositiveButton(
    "OK",
    new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            ExampleItem item = list.get(holder.getAdapterPosition());
            notifyItemChanged(holder.getAdapterPosition());
            Toast.makeText(v.getContext(), "Item number: " + holder.getAdapterPosition(), Toast.LENGTH_SHORT).show();

        }
    });
  • list - is your list, that list that is on RecyclerView.
  • item - the element in position that you clicked.
  • holder.getAdapterPosition() - the position of click, you can apply all changes to RecyclerView element or list with help of it.
  • notifyItemChanged(holder.getAdapterPosition()); - you're notifying RecyclerView that this item was changed and it needs to redraw it.
Blind Kai
  • 514
  • 5
  • 14
  • https://stackoverflow.com/questions/41992586/why-notifyitemchanged-only-works-in-post-runnable-after-setadapter This answer seems to suggest that I might need to call notifyItemChanged() from another thread with a delay ? – ShashankAC Aug 11 '19 at 11:59
  • Anyway I found a temporary fix which doesn't even need to call notifyDatasetChanged() or any such thing. I just directly updated the item after updating the database. – ShashankAC Aug 11 '19 at 12:14
0

You can move all the logic related to that on click events and menu events into the adapter class InventoryAdapter then u can perform that adapter.notifyDatasetChanged() in those on click methods after data set change. You need to move those logics to that class. And use InventoryViewHolder as only data class which holds data only.

PushpikaWan
  • 2,437
  • 3
  • 14
  • 23
  • Didn't get you. Anyway I found a temporary fix which doesn't even need to call notifyDatasetChanged(). I just directly updated the item after updating the database. – ShashankAC Aug 11 '19 at 12:12