我的应用程序必须在ListView上显示名称列表。这些名称以以下方式存储在Cloud Firestore中:
集合:用户-文档:由用户UID整理-字段:名称(我必须注意,每个用户还有其他字段,但是我需要专门检索名称字段)
要完成此任务,我有第一个列表来检索所有文档或用户UID。然后,该第一个列表将在for循环中使用,以检索用户集合中每个用户的名称。
但是,由于Firebase异步检索数据,通常会丢失一些名称,并且最终它们会以混乱的方式显示(与从第一个列表传递uid的顺序不一致)。
如果有人可以让我对如何使Firebase等待数据被检索之前继续进行for循环有任何见解,将不胜感激!
下面是我的一些代码,可以使您更好地了解自己在做什么。
代码的第一部分,该代码成功检索了所有文档(uid)并将它们放在列表中
subTopicsDatabase.collection("schoolTopics").document(docKey).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()){
DocumentSnapshot document = task.getResult();
if (document.exists()) {
List<String> list = new ArrayList<>();
Map<String, Object> map = document.getData();
if (map != null) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
list.add(entry.getValue().toString());
}
}
}});
代码的第二部分,由于Firebase的异步行为而无法正常工作。
for (int i = 0; i<list.size(); i++) {
String uid = list.get(i);
Toast.makeText(TutorsListActivity.this, uid, Toast.LENGTH_LONG).show();
subTopicsDatabase.collection("users").document(uid).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
@Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()) {
String stName = documentSnapshot.getString("name");
ArrayAdapter<String> adapter = new ArrayAdapter<>(TutorsListActivity.this, R.layout.item_subtopic, testList);
adapter.notifyDataSetChanged();
sListView2.setAdapter(adapter);
}
}
});
}
答案 0 :(得分:2)
您断言由于Firebase的异步行为而不起作用的说法是错误的。视图未显示所需方式的原因是,每次从Firebase收到文档时,您都在更新适配器。
在伪代码中,这应该发生:
// Create function with completion block - i.e. fetchTopicNames
//
// Create array to hold fetched String values - i.e. topicNames
// For loop to request each document
// add String value to `topicNames`
// if current iteration is last iteration, finish forLoop and return topicNames
//
在另一种方法中,调用新创建的方法,并用topicNames
的完整列表更新适配器。然后,您还可以对Array
执行其他操作,例如过滤和排序。也许还有一种更有效的方法,我只是为您提供完成任务的最基本的方法。
答案 1 :(得分:2)
您需要存储元素,并且在for循环的最后中,您必须显示名称列表。
正如您所说的,您现在将获得所有UID的列表,现在您希望在列表中列出它们的名称。我已经更新了您的代码,使其正常工作。
// Create a Hashmap Object which has Key as UID and Name as Key
HashMap<String,String> hashMap = new HashMap<>();
for (int i = 0; i<list.size(); i++) {
final String uid = list.get(i);
Toast.makeText(TutorsListActivity.this, uid, Toast.LENGTH_LONG).show();
subTopicsDatabase.collection("users").document(uid).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
@Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()) {
//Store Your UID and Name in Hashmap
String stName = documentSnapshot.getString("name");
hashMap.put(uid,stName);
}
//Check if it is last index of array then show the names list
if(i==list.size()-1){
showListInAdapter(hashMap);
}
}
});
}
private void showListInAdapter(HashMap<String,String> hashMap) {
//now convert your hashmap into a list of name and get Your Names List and show in Adapter
ArrayList<String> listOfNames = new ArrayList<>(hashMap.keySet());
//Set list to Adapter
ArrayAdapter<String> adapter = new ArrayAdapter<>(TutorsListActivity.this, R.layout.item_subtopic, listOfNames);
sListView2.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
答案 2 :(得分:0)
我建议您遵循Guide to App Architecture并使用LiveData观察者来保持ListView的更新。您可以按照this tutorial进行操作,并将Firebase访问权限插入“存储库”类中。
更改代码以适应MVVM模式可能需要一些工作,但这也将使您的应用程序更好地运行并简化以后的开发。
答案 3 :(得分:0)
您可以通过进行递归来模拟同步获取用户(该函数将其称为self,直到索引变大,然后uid列表的大小)。
因此,首先要定义适配器和字符串列表(代表用户名)。当您这样做时,您可以调用递归,它将填充您的List和notifyDataSetChanged。这是示例
// Define empty list of user names, which you will populate later with recursion
List<String> userNames = new ArrayList<String>();
// Connect adapter with empty list
ArrayAdapter<String> adapter = new ArrayAdapter<>(TutorsListActivity.this, R.layout.item_subtopic, userNames);
// Set adapter to ListView
sListView2.setAdapter(adapter);
// Call recursion with list of uids and starting index of 0
getUserSync(list, 0);
private void getUserSync(List<String> list, int i) {
if (i < 0 || i > list.length - 1) {
// If index i is out of bounds for list, we break the recursion
return;
}
String uid = list.get(i);
Toast.makeText(TutorsListActivity.this, uid, Toast.LENGTH_LONG).show();
subTopicsDatabase.collection("users").document(uid).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
@Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
// When we load document, we fetch name and add it to the list which is connected to adapter
// After that, we call adapter.notifyDataSetChanged which will update ui
// When all that is done, we call getUserSync, to fetch user name for next uid
if (documentSnapshot.exists()) {
String stName = documentSnapshot.getString("name");
if (stName != null) {
userNames.add(stName);
adapter.notifyDataSetChanged();
}
}
getUserSync(list, i++);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// If enything goes wrong, we break the recursion
return;
}
});
}
如果有任何麻烦,请随时发表评论。.