【问题标题】:Why notifyItemChanged(int position) of RecyclerView is working only once than thrice?为什么 RecyclerView 的 notifyItemChanged(int position) 只工作一次而不是三次?
【发布时间】:2021-08-25 05:45:55
【问题描述】:

我正在尝试复制 Recyclerview 方法的操作,其中我在 ArrayList itemname 中有 3 个项目,并且还存储在 sqlite 表中。对应于每个项目名称,在表中分配了一个“id”。在这里,我希望对于下面的每个迭代,所有 3 个项目都应根据 Adapter 类的 onBindViewHolder() 方法中定义的条件改变颜色。

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);
}

onBindViewHolder()方法负责根据Main类中位置变量的值改变卡片的颜色。

@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);
    }
  }

虽然上面的 for 循环同时将位置值 1、2、3 提供给 notifyItemChanged(),但这里只是改变了最后一张卡片的颜色。


但是当通过 android Volley 执行更新卡片的相同逻辑时,所有 3 张卡片都一张一张变红。截击方法如下图:

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.
}

请提出一些解决方法,以实现更新所有 3 张卡片颜色的预期输出。

【问题讨论】:

  • 欢迎来到 SO。你没有在这里使用列表视图,对吧?我已经删除了它的标签,好像你正在使用回收站

标签: android android-recyclerview android-volley notifydatasetchanged


【解决方案1】:

找到解决方案。同样可以通过向 Arraylist 添加位置,然后通过迭代在 Recyclerview 适配器类中通过 for 循环进一步调用该 ArrayList 来实现。

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

在 Recyclerview 适配器类中。

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);
        }
    }

这将立即刷新 Recyclerview,而不是调用单个回收器视图项进行更新。

【讨论】:

    【解决方案2】:
    1. 我会使用ListAdapter&lt;T, K&gt;(来自 RecyclerView)。
    2. 在游标迭代期间,我将根据新值创建(或修改)一个新列表。
    3. 然后我会向适配器提交一个新列表(ListAdapter 有一个名为 submitList(T) 的方法。

    由于您需要为 ListAdapter 提供“Diff Util”回调,它会负责重新绑定数据已更改的视图。

    如果您真的想要“实时”一一更改,那么您需要深入研究让 ViewModel 公开您可以观察到的状态(可能是 Flow)并对每个emit(...)做出反应。

    这里有替代方案,但我会先尝试 ListAdapter,它包含在 Android 中,并且可能会正常工作。

    如果您在设置列表适配器时需要帮助,并且因为我已经被问到这个问题超过 9000 次,所以我创建了 the simplest recycler view sample over at GitHub 我可以想象 :)

    【讨论】:

      【解决方案3】:

      notifyItemChanged() 的调用不是同步的,即onBindViewHolder() 将在稍后调用。在调用onBindViewHolder() 时,静态属性Security.alert_position 包含最新值(从第三次循环开始)。

      建议:

      在您对notifyItemChanged()onBindViewHolder() 的调用中添加日志语句。您可能会看到notifyItemChanged() 调用在onBindViewHolder() 调用之前。在onBindViewHolder() 中记录位置和Security.alert_position 值,这应该可以解释为什么您会看到您所看到的。

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

      在 onBindViewHolder()

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

      我怀疑您会看到对 onBindViewHolder() 的三个调用显示正确的位置值 (1,2,3),但 Security.alert_position 将始终为 3。

      虽然此建议无法解决您的程序逻辑,但它有望帮助您了解正在发生的事情。

      【讨论】:

      • 感谢您的指出,但为什么通过 Volley 方法同样可以正常工作?另外,您能否建议一些解决方法,让我可以同时刷新所有 3 张卡,而不是仅刷新最后一张。虽然 volley 方法似乎运行良好,但现在我已经转移到 Firebase。
      • @Jennifer,我不是 100% 清楚你想要完成什么。您可以使用notifyDataSetChanged() 而不是notifyItemChanged() 强制重新加载整个RecyclerView,这将重新创建并重新绑定所有视图,position == Security.alert_position 的项目将变为红色。关于 Volley 方法,我没有看到循环,只有一个 notifyItemChanged() 调用,因此 Security.alert_position 将具有可预测的值。建议的日志记录应该可以帮助您更好地了解正在发生的事情。
      • 嗯,我有一个 Recyclerview,我通过从 MySQL/Firebase 获取数据来膨胀它。每张卡代表一个无线传感器,在 DB 中具有特定的名称标签。该传感器被获取并存储在 SQLite 本地数据库中,并具有相应的 id。为了持续监控传感器的在线状态,通过 Volley 查询 MySQL,如果从数据库接收到值“1”,则该传感器的卡变为红色。如果所有 3 个传感器都打开,那么它们必须变成红色。这里 notifyDataSetChanged() 也只将最后一张卡片变为红色。 volley 方法通过 for 循环调用。
      • for (int i=0; i
      • 虽然 Firebase onDataSetChange() 方法将卡片实时变为红色,即传感器状态发生变化并且传感器活动类或 Recyclerview 在屏幕上打开但传感器已经处于活动状态并且传感器活动启动,然后只有最后一张卡是红色的问题突然出现。
      猜你喜欢
      • 2021-06-10
      • 2021-08-23
      • 2016-09-07
      • 2017-01-13
      • 2012-03-09
      • 2011-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多