【问题标题】:Data fetched from FirebaseDatabase is getting shown in 3 separate AlertDialogs instead of one从 FirebaseDatabase 获取的数据显示在 3 个单独的 AlertDialogs 中,而不是一个
【发布时间】:2016-12-30 19:49:22
【问题描述】:

我从FirebaseDatabase 获取一些数据,然后将它们放入array,然后尝试在自定义AlertDialog 中的List 中显示它们。

代码如下:

query = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");

uProfile.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        query.orderByChild("someChild").addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                if (dataSnapshot != null) {

                    Map<String, String> newD = (Map<String, String>) dataSnapshot.getValue();

                    ArrayList<String> l = new ArrayList<String>();
                    l.add(newD.get("lol").substring(30));

                    String names[] = l.toArray(new String[0]);

                    AlertDialog.Builder alertDialog = new AlertDialog.Builder(Activity.this);
                    LayoutInflater inflater = getLayoutInflater();
                    View convertView = inflater.inflate(R.layout.dialog_list, null);
                    alertDialog.setView(convertView);
                    alertDialog.setTitle("title");
                    ListView lv = (ListView) convertView.findViewById(R.id.lv);
                    ArrayAdapter<String> adapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, names);
                    lv.setAdapter(adapter);

                    alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {

                        }
                    });

                    alertDialog.show();
                } else {
                    Toast.makeText(getBaseContext(), "NULLLLL", Toast.LENGTH_SHORT).show();
                }
            }

            ...
            ...
        });

    }
});

这是数据库结构:

app
 -child
   -anotherChild
     -yetAnotherChild
       -inaccessibleChild
         -someChild: "value"
         -lol: "value"

我无法在此处使用valueEventListener(),因为我无法访问inaccessibleChild。这里的inaccessibleChild 是关注特定用户的其他用户的uid。我怎样才能访问那里uid

问题是正在获取数据,但不是在AlertDialog 中的列表中显示,而是在3 中逐一显示单独的AlertDialog

这里出了什么问题?

请告诉我。

【问题讨论】:

  • child/anotherChild/yetAnotherChild 下有多少个孩子?您正在附加一个ChildEventListener,它看起来在该节点下有 3 个子节点,所以这就是为什么 onChildAdded 被触发 3 次(alertDialog.show(); 被调用 3 次
  • @Wilik 是的......这就是正在发生的事情。如何让alertDialog.show(); 仅被调用 1 次,显示所有 3 个值?
  • Reaz Murshed 的回答提供了最好的例子,使用 ValueEventListener
  • @Wilik 请查看已编辑的问题
  • @Reaz Murshed 有最好的答案。如果您不能使用 SingleValueListener,请考虑重组数据以减少和简化查询,如 DavidEast youtube.com/playlist?list=PLl-K7zZEsYLlP-k-RKFa7RyNPa9_wCH2s 的 Firebase Database For SQL Developers 系列中所示

标签: android firebase firebase-realtime-database android-arrayadapter android-alertdialog


【解决方案1】:

Firebase 事务是异步的,因此您添加 3 个子项的初始行发生在您设置侦听器之后,因此您的回调被调用了 3 次。 (制作 3 个对话框)。

将这一行移到点击之外:

 query = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");

然后当你添加下面的监听器(在点击内)它应该没问题

query.orderByChild("someChild").addChildEventListener(new ChildEventListener() {
                    @Override
                    public void onChildAdded(DataSnapshot dataSnapshot, String s) {

【讨论】:

  • 这不起作用。查看更新的问题。仍然有 3 个对话框。
【解决方案2】:

使用valueEventListeneraddChildEventListener 时设置限制。像这样,

int limit = 100;
databaseRef.child("chats").limitToLast(limit).addValueEventListener(listener);

当您完成与 firebase 相关的工作后,请务必移除监听器。像这样,

databaseRef.child("chats").limitToLast(limit).removeEventListener(listener);

谢谢。

【讨论】:

    【解决方案3】:

    在调用 .addChildEventListener() 之前创建并显示 AlertDialog

    下载适当的数据后,然后在 addChildEventListener 调用 notifyDatasetChanged() 中使用。不要在 addChildEventListener 中创建 AlertDialog

    【讨论】:

      【解决方案4】:

      您可以使用 Firebase 查询来限制侦听器下载的数据。

      query.orderByChild("someChild").limitToLast(1).addChildEventListener(...
      

      【讨论】:

        【解决方案5】:

        @Blundell 具有解决您问题的洞察力。我只是想向您推荐与addValueEventListener 类似的方法。

        值事件侦听器将在初始状态触发一次 数据,然后每次该数据的值更改时再次。

        您需要从 onClick 函数中移出 firebase 查询。所以你的查询可能看起来像这样..

        // Declare the variable names as public
        private String names[];
        
        private void addFirebaseListener() {
            ref = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");
            ref. userRef.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot snapshot) {
                  Map<String, String> newD = (Map<String, String>) dataSnapshot.getValue();
        
                  ArrayList<String> l = new ArrayList<String>();
                  l.add(newD.get("lol").substring(30));
        
                  names[] = l.toArray(new String[0]);
        
                  // Call notifyDataSetChanged each time your array gets updated
                  adapter.notifyDataSetChanged();
                }
        
                @Override public void onCancelled(FirebaseError error) { }
            });
        }
        

        现在编写一个函数来显示AlertDialog中的ListView

        // Declare the adapter as public and initialize it with null
        private ArrayAdapter<String> adapter = null;
        
        private void showListInDialog() {
            AlertDialog.Builder alertDialog = new AlertDialog.Builder(Activity.this);
            LayoutInflater inflater = getLayoutInflater();
            View convertView = inflater.inflate(R.layout.dialog_list, null);
            alertDialog.setView(convertView);
            alertDialog.setTitle("title");
            ListView lv = (ListView) convertView.findViewById(R.id.lv);
        
            if(adapter == null)
                adapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, names);
        
            // Now set the adapter
            lv.setAdapter(adapter);
        
            alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    alertDialog.dismiss();
                }
            });
        
            alertDialog.show();
        }
        

        现在在您的 onCreate 函数中,您需要先设置 Firebase 侦听器,然后像这样设置 onClick 函数。

        uProfile.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showListInDialog();
            }
        });
        

        【讨论】:

        • 请查看已编辑的问题。它说明了为什么我不能使用valueEventListener()
        • I can't use valueEventListener() here as I have no access to inaccessibleChild - 这不是很清楚。当您有一些无法访问的节点时,您为什么要读取一些数据?
        • 抱歉不清楚。请立即查看已编辑的问题。
        • 请尝试addListenerForSingleValueEvent。请参阅更新的答案。您可以尝试getKey() 获取uid。
        • 是的,我尝试了getKey() 的方法,但是当用户取消关注时,这些数据应该被删除,所以我怎么能访问这个key
        【解决方案6】:

        @Hammad 根据您的要求尝试修改后的代码。

            query = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");
        
            AlertDialog alertDialog;
            ArrayList<String> l;
        
            uProfile.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    query.orderByChild("someChild").addChildEventListener(new ChildEventListener() {
                        @Override
                        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                            if (dataSnapshot != null) {
        
                                Map<String, String> newD = (Map<String, String>) dataSnapshot.getValue();
        
            if(l == null) {
                                l = new ArrayList<String>();
            }
                                l.add(newD.get("lol").substring(30));
        
            String names[] = new String[l.size()];
            for(int length=0; length < l.size(); l++) {
                names[length] = l.get(length);
            }
        
            if(alertDialog == null) {
                                alertDialog = new AlertDialog.Builder(Activity.this).create();
                                LayoutInflater inflater = getLayoutInflater();
                                View convertView = inflater.inflate(R.layout.dialog_list, null);
                                alertDialog.setView(convertView);
                                alertDialog.setTitle("title");
                                ListView lv = (ListView) convertView.findViewById(R.id.lv);
        
            } ArrayAdapter<String> adapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, names);
                                lv.setAdapter(adapter);
        
                                alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int i) {
        
                                    }
                                });
        
        if(!alertDialog.isShowing()) {
                                alertDialog.show();
        }
                            } else {
                                Toast.makeText(getBaseContext(), "NULLLLL", Toast.LENGTH_SHORT).show();
                            }
                        }
        
                        ...
                        ...
                    });
        
                }
            });
        

        【讨论】:

        • 收到此错误:java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. 点击uProfile
        • 我编辑了我的答案并添加了 if(!alertDialog.isShowing()) 。如果有任何问题,请提及问题的确切行。
        • 还是同样的问题...这里在alertDialog.show();
        • @HammadNasir 立即尝试。我将 AlertDialog.Builder 对象更改为 AlertDialog。
        • 那么问题Cannot resolve method setPositiveButton()就出现了..
        【解决方案7】:
        ChildEventListener childEventListener = new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
            Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
        

        /**

        检索项目列表或监听项目列表的添加。此回调为每个现有的孩子触发一次,然后在每次将新的孩子添加到指定路径时再次触发。传递给侦听器的 DataSnapshot 包含新孩子的数据。

        **/

        }
        
        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
            Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
        }
        
        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {
            Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
        }
        
        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
            Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
        }
        
        @Override
        public void onCancelled(DatabaseError databaseError) {
        }
        

        }; ref.addChildEventListener(childEventListener); 使用 ChildEventListener onChildAdded 是调用您在该节点中的每个孩子。意味着您的代码运行 3 次,因此对话框出现 3 次。

        这就是问题 所以解决办法是:

        虽然使用 ChildEventListener 是读取数据列表的推荐方法,但在某些情况下将 ValueEventListener 附加到列表引用很有用。

        将 ValueEventListener 附加到数据列表会将整个数据列表作为单个 DataSnapshot 返回,然后您可以循环访问单个子数据。

        即使查询只有一个匹配项,快照仍然是一个列表;它只包含一个项目。要访问该项目,您需要遍历结果:

        inaccessibleChild.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
               //here you can access the each child of that node.
            }
        }
        
        @Override
        public void onCancelled(DatabaseError databaseError) {
            // Getting Post failed, log a message
            Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
            // ...
        }
        

        });

        【讨论】:

          【解决方案8】:

          好吧,上面的答案都没有真正的帮助,虽然Linxy的答案是正确的,但我是在解决问题后看到的。

          将所有代码移出并只写这一行:alertDialog.show(); inside setOnClickListener() 改进我的代码:

              query = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");
          
              query.orderByChild("someChild").addChildEventListener(new ChildEventListener() {
                          @Override
                          public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                              if (dataSnapshot != null) {
          
                                  Map<String, String> newD = (Map<String, String>) dataSnapshot.getValue();
          
                                  ArrayList<String> l = new ArrayList<String>();
                                  l.add(newD.get("lol").substring(30));
          
                                  String names[] = l.toArray(new String[0]);
          
                                  AlertDialog.Builder alertDialog = new AlertDialog.Builder(Activity.this);
                                  LayoutInflater inflater = getLayoutInflater();
                                  View convertView = inflater.inflate(R.layout.dialog_list, null);
                                  alertDialog.setView(convertView);
                                  alertDialog.setTitle("title");
                                  ListView lv = (ListView) convertView.findViewById(R.id.lv);
                                  ArrayAdapter<String> adapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, names);
                                  lv.setAdapter(adapter);
          
                                  alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                      @Override
                                      public void onClick(DialogInterface dialogInterface, int i) {
          
                                      }
                                  });
                              } else {
                                  Toast.makeText(getBaseContext(), "NULLLLL", Toast.LENGTH_SHORT).show();
                              }
                          }
          
                          ...
                          ...
                      });
          
          uProfile.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                  alert11.show();
              }
          });
          

          无论如何,我要感谢所有回答这个问题的人。

          和平。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2019-12-21
            • 2021-12-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多