如何使用 Firebase 查询的结果最终在 Android Studio 中填充 ListView

Posted

技术标签:

【中文标题】如何使用 Firebase 查询的结果最终在 Android Studio 中填充 ListView【英文标题】:How to use results from a Firebase query to ultimately populate a ListView in Android Studio 【发布时间】:2015-12-07 06:53:06 【问题描述】:

我发现我的 ListView 在应用程序运行时没有更新,因为适配器是在 Firebase 查询发生之前设置的。

第一个问题:有没有为查询完成设置监听器?这样我可以在那里设置adapter。我知道一些 Firebase 方法会自动生成 onComplete()/onSuccess() 侦听器。

出于调试目的,我可以通过重新输入onResume() 来触发对 ListView 的更新。这是我收到错误消息的地方:

The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(16908298, class android.widget.ListView) with Adapter(class android.widget.ArrayAdapter)]

据我了解,如果我在ArrayAdapter 中使用usernames,则无法在onChildAdded() 方法内将项目添加到ArrayListusernames”。

第二个问题:我还能如何存储从我的 Firebase 查询返回的值并使用这些值最终填充 ListView 而无需直接从“后台线程”更改其内容?

我的尝试:

public class EditFriendsActivity extends AppCompatActivity 

    protected ArrayList<String> usernames = new ArrayList<>();

    @Override
    protected void onResume() 
        super.onResume();

        Firebase usernameRef = ref.child("users");
        Query queryRef = usernameRef.orderByKey(); 

        queryRef.addChildEventListener(new ChildEventListener() 
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) 
            String username = (String) dataSnapshot.child("username").getValue(); 
            usernames.add(username); 
            Log.v(TAG, usernames + "");
        
        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String s) 
            Log.v(TAG, "Inside ChildChanged");
        

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) 
            Log.v(TAG, "Inside ChildRemoved");
        

        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String s) 
            Log.v(TAG, "Inside ChildMoved");
        

        @Override
        public void onCancelled(FirebaseError firebaseError) 

            Log.e(TAG, firebaseError.getMessage());

            AlertDialog.Builder builder = new AlertDialog.Builder(EditFriendsActivity.this);
            builder.setTitle(R.string.error_title)
            .setMessage(firebaseError.getMessage())
            .setPositiveButton(android.R.string.ok, null);

            AlertDialog dialog = builder.create();
            dialog.show();
            
        );

        // Array adapter
        ArrayAdapter<String> adapter = new ArrayAdapter<>(
                EditFriendsActivity.this,
                android.R.layout.simple_list_item_multiple_choice, 
                usernames);

        mFriendsList.setAdapter(adapter); 
    
  

更新的代码:修复了每次从标记答案和清除列表中听者的错字。

Firebase userRef = ref.child("users");
userRef.addListenerForSingleValueEvent(new ValueEventListener() 

    @Override
    public void onDataChange(DataSnapshot dataSnapshot) 
        usernames.clear(); // Clear list before updating
        for (DataSnapshot child : dataSnapshot.getChildren()) 
            String username = (String) child.child("username").getValue();
            usernames.add(username);
            Log.v(TAG, usernames + "");
                

跟进问题Here

【问题讨论】:

我稍后会写一个答案,但你可能想看看github.com/firebase/FirebaseUI-Android。这个库有一个 FirebaseListAdapter 实现,可以为您处理。 你可能应该提出一个新问题并删除后续问题的伙伴 另外:如果我的答案有错别字,请在此处修复。我们不介意修复我们在 *** 上的帖子。如果您的解决方案有很大不同,请将其添加为您自己的答案。 非常酷!不知道我可以编辑别人的帖子。我很欣赏这些提示,因为我在这里还很新,而且一般都在编码。有这样的社区真是令人鼓舞! 【参考方案1】:

有没有为查询完成设置监听器?这样我就可以在那里设置适配器。我知道一些 Firebase 方法会自动生成 onComplete()/onSuccess() 侦听器。

Firebases 将数据从后端同步到您的应用程序。因此,与具有请求/响应流的传统数据库不同,Firebase 查询并没有真正完成。这就是为什么在FirebaseUI library 中我们保持一个开放的侦听器并通知 ListView 任何更改。

也就是说:如果你只想从一个位置加载数据一次,你可以使用addSingleValueEventListener

queryRef.addListenerForSingleValueEvent(new ValueEventListener() 
    @Override
    public void onDataChange(DataSnapshot snapshot) 
        for (DataSnapshot child: snapshot.getChildren()) 
            String username = (String) child.child("username").getValue(); 
            usernames.add(username); 
        
        ArrayAdapter<String> adapter = new ArrayAdapter<>(
            EditFriendsActivity.this,
            android.R.layout.simple_list_item_multiple_choice, 
            usernames);

        mFriendsList.setAdapter(adapter); 
    

    @Override
    public void onCancelled(FirebaseError firebaseError) 
        Log.e(TAG, firebaseError.getMessage());
    
);

上面可能有错别字,但这是它的大致要点。

【讨论】:

我怀疑我对你编写的每个循环的解释。这是我的镜头“对于快照中的每个节点,获取它的子节点(在我的情况下,/users 的子节点将是 /users/username 和 /users/email)并在每个节点上执行 .child("username").getValue();。 "快照不是我的参考中所有节点的集合吗?如果是这样,为什么我们需要执行 getChildren()?你能告诉我它是怎么读的吗? 您正在响应child_added 事件,因此您会收到每个孩子的电话。我听了一个value 事件,它让所有的孩子都一口气。这就是为什么我需要一个循环,而你不需要。 您介意看看我的后续问题吗?【参考方案2】:

您可以简单地使用查询

Query ref = FirebaseDatabase.getInstance().getReference()
            .child("users").orderByChild("name");

【讨论】:

以上是关于如何使用 Firebase 查询的结果最终在 Android Studio 中填充 ListView的主要内容,如果未能解决你的问题,请参考以下文章

如何以正确的方式在 Firebase 查询中加入结果

如何在 Firebase 云 Firestore 中查询一个字段然后按另一个字段排序?

得到错误的结果:从 AngularJS 访问 api 并使用 NodeJS 从 firebase 查询结果时出现时区问题

没有结果时,Firebase 查询不调用块

Swift -Firebase 如何知道 .childAdded 是不是为空?

当firebase查询与数据库中的任何内容都不匹配时如何调用函数