在 Android 上處理數據列表

本文檔介紹如何在 Firebase 中使用數據列表。要了解的讀取和寫入數據火力地堡的基礎看在Android上讀取和寫入數據

獲取數據庫參考

讀取和寫入數據從數據庫中,你需要的實例DatabaseReference

爪哇

private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();

科特林+KTX

private lateinit var database: DatabaseReference
// ...
database = Firebase.database.reference

讀取和寫入列表

附加到數據列表

使用push()方法,將數據追加到多用戶應用程序的列表。所述push()方法生成的唯一密鑰每一個新的子被添加到指定的火力地堡基準時間。通過為列表中的每個新元素使用這些自動生成的鍵,多個客戶端可以同時將子項添加到同一位置而不會發生寫入衝突。通過生成的唯一的密鑰push()是基於時間戳,所以列表項將自動按時間順序排列。

您可以使用參考由返回的新數據push()方法來獲得孩子的孩子的自動生成的密鑰組數據的價值。調用getKey()上的push()參考返回自動生成的關鍵字的值。

您可以使用這些自動生成的鍵來簡化數據結構的扁平化。有關詳細信息,請參閱數據扇出示例

監聽子事件

使用列表時,您的應用程序應該偵聽子事件,而不是用於單個對象的值事件。

兒童事件響應碰巧從操作節點的兒童的具體操作引發的,如通過添加一個新的子push()方法或孩子通過更新updateChildren()方法。這些中的每一個都可以用於偵聽數據庫中特定節點的更改。

為了偵聽子事件DatabaseReference ,附上ChildEventListener

聽眾事件回調典型用法
ChildEventListener onChildAdded()檢索項目列表或監聽項目列表的添加。此回調為每個現有子項觸發一次,然後在每次將新子項添加到指定路徑時再次觸發。該DataSnapshot傳遞給聽者包含新的子數據。
onChildChanged()偵聽列表中項目的更改。每當修改子節點時都會觸發此事件,包括對子節點後代的任何修改。該DataSnapshot傳遞給事件偵聽器包含了孩子的更新數據。
onChildRemoved()偵聽從列表中刪除的項目。該DataSnapshot傳遞給事件回調包含了刪除的子數據。
onChildMoved()偵聽有序列表中項目順序的更改。每當觸發此事件onChildChanged()回調是導致重新排序孩子的更新觸發。它的使用將是有序的數據orderByChildorderByValue

例如,一個社交博客應用程序可能會結合使用這些方法來監控帖子評論中的活動,如下所示:

爪哇

ChildEventListener childEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

        // A new comment has been added, add it to the displayed list
        Comment comment = dataSnapshot.getValue(Comment.class);

        // ...
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        Comment newComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        Comment movedComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException());
        Toast.makeText(mContext, "Failed to load comments.",
                Toast.LENGTH_SHORT).show();
    }
};
databaseReference.addChildEventListener(childEventListener);

科特林+KTX

val childEventListener = object : ChildEventListener {
    override fun onChildAdded(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.key!!)

        // A new comment has been added, add it to the displayed list
        val comment = dataSnapshot.getValue<Comment>()

        // ...
    }

    override fun onChildChanged(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildChanged: ${dataSnapshot.key}")

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        val newComment = dataSnapshot.getValue<Comment>()
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildRemoved(dataSnapshot: DataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.key!!)

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildMoved(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.key!!)

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        val movedComment = dataSnapshot.getValue<Comment>()
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onCancelled(databaseError: DatabaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException())
        Toast.makeText(context, "Failed to load comments.",
                Toast.LENGTH_SHORT).show()
    }
}
databaseReference.addChildEventListener(childEventListener)

監聽值事件

同時使用ChildEventListener是讀取數據的列表的推薦的方法,在有些情況下附接ValueEventListener到列表參考是有用的。

附加ValueEventListener數據的列表將返回數據的整個列表作為一個單一的DataSnapshot ,然後你可以遍歷訪問個別兒童。

即使查詢只有一個匹配項,快照仍然是一個列表;它只包含一個項目。要訪問該項目,您需要遍歷結果:

爪哇

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

科特林+KTX

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

當你想取一個列表的所有兒童在一個單一的操作,而不是傾聽更多的這種模式可能是有用的onChildAdded事件。

分離監聽器

回調是通過調用去除removeEventListener()在你的火力地堡數據庫的參考方法。

如果偵聽器已多次添加到數據位置,則每個事件都會多次調用它,您必須將其分離相同的次數才能將其完全刪除。

調用removeEventListener()對父母聽眾不會自動刪除其子結點註冊的偵聽器; removeEventListener()也必須在任何一個孩子叫聽眾刪除回調。

排序和過濾數據

您可以使用實時數據庫Query類檢索的關鍵排序的數據,按值,或者由孩子的價值。您還可以將排序結果過濾為特定數量的結果或一系列鍵或值。

排序數據

要檢索已排序的數據,首先指定一種 order-by 方法來確定結果的排序方式:

方法用法
orderByChild()按指定子鍵或嵌套子路徑的值對結果進行排序。
orderByKey()按子鍵排序結果。
orderByValue()按子值對結果進行排序。

您只能同時使用一個次序的方法。在同一查詢中多次調用 order-by 方法會引發錯誤。

以下示例演示瞭如何檢索用戶按星數排序的熱門帖子列表:

爪哇

// My top posts by number of stars
String myUserId = getUid();
Query myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
        .orderByChild("starCount");
myTopPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

科特林+KTX

// My top posts by number of stars
val myUserId = uid
val myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
    .orderByChild("starCount")

myTopPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

這定義了查詢,當與結合孩子聽眾同步客戶端提供基於他們的用戶ID的數據庫路徑,用戶的帖子,下令恆星每個崗位收到的數量。使用ID作為索引鍵的這種技術被稱為數據扇出,你可以閱讀更多關於它在構建數據庫

在調用orderByChild()方法指定的子鍵由訂購的結果。在這種情況下,職位是由各自的值進行排序的"starCount"的孩子。查詢也可以按嵌套子項排序,以防您的數據如下所示:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

在這個例子中,我們可以訂購我們通過在嵌套值列表元素metrics通過指定我們到嵌套子的相對路徑鍵orderByChild()調用。

爪哇

// Most viewed posts
Query myMostViewedPostsQuery = databaseReference.child("posts")
        .orderByChild("metrics/views");
myMostViewedPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

科特林+KTX

// Most viewed posts
val myMostViewedPostsQuery = databaseReference.child("posts")
        .orderByChild("metrics/views")
myMostViewedPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

有關其它數據類型如何排序的詳細信息,請參閱查詢數據是如何排序

過濾數據

要過濾數據,您可以在構建查詢時將任何限製或範圍方法與 order-by 方法結合使用。

方法用法
limitToFirst()設置從結果有序列表的開頭返回的最大項目數。
limitToLast()設置從結果有序列表的末尾返回的最大項目數。
startAt()根據選擇的排序方法返回大於或等於指定鍵或值的項目。
startAfter()根據選擇的排序方法返回大於指定鍵或值的項目。
endAt()根據所選的排序方式,返回小於或等於指定鍵或值的項目。
endBefore()根據選擇的排序方法,返回小於指定鍵或值的項目。
equalTo()根據選擇的排序方法返回等於指定鍵或值的項目。

與 order-by 方法不同,您可以組合多個限製或範圍函數。例如,你可以結合startAt()endAt()方法來限制的結果,值的指定範圍。

即使查詢只有一個匹配項,快照仍然是一個列表;它只包含一個項目。要訪問該項目,您需要遍歷結果:

爪哇

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

科特林+KTX

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

限制結果數量

您可以使用limitToFirst()limitToLast()方法來設置兒童被同步對於一個給定的回調的最大數量。例如,如果你使用limitToFirst()設置的100個限度,你最初只獲得高達100 onChildAdded()回調。如果您有少於100個項目存儲在您的火力地堡數據庫, onChildAdded()為每個項目回調火災。

由於項目變化,您會收到onChildAdded()為輸入查詢和物品回調onChildRemoved()回調起來那輟學如此項總數停留在100。

以下示例演示示例博客應用程序如何定義查詢以檢索所有用戶最近發布的 100 篇文章的列表:

爪哇

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
Query recentPostsQuery = databaseReference.child("posts")
        .limitToFirst(100);

科特林+KTX

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys.
databaseReference.child("posts").limitToFirst(100)

本實施例中僅定義了一個查詢,來實際同步數據它需要有一個附加的監聽器

按鍵或值過濾

您可以使用startAt() startAfter() endAt() endBefore()equalTo()選擇任意的開始,結束,以及查詢等當點。這對於分頁數據或查找具有特定值的子項的項目非常有用。

查詢數據的排序方式

本節介紹如何將數據由每個訂單由方法來分類的Query類。

orderByChild

當使用orderByChild()包含指定的子鍵如下排序的數據:

  1. 與兒童null的指定子鍵值是第一位的。
  2. 用價值孩子false的指定子鍵來下。如果有多個孩子的價值false ,它是按照字典序的關鍵。
  3. 用價值孩子true用於指定的子鍵來下。如果有多個孩子的價值true ,它們是由鍵按字典順序排序。
  4. 接下來是帶有數值的孩子,按升序排序。如果指定子節點的多個子節點具有相同的數值,則它們按關鍵字排序。
  5. 字符串在數字之後,按字典順序升序排列。如果指定子節點的多個子節點具有相同的值,則它們按字典順序按關鍵字排序。
  6. 對象排在最後,並按字典順序按鍵升序排序。

orderByKey

當使用orderByKey()對數據進行排序,數據在由鍵升序返回。

  1. 具有可以解析為 32 位整數的鍵的子項首先出現,按升序排序。
  2. 接下來是以字符串值作為鍵的子項,按字典順序升序排列。

orderByValue

當使用orderByValue()孩子們通過自己的價值排序。排序標準是一樣orderByChild()除了節點的值被用來代替一個指定的子密鑰的值。

下一步