前回の内容
【第1回】何人が何十回やってるか分からないが、それでも俺はFirebaseでチャットアプリを作りたい
今回の内容
第2回はチャット部屋一覧のPub/Subを行なっていきたいと思います。
出来上がりはこんな感じですね!左がPubして、右がSubしてます!
では早速実装をば!
部屋一覧のPub
やることは"rooms"という名前のCollectionを作って、そこにDocumentを追加するだけです!簡単ですね!
val name = editText.editable.toString()
val createTime = TimeStamp(Date())
val room = Room(name, createTime)
db.collection("rooms")
.add(room)
db.collenction("rooms")でroomsという名前のCollectionへの参照を取得して、ユーザーが入力した部屋名とTimeStampをRoomという名前のdataクラスに入れて、addしています。
TimeStampはDocumentのtimestampタイプと対応しています。
Firebaseコンソールにて、Firestore上で"rooms"Collectionにドキュメントが追加され、そのドキュメントの"name"フィールドに「Hello!」、"createTime"フィールドに日付と時刻が格納されていることが分かると思います。
部屋一覧のPubは以上です!
部屋一覧のSub
以下でQueryを取得します。
val query = db.collection("rooms")
.orderBy("createTime")
db.collection("rooms")で"rooms"Collectionへの参照を取得し、orderBy("createTime")でcreateTimeの昇順でソートしています。
abstract class FireStoreAdapter<VH : RecyclerView.ViewHolder>(var query: Query)
: RecyclerView.Adapter<VH>(), EventListener<QuerySnapshot> {
private var registration: ListenerRegistration? = null
private val snapshots: ArrayList<DocumentSnapshot> = ArrayList()
fun startListening() {
if (registration == null) {
registration = query.addSnapshotListener(this)
}
}
fun stopListening() {
registration?.let {
it.remove()
registration = null
}
snapshots.clear()
notifyDataSetChanged()
}
fun changeQuery(query: Query) {
stopListening()
snapshots.clear()
notifyDataSetChanged()
this.query = query
startListening()
}
override fun getItemCount() = snapshots.size
protected fun getSnapshot(index: Int): DocumentSnapshot {
return snapshots[index]
}
override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {
if (e != null || documentSnapshots == null) {
Log.w("error", "onEvent:error", e)
return
}
for (change in documentSnapshots.documentChanges) {
val snapShot = change.document
when (change.type) {
DocumentChange.Type.ADDED -> onDocumentAdded(change)
DocumentChange.Type.MODIFIED -> onDocumentModified(change)
DocumentChange.Type.REMOVED -> onDocumentRemoved(change)
}
}
}
protected fun onDocumentAdded(change: DocumentChange) {
snapshots.add(snapshots.size, change.document)
notifyItemInserted(snapshots.size)
}
protected fun onDocumentModified(change: DocumentChange) {
if (change.oldIndex == change.newIndex) {
snapshots.set(change.oldIndex, change.document)
notifyItemInserted(change.oldIndex)
} else {
snapshots.removeAt(change.oldIndex)
snapshots.add(change.newIndex, change.document)
notifyItemMoved(change.oldIndex, change.newIndex)
}
}
protected fun onDocumentRemoved(change: DocumentChange) {
snapshots.removeAt(change.oldIndex)
notifyItemRemoved(change.oldIndex)
}
}
見たことある人もいるかもしれませんが、これはCloud Firestore Android Codelabで登場するクラスです。
要所だけ説明すると、query.addSnapshotListener(this)で"rooms"テーブルをSubします。
"rooms"テーブルに変更があるとonEventが呼ばれます。引数のdocumentSnapshotsは追加/変更/削除されたDocumentのスナップショットです。
documentSnapshots.documentChangesで追加/変更/削除されたDocumentChangeのリストを取得します。DocumentChangeはDocumentの変更情報を持ったクラスです。変更内容によってAdapterへの処理を変えています。
FireStoreAdapterを継承したAdapterを作ります。
class Adapter(query: Query,
private val onClickItem: (room: Room?, documentId: String) -> Unit) : FireStoreAdapter<Adapter.ViewHolder>(query) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.item_room,
parent,
false)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getSnapshot(position))
}
inner class ViewHolder(val binding: ItemRoomBinding)
: RecyclerView.ViewHolder(binding.root) {
fun bind(snapShot: DocumentSnapshot) {
val room = snapShot.toObject(Room::class.java)
binding.room.text = room?.name
binding.room.setOnClickListener {
onClickItem(room, snapShot.id)
}
}
}
}
注目するところはViewHolder#bind()です。snapShot.toObject(Room::class.java)でRoomクラスのオブジェクトに変換しています。
部屋が選択されたら、RoomオブジェクトとDocumentIdをクロージャに渡します。
これで部屋一覧の実装は終わりです!
おわりに
第1回ではログイン機能を作ってユーザー情報をFirestoreに追加していき、今回で部屋一覧の監視と追加を実装しました。次回はチャットアプリの肝であるチャット機能を実装します。