本頁面由 Cloud Translation API 翻譯而成。
Switch to English

在Android上使用數據列表

本文檔介紹如何使用Firebase中的數據列表。要了解讀取和寫入Firebase數據的基礎知識,請參閱在Android上讀取和寫入數據

獲取數據庫參考

要從數據庫讀取和寫入數據,您需要一個DatabaseReference實例:

爪哇

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

Kotlin + KTX

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

讀寫列表

附加到數據列表

使用push()方法將數據追加到多用戶應用程序中的列表中。每當將新的子級添加到指定的Firebase引用中時, push()方法都會生成一個唯一鍵。通過為列表中的每個新元素使用這些自動生成的鍵,多個客戶端可以在同一時間將子代添加到同一位置,而不會發生寫衝突。 push()生成的唯一鍵基於時間戳,因此列表項將按時間順序自動排序。

您可以使用對push()方法返回的新數據的引用來獲取子項的自動生成鍵的值或為子項設置數據。在push()引用上調用getKey()返回自動生成的鍵的值。

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

聽兒童活動

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

子事件是根據某個操作(例如,通過push()方法添加的新子級或通過updateChildren()方法更新的子級push()對節點子級發生的特定操作的響應而觸發的。這些中的每一個都可以用於偵聽數據庫中特定節點的更改。

為了在DatabaseReference上偵聽子事件,請附加一個ChildEventListener

聽眾事件回調典型用法
ChildEventListener onChildAdded() 檢索項目列表或收聽對項目列表的添加。對於每個現有子項,將分別觸發一次此回調,然後在將新子項添加到指定路徑時,再次觸發此回調。傳遞給偵聽器的DataSnapshot包含新子級的數據。
onChildChanged() 偵聽列表中項目的更改。每當修改子節點(包括對子節點後代的任何修改)時,都會觸發此事件。傳遞給事件偵聽器的DataSnapshot包含子項的更新數據。
onChildRemoved() 偵聽從列表中刪除的項目。傳遞給事件回調的DataSnapshot包含已刪除子項的數據。
onChildMoved() 收聽對有序列表中項目順序的更改。每當導致子項重新排序的更新觸發onChildChanged()回調時, 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();
    }
};
ref.addChildEventListener(childEventListener);

Kotlin + 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());
        // ...
    }
});

Kotlin + 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事件時,此模式很有用。

分離聽眾

通過調用Firebase數據庫參考上的removeEventListener()方法來刪除回調。

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

在父偵聽removeEventListener()上調用removeEventListener()不會自動刪除在其子節點上註冊的偵聽器。還必須在任何子偵聽removeEventListener()上調用removeEventListener()才能刪除回調。

排序和過濾數據

您可以使用Realtime Database Query類檢索按鍵,值或子項值排序的數據。您還可以將排序的結果篩選為特定數目的結果或一定範圍的鍵或值。

排序數據

要檢索排序的數據,請先指定一種排序方法來確定結果的排序方式:

方法用法
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
    // ...
});

Kotlin + 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",
  }
},

在此示例中,我們可以通過在orderByChild()調用中指定嵌套子項的相對路徑,通過嵌套在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
    // ...
});

Kotlin + KTX

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

有關如何排序其他數據類型的更多信息,請參見查詢數據的排序方式

篩選資料

要過濾數據,可以在構造查詢時將任何limit或range方法與order-by方法結合使用。

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

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

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

爪哇

90

Kotlin + 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()方法來設置給定回調要同步的最大子limitToLast() 。例如,如果使用limitToFirst()將限制設置為100,則最初最多只能接收100個onChildAdded()回調。如果您在Firebase數據庫中存儲的項目少於100個, onChildAdded()為每個項目觸發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);

Kotlin + KTX

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

此示例僅定義一個查詢,以實際同步需要具有附加偵聽器的數據。

按鍵或值過濾

您可以使用startAt()endAt()equalTo()來選擇查詢的任意起點,終點和等價點。這對於對數據進行分頁或查找具有特定值的子項的項目很有用。

查詢數據如何排序

本節說明如何通過Query類中的每個order-by方法對數據進行排序。

orderByChild

使用orderByChild() ,包含指定子鍵的數據按以下順序排序:

  1. 指定的子項的值為null的子項優先。
  2. 接下來是指定子項的值為false的子項。如果多個子項的值為false則按字母順序對它們進行排序。
  3. 接下來是指定子項的值為true的子項。如果多個子項的值為true ,則按字母順序對它們進行排序。
  4. 接下來是帶有數字值的子級,以升序排列。如果指定的子節點的多個子節點具有相同的數值,則按鍵對它們進行排序。
  5. 字符串緊隨數字之後,並按字典順序升序排序。如果多個子節點在指定的子節點上具有相同的值,則按字母順序對它們進行排序。
  6. 對象排在最後,按字母順序從小到大按字母順序排序。

orderByKey

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

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

orderByValue

使用orderByValue() ,子項按其值排序。排序標準與orderByChild()相同,除了使用節點的值代替指定的子鍵的值之外。

下一步