(Android App) Is there a way to retrieve data from a Firebase database while inside the SnapshotParser part of the FirebaseRecyclerOptions query?

huangapple go评论65阅读模式
英文:

(Android App) Is there a way to retrieve data from a Firebase database while inside the SnapshotParser part of the FirebaseRecyclerOptions query?

问题

因此,正如标题所述,我想在构建FirebaseRecyclerOptions中的对象之前从Firebase数据库检索数据,以便在FirebaseRecyclerAdapter中使用。基本上,我正在尝试在我正在开发的应用程序中创建一个好友列表。数据库的结构如下:

Friends:
  uid1:
    id: friendID
  uid2:
    id: friendID

Users:
  uid1:
    name: name
    status: status
    image: profileImageUrl
  uid2:
    name: name
    status: status
    image: profileImageUrl

我目前的代码如下:

FirebaseRecyclerOptions<Users> options = new FirebaseRecyclerOptions.Builder<Users>()
    .setQuery(usersDatabase, new SnapshotParser<Users>() {
        @NonNull
        @Override
        public Users parseSnapshot(@NonNull DataSnapshot snapshot) {
            System.out.println(snapshot);
            rootRef.child("Users").child(snapshot.getValue().toString()).addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                    System.out.println(dataSnapshot);
                    name = dataSnapshot.child("name").getValue().toString();
                    status = dataSnapshot.child("status").getValue().toString();
                    image = dataSnapshot.child("image").getValue().toString();
                    return;
                }

                @Override
                public void onCancelled(@NonNull DatabaseError databaseError) {

                }
            });
            System.out.println(snapshot);
            return new Users(name, image, status);
        }
    }).build();

问题是我添加的ValueEventListener直到返回新的Users实例后才触发。我应该将ValueEventListener添加到与FirebaseRecyclerOptions查询相同的DatabaseReference(usersDatabase)中吗?

英文:

So, as the title says, I am looking to retrieve data from a Firebase database before constructing an object in FirebaseRecyclerOptions to be used in a FirebaseRecyclerAdapter. Basically, what I am trying to do is make a friends list in an app I'm working on. This what the database looks like:

Friends:
  uid1:
    id: friendID
  uid2:
    id: friendID

Users:
  uid1:
    name: name
    status: status
    image: profileImageUrl
  uid2:
    name: name
    status: status
    image: profileImageUrl

I've got code that currently looks like this:

FirebaseRecyclerOptions&lt;Users&gt; options = new FirebaseRecyclerOptions.Builder&lt;Users&gt;().setQuery(usersDatabase, new SnapshotParser&lt;Users&gt;() {
        @NonNull
        @Override
        public Users parseSnapshot(@NonNull DataSnapshot snapshot) {
            System.out.println(snapshot);
            rootRef.child(&quot;Users&quot;).child(snapshot.getValue().toString()).addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                    System.out.println(dataSnapshot);
                    name = dataSnapshot.child(&quot;name&quot;).getValue().toString();
                    status = dataSnapshot.child(&quot;status&quot;).getValue().toString();
                    image = dataSnapshot.child(&quot;image&quot;).getValue().toString();
                    return;
                }

                @Override
                public void onCancelled(@NonNull DatabaseError databaseError) {

                }
            });
            System.out.println(snapshot);
            return new Users(name, image, status);
        }
    }).build();

The problem is that the ValueEventListener I add does not trigger until after the new Users instance is returned. Should I be adding the ValueEventListener to the same DatabaseReference (userDatabase) as the FirebaseRecyclerOptions query?

答案1

得分: 1

你所尝试的在FirebaseUI中并不是真正可能的。 快照解析器需要立即或同步地返回一个User对象。 你不能执行一个异步数据库查询(它不会立即完成,甚至不能保证完全完成)来提供那个值。

如果你需要执行多个查询以填充你的视图,你将无法有效地使用FirebaseUI。 你可能需要考虑提前执行所有的查询,或者编写一个特殊的适配器,允许在结果变为可用时异步地填充视图内容。 这将需要编写大量代码来正确实现。

英文:

What you're trying to do isn't really possible with FirebaseUI. The snapshot parser needs to return a User object immediately, or synchronously. You can't perform an asynchronous database query (which does not complete immediately, or even guaranteed to complete at all) in order to provide that value.

If you need to perform multiple queries in order to populate your views, you won't be able to use FirebaseUI effectively. You should probably consider doing all your lookups ahead of time, or write a special adapter that allows view contents to be populated asynchronously as the results become available. This will end up being a lot of code to do correctly.

答案2

得分: 0

似乎为自己的问题提供答案有些多余,但这主要是为了帮助那些遇到类似问题的人。按照 @Doug Stevenson 的建议,我开始尝试创建自定义的回收适配器(recycler adapters)和选项类(options class)。然而,我意识到选项的查询可以进行修改。因此,基本的解决方案如下:

Query query = database.collection("Users");

@Override
protected void onStart() {
    super.onStart();
    String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
    DocumentReference ref = FirebaseFirestore.getInstance().collection("Users").document(uid);
    ref.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
        @Override
        public void onComplete(@NonNull Task<DocumentSnapshot> task) {
            if (task.isSuccessful()){
                DocumentSnapshot document = task.getResult();
                if (document.exists()) {
                    friends = (ArrayList<String>) document.get("friends");
                    if (friends.size() > 0) {
                        FirestoreRecyclerOptions<Users> options = new FirestoreRecyclerOptions.Builder<Users>().setQuery(query.whereIn("user_id", friends), Users.class).build();
                        FirestoreRecyclerAdapter<Users, UsersViewHolder> adapter = new FirestoreRecyclerAdapter<Users, UsersViewHolder>(options) {
                            @Override
                            public UsersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                                View view = LayoutInflater.from(parent.getContext())
                                        .inflate(R.layout.single_user_layout, parent, false);
                                return new UsersViewHolder(view);
                            }

                            @Override
                            protected void onBindViewHolder(@NonNull UsersViewHolder usersViewHolder, int i, @NonNull Users users) {
                                if (users != null) {
                                    usersViewHolder.setName(users.name);
                                    usersViewHolder.setStatus(users.status);
                                    usersViewHolder.setImg(users.image);

                                    final String userID = getSnapshots().getSnapshot(i).getId();
                                    usersViewHolder.mView.setOnClickListener(new View.OnClickListener() {
                                        @Override
                                        public void onClick(View view) {
                                            Intent profilePage = new Intent(FriendsList.this, ProfileActivity.class);
                                            profilePage.putExtra("userID", userID);
                                            startActivity(profilePage);
                                        }
                                    });
                                }
                            }
                        };
                        usersListPage.setAdapter(adapter);
                        adapter.startListening();
                    }
                }
            }
        }
    });
}

setQuery 方法中,与其将集合引用作为查询,我从中创建了一个查询对象,然后修改了查询 query.whereIn(),该方法允许您检查文档的字段是否包含给定对象或列表中的任一对象。

我知道这里的代码非常混乱。

英文:

It perhaps seems a little redundant to be answering my own question, but this is mostly for anyone else that has trouble with this. Following @Doug Stevenson's suggestion, I started trying to make my own custom recycler adapters and options class. However, I realized that the queries for the options could be modified. So basically, the solution is this:

Query query = database.collection(&quot;Users&quot;);
@Override
protected void onStart() {
super.onStart();
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference ref = FirebaseFirestore.getInstance().collection(&quot;Users&quot;).document(uid);
ref.get().addOnCompleteListener(new OnCompleteListener&lt;DocumentSnapshot&gt;() {
@Override
public void onComplete(@NonNull Task&lt;DocumentSnapshot&gt; task) {
if (task.isSuccessful()){
DocumentSnapshot document = task.getResult();
if (document.exists()) {
friends = (ArrayList&lt;String&gt;) document.get(&quot;friends&quot;);
if (friends.size() &gt; 0) {
FirestoreRecyclerOptions&lt;Users&gt; options = new FirestoreRecyclerOptions.Builder&lt;Users&gt;().setQuery(query.whereIn(&quot;user_id&quot;, friends), Users.class).build();
FirestoreRecyclerAdapter&lt;Users, UsersViewHolder&gt; adapter = new FirestoreRecyclerAdapter&lt;Users, UsersViewHolder&gt;(options) {
@Override
public UsersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_user_layout, parent, false);
return new UsersViewHolder(view);
}
@Override
protected void onBindViewHolder(@NonNull UsersViewHolder usersViewHolder, int i, @NonNull Users users) {
if (users != null) {
usersViewHolder.setName(users.name);
usersViewHolder.setStatus(users.status);
usersViewHolder.setImg(users.image);
final String userID = getSnapshots().getSnapshot(i).getId();
usersViewHolder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent profilePage = new Intent(FriendsList.this, ProfileActivity.class);
profilePage.putExtra(&quot;userID&quot;, userID);
startActivity(profilePage);
}
});
}
}
};
usersListPage.setAdapter(adapter);
adapter.startListening();
}
}
}
}
});
}

In the setQuery method, rather than using the collection reference as the query, I created a query object from it, and then modified the query query.whereIn(), which allows you to check to see if the field of a document contains the given object or one of the objects in a list.

My code here is very much a mess, I know.

huangapple
  • 本文由 发表于 2020年8月31日 06:52:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/63662866.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定