为啥 notifyItemChanged(position) 不更新 RecyclerView 中的所有项目?

Posted

技术标签:

【中文标题】为啥 notifyItemChanged(position) 不更新 RecyclerView 中的所有项目?【英文标题】:Why notifyItemChanged(position) not updating all the items in RecyclerView?为什么 notifyItemChanged(position) 不更新 RecyclerView 中的所有项目? 【发布时间】:2021-08-23 23:17:54 【问题描述】:

在从 firebase 数据库中获取某些值后,我正在尝试在 recyclerview 中同时更新 3 个项目。下面的方法对 ArrayList itemname 进行三次迭代,每个值都调用 SecurityAlertSensor(itemname) 方法。

在每次迭代的此方法中,回收器视图中的相应项目应更改。就像如果从 SQLite DB 获取的 id 是 1,那么回收器视图的第一项将被更新,并且在下一次迭代中,如果 id 是 2,那么第二项将针对第 3 项进行类似更新。

但是使用下面的代码,只有回收器视图的三项中的最后一项正在更新前两项,这不起作用。

for (int i=0; i<itemname.size(); i++) 
        if (isOnline()) 
            SecurityAlertSensor(itemname.get(i));
        
        else if(!isOnline())
            Toast.makeText(getApplicationContext(), "Update Failed!", Toast.LENGTH_SHORT).show();
    

此方法在每次迭代时从 Firebase DB 获取值,如果值为“1”,则调用 notifyItemChanged(position) 以更新 recyclerview 中的特定项目。

private void SecurityAlertSensor(final String email) 
        sec_ref2 = sec_ref1.child(email);
        sec_ref2.addValueEventListener(new ValueEventListener() 
            int id;
            @SuppressLint("SetTextI18n")
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot)
            
             String value = dataSnapshot.getValue(String.class);
             if (value != null)
             
                 if (value.contains("0") || value.contains("1"))
                 
                     Toast.makeText(Security.this, value, Toast.LENGTH_SHORT).show();
                     String[] tokens = value.split(",");
                     power_state = tokens[0];
                     ContentValues values = new ContentValues();
                     values.put(SecurityDatabaseContract.UserDatabase2.SEC_NAME_COL4, tokens[0]);
                     values.put(SecurityDatabaseContract.UserDatabase2.SEC_NAME_COL5, tokens[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();
                     

                     if (tokens[1].equals("1")) 
                         alert_position = id;
                         userAdapter.notifyItemChanged(alert_position);
                     
                 
                 sec_ref2.removeEventListener(this);
             
                else
                        Toast.makeText(Security.this, "Device Not Found!", Toast.LENGTH_SHORT).show();
            

            @Override
            public void onCancelled(@NonNull DatabaseError error) 
                Toast.makeText(getApplicationContext(), "Not Found!", Toast.LENGTH_LONG).show();  // Failed to read value
            
        );

在 Recyclerview Adapter 类的 onBindViewHolder 方法中。每次在主类中实例化 notifyItemChanged(position) 时都会调用它。

public void onBindViewHolder(@NonNull final UserViewHolder holder, final int position) 
 if(position == Security.alert_position) 
            holder.state.setText("< Activated >");
            holder.state.setTextColor(Color.RED);
            holder.cards.setBackgroundColor(0x33FF0000);
        

【问题讨论】:

【参考方案1】:

你的问题在onBindViewHolder():

if(position == Security.alert_position) 

您依赖于在每次迭代中将Security.alert_position 的值设置为项目位置。我猜对notifyItemChanged() 的每次调用都不会立即对onBindViewHolder() 进行调用,但是对onBindViewHolder() 的调用是异步的,并且会在以后发生。此时Security.alert_position已经设置为第三个值,所以每次输入onBindViewHolder()时,Security.alert_position的值就是你设置的最后一个值。

您可以添加一些调试日志来验证我的假设。一般来说,您的架构并不好,因为您在另一个地方做某事时依赖于存储在一个地方的状态。

【讨论】:

感谢大卫的建议。但是,为什么在通过 Volley 从 mysql 获取相同数据时,同样的技巧会起作用(所有三个项目都在正确更新)?另外,您能否为此提出一些解决方法? 我不太了解 Volley 的工作原理,无法回答这个问题,抱歉。解决方法?从您发布的代码中,我对您在这里尝试做的事情知之甚少。不过,通常这种架构是错误的。

以上是关于为啥 notifyItemChanged(position) 不更新 RecyclerView 中的所有项目?的主要内容,如果未能解决你的问题,请参考以下文章

Recyclerview在Android中notifyItemChanged后防止触摸事件

Android RecyclerView Adapter notifyItemChanged() 获取 IllegalStateException 已经在池中

Android-RecyclerView系列 notifyItemChanged() - 实现单选选中状态更新

Android RecycleView notifyItemChanged()刷新闪烁异常处理

为啥 matplotlib.PatchCollection 会弄乱补丁的颜色?

为啥 background-size:100% 100%;不适用于此 SVG?