【问题标题】:Retrieving data from Firebase Realtime Database in Android从 Android 中的 Firebase 实时数据库中检索数据
【发布时间】:2016-09-27 02:05:10
【问题描述】:

我是 Firebase 和 NoSQL 的新手。我有一个 Android 演示,带有一个城市自动完成文本字段,我想在输入时从我的 Firebase 数据库中填充我拥有的城市。

{   "cities":{
        "Guayaquil":true,
        "Gualaceo":true,
        "Quito":true,
        "Quevedo":true,
        "Cuenca":true,
        "Loja":true,
        "Ibarra":true,
        "Manta":true
    }
}

这是我目前所拥有的。

如何从以字母开头的数据库城市中检索(从键盘输入)?如果我开始输入“G”,我希望收到“Guayaquil”和“Gualaceo”。

如果我使用 orderByValue 总是返回一个空快照。

如果我使用orderByKey,则返回整个列表。

    Query citiesQuery = databaseRef.child("cities").startAt(input).orderByValue();
    citiesQuery.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            List<String> cities = new ArrayList<String>();
            for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
                cities.add(postSnapshot.getValue().toString());
            }

注意:如果您能推荐更好的数据结构,欢迎您。

【问题讨论】:

  • 这个城市数据库有多大?您的应用程序是否可以将数据库一次读入本地副本(如某种数组),然后使用本地逻辑仅在键入时显示适当的值?
  • 最初大约是 10,但可以增加到 100。我希望这个功能是“在线”的,所以我可以添加新城市而无需在本地存储它
  • 哦,那很好。就处理器和网络而言,100个小字符串几乎没有,所以肯定会很快。让我澄清一下我将其存储在本地的意思。我的意思是在运行时内存中,仅此而已。我并不是说将副本存储在设备存储或类似的东西中。我的意思是,将城市读入一个数组,然后使用该数组来显示数据。当数据库更改时,您几乎可以立即轻松地更新数组,但我怀疑它会经常更改。查看我的答案,了解我在想什么。
  • 作为额外说明,在我的回答中,您可以从该事件侦听器中取出“单一”一词,这将使其成为实时。然后只需在循环之前添加 cityList.clear() ,然后每当您的数据库更改时,您的整个数组列表都会被刷新。

标签: android firebase firebase-realtime-database nosql


【解决方案1】:

@NicholasChen 已发现问题。但这是您使用 3.x SDK 实现的方式:

DatabaseReference cities = databaseRef.child("cities")
Query citiesQuery = cities.orderByKey().startAt(input).endAt(input+"\uf8ff");
citiesQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        List<String> cities = new ArrayList<String>();
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            cities.add(postSnapshot.getValue().toString());
        }

从用户输入开始,到以用户输入开头的最后一个字符串结束,您将获得所有匹配项

对于相对较短的项目列表,Ryan 的方法也可以正常工作。但是上面的 Firebase 查询会过滤服务器端。

更新

我刚刚运行了这段代码:

    DatabaseReference databaseRef = FirebaseDatabase.getInstance().getReference("39714936");

    String input = "G";

    DatabaseReference cities = databaseRef.child("cities");
    Query citiesQuery = cities.orderByKey().startAt(input).endAt(input + "\uf8ff");
    citiesQuery.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            List<String> cities = new ArrayList<String>();

            for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
                cities.add(postSnapshot.getValue().toString());
            }
            System.out.println(cities);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });

它打印出来了:

是的

是的

如此明显地匹配两个城市。

随意测试我的数据库:https://stackoverflow.firebaseio.com/39714936

【讨论】:

  • 它正在返回一个空快照
  • 我测试过,它对我有用。我在问题中添加了完整的 sn-p 和指向我的数据库的链接。
【解决方案2】:

尝试这样的方法来遍历 cities 快照中的孩子并将所有城市添加到 ArrayListArrayList 中。

ArrayList<String> cityList = new ArrayList<>();

databaseRef.child("cities").addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        cityList.clear();
        for (DataSnapshot data : dataSnapshot.getChildren()){
            cityList.add(data.getKey);
        }

    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.w(TAG, "getUser:onCancelled", databaseError.toException());
        // ...
    }
});

为清楚起见编辑本段:

这会将您的所有城市读入程序内存,以便您可以使用该数据向用户显示城市。如果城市列表发生变化,用户看到的数据也会发生变化。如果用户不在线,这将不起作用。这会在数据库上放置一个实时的、仅在线的侦听器。

我脑子里的逻辑是这样的:

  1. 在文本框上设置一个值监听器。
  2. 当用户键入时,使视图显示数组列表中的所有项目 以输入的相同子字符串开头。
  3. 当然要处理 arrayIndex 错误。

希望这会让您走上正轨。我相信还有其他方法可以实现它,但这是我个人会做的。如果您在显示正确城市的代码方面需要帮助,请与我聊天,我可以与您进行头脑风暴。

【讨论】:

  • 我明白你的意思,但我想保持在线“自动完成”功能,这样我就可以添加或删除城市,而无需在本地存储。
  • 它仍然会按照我的方式在线。无论您做什么,您都必须在某个时候将一些数据读入变量,否则您的应用程序将无法显示城市名称。它仍然支持在线删除,如果有人在另一个用户输入时删除了一个城市,它仍然会在输入时更新其他用户列表。我想我可能对听众的解释有误,或者我只是误解了要求。
  • 我猜你的方法是正确的。但我的想法是向 Firebase 发送城市的首字母,然后在快照中仅列出以这些字母开头的城市。
  • @ErikMedina 我认为 Ryan 已经给出了正确的解决方案。您只需将此数组连接到您的自动完成功能,当您在线添加数据时,它将在您的数组列表中简单更新。如果您不了解侦听器的概念,那么我建议您阅读 firebase 文档并尝试给出的示例以掌握基本知识。
  • 你可以这样做,但我个人认为添加事件监听器会更容易。尤其是这么小的名单。我在您的方法中看到的一个问题是轻微的网络故障。如果他们的联系参差不齐,则每种字母类型的所有呼叫都可能会很明显。仍然绝对可行。如果我现在不在我的小手机上打字,我会为你的方法想出一段代码,哈哈。我的手机无法处理。
【解决方案3】:

当然,OrderByValue 什么也不返回,因为那是您拥有的布尔值。

您可以使用 startAt 和 endAt 方法来执行此操作。 (以下是 Firebase 2.0 的代码)

var ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs");
ref.orderByKey().startAt("b").endAt("b\uf8ff").on("child_added", function(snapshot) {
  console.log(snapshot.key());
});

您可以在 Firebase 3 文档站点 here 上探索更多信息。

Ryan 的做法是正确的。但是,您必须在 dataSnapshot 上实现 startAt 以确保您的“实时”搜索有效。

【讨论】:

  • 这为什么会在 Android 中被标记??
  • 我不认为您将 startAt 与我的方法一起使用,因为我正在执行查询,我正在分配一个实时侦听器来侦听数据库的任何更改。
猜你喜欢
  • 1970-01-01
  • 2020-07-15
  • 2019-04-16
  • 2020-12-09
  • 1970-01-01
  • 1970-01-01
  • 2021-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多