Firebase Query 从两个节点一起获取数据

Posted

技术标签:

【中文标题】Firebase Query 从两个节点一起获取数据【英文标题】:Firebase Query to fetch data from two nodes together 【发布时间】:2021-12-16 17:08:59 【问题描述】:

我在 Firebase 实时数据库中有一个名为“Posts”的根节点。在其中,我有两个节点,分别称为“ImagePosts”和“TextPosts”。在“ImagePosts”(和“TextPosts”)中,我有各种帖子的 postId。在 postID 中,我拥有该特定帖子的所有详细信息,包括 postedAt(发布时间)。

我想要做的是编写一个查询以从“ImagePosts”和“TextPosts”中获取数据,并以降序/反向顺序显示所有帖子(也就是说,最后/最近发布的帖子应该显示根据“postedAt”,在我的回收站视图的顶部)。

Please click here to see database structure

为了实现这一点,我创建了一个名为 Post 的模型和两个名为“PostAdapter”和“TextPostAdapter”的适配器。我的回收站视图是“DashboardRV”。到目前为止我尝试了什么:

家庭片段代码:

public class HomeFragment extends Fragment 

ShimmerRecyclerView dashboardRV;
ArrayList<Post> postList;

public HomeFragment() 



@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);



@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) 

    View view = inflater.inflate(R.layout.fragment_home, container, false);

    dashboardRV = view.findViewById(R.id.dashboardRv);
    dashboardRV.showShimmerAdapter();
    postList = new ArrayList<>();
    PostAdapter postAdapter = new PostAdapter(postList, getContext());
    LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
    dashboardRV.setLayoutManager(layoutManager);
    dashboardRV.addItemDecoration(new DividerItemDecoration(dashboardRV.getContext(), DividerItemDecoration.VERTICAL));
    dashboardRV.setNestedScrollingEnabled(false);

    dashboardRV.setAdapter(postAdapter);
    postList.clear();

database.getReference()
            .child("Posts")
            .child("ImagePosts")
            .addValueEventListener(new ValueEventListener() 
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) 
                  
                    for (DataSnapshot dataSnapshot : snapshot.getChildren()) 
                        Post post = dataSnapshot.getValue(Post.class);
                        post.setPostId(dataSnapshot.getKey());
                        postList.add(post);
                    
                    Collections.reverse(postList);
                    dashboardRV.hideShimmerAdapter();
                    postAdapter.notifyDataSetChanged();
                

                @Override
                public void onCancelled(@NonNull DatabaseError error) 

                
            );

    database.getReference()
            .child("Posts")
            .child("TextPosts")
            .addValueEventListener(new ValueEventListener() 
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) 
                    postList.clear();
                    for (DataSnapshot dataSnapshot : snapshot.getChildren()) 
                        Post post = dataSnapshot.getValue(Post.class);
                        post.setPostId(dataSnapshot.getKey());
                        postList.add(post);
                    
                    Collections.reverse(postList);
                    dashboardRV.hideShimmerAdapter();
                    textPostAdapter.notifyDataSetChanged();
                

                @Override
                public void onCancelled(@NonNull DatabaseError error) 

                
            );

但这种方法的问题在于它不会同时显示所有“TextPosts”和“ImagePosts”。它只显示打开应用程序上的所有图像帖子,然后当我更改片段并返回时,它会显示所有文本帖子。我只是卡在这里。

【问题讨论】:

您在哪里使用此代码?在片段或 ViewModel 或活动中 @DeePanShu 我已经在我的家庭片段中使用了这个代码。 您正在使用一个带有两个不同适配器的 recyclerview(dashboardRV)。这种方式适合你吗?? 是的,我明白这可能是我的问题的原因,但我想在一个 RV 中显示所有帖子。请指导我该怎么做。 【参考方案1】:

一次只为一个回收器视图使用一个适配器:

以下是同时包含(图片和文本帖子)的单个适配器的代码:

postList = new ArrayList<>();

PostAdapter postAdapter = new PostAdapter(postList, getContext());
dashboardRV.setAdapter(postAdapter);

// call clear before refreshing the list
postList.clear();

database.getReference()
            .child("Posts")
            .child("ImagePosts")
            .addValueEventListener(new ValueEventListener() 
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) 

                    for (DataSnapshot dataSnapshot : snapshot.getChildren())
                        Post post = dataSnapshot.getValue(Post.class);
                        post.setPostId(dataSnapshot.getKey());
                        postList.add(post);
                    
                    Collections.sort(postList);
                    dashboardRV.hideShimmerAdapter();
                    postAdapter.notifyDataSetChanged();
                

                @Override
                public void onCancelled(@NonNull DatabaseError error) 

                
            );

    database.getReference()
            .child("Posts")
            .child("TextPosts")
            .addValueEventListener(new ValueEventListener() 
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) 

                    for (DataSnapshot dataSnapshot : snapshot.getChildren())              
                        Post post = dataSnapshot.getValue(Post.class);
                        post.setPostId(dataSnapshot.getKey());
                        postList.add(post);
                    
                    Collections.sort(postList);
                    dashboardRV.hideShimmerAdapter();
                    postAdapter.notifyDataSetChanged();
                

                @Override
                public void onCancelled(@NonNull DatabaseError error) 

                
            );

// 使 Post 类实现可与获取后的列表进行排序:

// A class 'Post' that implements Comparable
class Post implements Comparable<Post>

    ...
 
    // Used to sort posts by their postedAt
    public int compareTo(Post p)
    
        return this.postedAt - p.postedAt;
    
 
    ...

此链接将有助于探索如何根据postedAt进行排序:Using Comparable

【讨论】:

嘿@DeePanShu 我试过你说的。问题仍然没有解决。首先,当应用打开时,Home Fragment 中只会显示图片帖子。只有当我在片段之间切换,然后回到主页片段时,才会显示图片帖子 + 文字帖子。其次,既然您建议我只为一个 RV 使用一个适配器,我如何在一个适配器中充气两个布局,因为文本发布和图像发布的布局不同。 共享 HomeFragment 的代码,对于一个适配器中的两种布局,您必须在一个适配器中使用 2 个视图持有者:***.com/a/58744127/10954249 我已经更新并包含了 Home Fragment 的代码。请检查@DeePanShu。 请将数据库节点获取代码转移到 onViewCreated() 方法并应用我的代码,我没有看到我的代码被应用。 我已经应用了你的代码。将我的帖子类更改为包含可比较的,仅使用一个适配器并将 postList.clear() 和dashboardRV.setAdapter(postAdapter) 移到顶部(只需复制粘贴您的代码)。而且我真的不明白您将代码转移到 onViewCreated() 方法的意思。对不起,我有点初学者。【参考方案2】:

要在本地合并 2 个单独的 Firebase 实时数据库请求,我建议您使用 Tasks.whenAllSuccess() 方法。您可以使用以下代码行来实现这一点:

DatabaseReference imagePostsRef = database.getReference()
        .child("Posts")
        .child("ImagePosts");
DatabaseReference textPostsRef = database.getReference()
        .child("Posts")
        .child("TextPosts");

Task firstTask = imagePostsRef.get();
Task secondTask = textPostsRef.get();

Task combinedTask = Tasks.whenAllSuccess(firstTask, secondTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() 
    @Override
    public void onSuccess(List<Object> list) 
         //Do what you need to do with your list
    
);

如您所见,当覆盖“onSuccess()”方法时,结果是一个对象列表。最后,只需将列表中的每个对象映射为 Post 类型的对象,然后将新列表传递给 single 适配器。

【讨论】:

嘿克里希纳。我可以帮助您了解其他信息吗?如果您认为我的回答对您有所帮助,请考虑通过单击左侧投票箭头下方的复选标记(✔️)来接受它。应将颜色更改为绿色。我真的很感激。谢谢!

以上是关于Firebase Query 从两个节点一起获取数据的主要内容,如果未能解决你的问题,请参考以下文章

如何获得 Firebase 节点的随机子节点?

如何从 Firebase Android Query 中的 StartAt 特定位置和 EndAt 特定位置获取值

从 Firebase 中的嵌套查询填充回收站视图

从 firebase (Swift) 获取子节点

如何从 firebase 数据库参考中获取子节点列表?

如何使用 HashMap 从 Android 的 Firebase 中的子节点获取数据?