Jennifer
Jennifer

Reputation: 1

Why notifyItemChanged(int position) of RecyclerView is working only once than thrice?

I am trying to replicate the action of Recyclerview methods wherein I am having 3 items in ArrayList itemname and also stored in sqlite table. Corresponding to each item name an 'id' is assigned in the table. Here I want that for each below iteration all the 3 items shall change color as per the condition defined in the onBindViewHolder() method of the Adapter class.

for (int i=0; i<itemname.size(); i++) {   
String query = "SELECT id FROM sec_table WHERE address='"+itemname.get(i)+"'";
                 Cursor c1 = SecDB.rawQuery(query,null);

                 if (c1 != null && c1.getCount() != 0)
                 {
                     while (c1.moveToNext()) {
                         id = c1.getInt(c1.getColumnIndex("id"));
                     }
                        c1.close();
                 }

      alert_position = id;                // Class variable 'alert_position'
      userAdapter.notifyItemChanged(alert_position);
}

The onBindViewHolder() method is responsible for changing the color of the cards as per the value of the position variable in the Main class.

@Override                // Inside the Adapter class
public void onBindViewHolder(@NonNull final UserViewHolder holder, final int position) {
    final SecurityDetails userDetails = secDetailsList.get(position);
    LogsDatabaseHelper logsDBHelper = new LogsDatabaseHelper(context);


    SecurityDatabaseHelper secDBHelper = new SecurityDatabaseHelper(context);
    LogsDB = logsDBHelper.getWritableDatabase();
    SecDB = secDBHelper.getWritableDatabase();

    if (requestQueue==null)
    requestQueue = Volley.newRequestQueue(context);      // Creating RequestQueue...

    holder.tvName.setText(userDetails.getName());

    if(position == Security.alert_position) {
        Toast.makeText(context, position+"", Toast.LENGTH_SHORT).show();
        holder.state.setText("< Activity Detected >");
        holder.state.setTextColor(Color.RED);
        holder.cards.setBackgroundColor(0x33FF0000);
    }
  }

Here the above for loop is changing the color of the last card only although it is feeding position values 1,2,3 simultaneously to the notifyItemChanged().


But when the same logic of updating the cards is performed via android Volley all 3 cards are turning red one by one. The volley method is shown below:

for (int i=0; i<itemname.size(); i++) {   // Calls the Volley method with different items name from arraylist
      CardsUpdate(itemname.get(i));
}

private void CardsUpdate(final String email) {
    StringRequest stringRequest = new StringRequest(Request.Method.POST, "http://plateau.com/SecuritySensorUpdate.php", new Response.Listener<String>()
    {
        int id;
        @Override
         public void onResponse(String ServerResponse) {
            if (ServerResponse.equalsIgnoreCase("1")) {
                ContentValues values = new ContentValues();
                values.put(SecurityDatabaseContract.UserDatabase2.SEC_NAME_COL4, "1");
                SecDB.update(SecurityDatabaseContract.UserDatabase2.TABLE_NAME2, values, "address='" + email + "'", null);

                String query = "SELECT id FROM sec_table WHERE address='" + email + "'";
                Cursor c1 = SecDB.rawQuery(query, null);

                if (c1 != null && c1.getCount() != 0) {
                    while (c1.moveToNext()) {
                        id = c1.getInt(c1.getColumnIndex("id"));
                    }
                    c1.close();
                }

                alert_position = id;
                userAdapter.notifyItemChanged(alert_position);
            }
            else
                Toast.makeText(Security.this, ServerResponse, Toast.LENGTH_SHORT).show();
        }},
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError volleyError) {
                  //  Toast.makeText(Security.this, volleyError.toString(), Toast.LENGTH_SHORT).show();
                }
            })
    {
        @Override
        protected Map<String, String> getParams()
        {
            Map<String, String> params = new HashMap<>();
            params.put("email", email);
            return params;
        }};

    requestQueue.add(stringRequest);        // Adding the StringRequest object into requestQueue.
}

Kindly suggest some workaround to achieve the desired output of updating the colors of all 3 cards.

Upvotes: 0

Views: 2994

Answers (3)

Jennifer
Jennifer

Reputation: 1

Got the solution. The same can be achieved by adding positions to Arraylist and then further calling that ArrayList via a for loop within the Recyclerview adapter class by iteration.

 if (tokens[0].equals("1")) {
                     power_position.add(id);
                     userAdapter.notifyDataSetChanged();
                 }

And within the Recyclerview adapter class.

for (int i=0; i<Security.alert_position.size(); i++) {
        if (position == Security.alert_position.get(i)) {
            holder.state.setText("< Data Changed >");
            holder.state.setTextColor(Color.RED);
            holder.cards.setBackgroundColor(0x33FF0000);
        }
    }

This will refresh the Recyclerview at once rather than calling individual recycler view items for updates.

Upvotes: 0

Martin Marconcini
Martin Marconcini

Reputation: 27246

  1. I would use a ListAdapter<T, K> (from RecyclerView).
  2. During the Cursor iteration, I would create (or modify) a new List based on the new values.
  3. I would then submit a new list to the adapter (ListAdapter has a method called submitList(T).

Since you need to provide a "Diff Util" callback to the ListAdapter, it will take care of rebinding the views whose data has changed.

If you really want "real-time" one by one changing, then you'd need to dive deeper into having a ViewModel expose a state (perhaps a Flow) that you can observe and react to each emit(...).

There are alternatives here, but I would try the ListAdapter first, it's included in Android, and would likely work fine.

If you need help setting up a List Adapter, and because I've been asked this question over 9000 times, I have created the simplest recycler view sample over at GitHub I can imagine :)

Upvotes: 1

CSmith
CSmith

Reputation: 13458

calls to notifyItemChanged() are not synchronous, i.e. onBindViewHolder() will be called at a later time. By the time onBindViewHolder() is called, the static property Security.alert_position contains the newest value (from the 3rd time through the loop).

Recommendations:

Add Log statements to your call to notifyItemChanged() and onBindViewHolder(). You'll likely see that notifyItemChanged() calls precede the calls to onBindViewHolder(). Log the position and Security.alert_position values in onBindViewHolder(), this should explain why you're seeing what you're seeing.

Log.d("MY-TAG", String.format("calling notifyItemChanged(%d)", alert_position));

in onBindViewHolder()

Log.d("MY-TAG", String.format("onBindViewHolder(%d), Security.alert_position=%d", position, Security.alert_position));

I suspect you'll see the three calls to onBindViewHolder() showing correct position values (1,2,3), but Security.alert_position will always be 3.

While this recommendation won't solve your program logic, it will hopefully help you understand what's occurring.

Upvotes: 0

Related Questions