如今,将应用程序的数据存储在云中非常重要,因为用户倾向于拥有多个设备,并希望其应用程序在所有设备之间保持同步。 使用Cloud Firestore (Firebase平台上可用的实时NoSQL数据库),这样做比以往任何时候都更加轻松,安全。
在较早的教程中 ,我向您介绍了Cloud Firestore必须提供的所有强大功能。 今天,我将向您展示如何与其他Firebase产品(例如FirebaseUI Auth和Firebase Analytics)一起使用它,以创建一个简单但可高度扩展的体重跟踪器应用程序。
先决条件
要遵循此分步教程,您需要:
1.项目设置
为了能够在Android Studio项目中使用Firebase产品,您将需要Google Services Gradle插件,Firebase配置文件以及一些implementation依赖项。 使用Firebase Assistant,您可以非常轻松地将它们全部获取。
转到工具> Firebase,打开助手。 接下来,选择“ 分析”选项,然后单击“ 记录分析事件”链接。

现在,您可以按Connect to Firebase按钮将您的Android Studio项目连接到新的Firebase项目。

但是,要实际添加插件和implementation依赖关系,您还需要按“ 向应用程序添加分析”按钮。
我们今天创建的体重跟踪器应用程序将仅具有两个功能:存储体重并将其显示为按时间倒序排序的列表。 当然,我们将使用Firestore存储权重。 为了将它们显示为列表,我们将使用FirebaseUI库中可用的与Firestore相关的组件。 因此,将以下implementation依赖项添加到app模块的build.gradle文件中:
implementation 'com.firebaseui:firebase-ui-firestore:3.2.2'
用户必须只能查看自己的体重,而不能查看使用该应用程序的每个人的体重。 因此,我们的应用程序需要具有唯一标识其用户的能力。 FirebaseUI Auth提供了此功能,因此接下来添加以下依赖项:
implementation 'com.firebaseui:firebase-ui-auth:3.2.2'
我们还将需要一些Material Design小部件,以使我们的应用程序具有令人满意的外观。 因此,请确保将“设计支持”库和“ 材质对话框”库添加为依赖项。
implementation 'com.android.support:design:26.1.0'
implementation 'com.afollestad.material-dialogs:core:0.9.6.0'
最后,按立即同步按钮以更新项目。
2.配置Firebase身份验证
Firebase身份验证支持各种身份提供程序。 但是,所有这些默认情况下都是禁用的。 要启用其中一个或多个,您必须访问Firebase 控制台 。
在控制台中,选择在上一步中创建的Firebase项目,转到其“ 身份验证”部分,然后按“ 设置登录方法”按钮。

要允许用户使用Google帐户登录到我们的应用,请启用Google作为提供者,为项目指定一个有意义的面向公众的名称,然后按保存按钮。

Google是您可以使用的最简单的身份提供者。 它不需要配置,您的Android Studio项目将不需要任何其他依赖项。
3.配置Cloud Firestore
在开始使用Firestore之前,必须先在Firebase控制台中启用它。 为此,请转到“ 数据库”部分,然后按Cloud Firestore Beta卡中的“入门”按钮。

现在将提示您选择数据库的安全模式。 确保选择“ 以锁定模式启动”选项,然后按“ 启用”按钮。

默认情况下,在锁定模式下,任何人都无法访问或修改数据库的内容。 因此,您现在必须创建一个安全规则,该规则允许用户仅读写属于他们的那些文档。 首先打开“ 规则”标签。
在为数据库创建安全规则之前,我们必须最终确定如何在数据库中存储数据。 假设我们将有一个名为users的顶级集合,其中包含代表我们用户的文档。 这些文档可以具有与Firebase身份验证服务为用户生成的ID相同的唯一ID。
因为用户将在其文档中添加几个权重条目,所以使用子集合存储这些条目是理想的。 我们称子集合weights 。

基于上述架构,我们现在可以为路径users/{user_id}/weights/{weight}创建一个规则。 规则是,仅当{user_id}变量等于用户的Firebase身份验证ID时,才允许用户读取和写入路径。
因此,更新规则编辑器的内容。
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user_id}/weights/{weight} {
allow read, write: if user_id == request.auth.uid;
最后,按发布按钮以激活规则。
4.验证用户
仅当用户使用Google帐户登录时,我们的应用才可以使用。 因此,打开后,它必须立即检查用户是否具有有效的Firebase身份验证ID。 如果用户确实具有该ID,则应该继续并呈现用户界面。 否则,它应该显示一个登录屏幕。
要检查用户是否具有ID,我们只需检查FirebaseAuth类的currentUser属性是否不为null。 如果为null,则可以通过调用AuthUI类的createSignInIntentBuilder()方法来创建登录意图。
以下代码向您展示了如何将Google作为身份提供者:
if(FirebaseAuth.getInstance().currentUser == null) {
AuthUI.getInstance().createSignInIntentBuilder()
.setAvailableProviders(arrayListOf(
AuthUI.IdpConfig.GoogleBuilder().build()
请注意,如果有效的ID已经存在,我们将调用名为showUI()的方法。 此方法尚不存在,因此请创建它,并暂时使其主体保持空白。
为了捕获登录意图的结果,我们必须重写活动的onActivityResult()方法。 在该方法内部,如果resultCode参数的值为RESULT_OK并且currentUser属性不再为null,则意味着用户已成功登录。 在这种情况下,我们必须再次调用showUI()方法来呈现用户界面。
如果用户登录失败,我们可以显示修饰语并通过调用finish()方法关闭应用程序。
因此,将以下代码添加到活动中:
override fun onActivityResult(requestCode: Int, resultCode: Int,
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK
&& FirebaseAuth.getInstance().currentUser != null) {
// Successfully signed in
Toast.makeText(this, "You must sign in to continue",
Toast.LENGTH_LONG).show()
此时,如果您是首次运行该应用程序,则应该能够看到如下所示的登录屏幕:

在后续运行中(由于默认启用了Google Smart Lock),您将自动登录。
5.定义布局
我们的应用程序需要两种布局:一种用于主要活动,另一种用于重量条目,这些条目将显示为按时间倒序排列的列表项。
主要活动的布局必须具有RecyclerView小部件(将用作列表)和FloatingActionButton小部件,用户可以按该小部件来创建新的重量条目。 将它们都放置在RelativeLayout小部件中后,活动的布局XML文件应如下所示:
<?xml version="1.0" encoding="utf-8"?>
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tutsplus.weighttracker.MainActivity">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/weights">
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_margin="16dp"
android:src="@drawable/ic_add_black_24dp"
android:tint="@android:color/white"
android:onClick="addWeight"/>
我们已经将名为addWeight()的单击事件处理程序与FloatingActionButton小部件相关联。 该处理程序尚不存在,因此请在活动中为其创建一个存根。
为了使重量条目的布局简单,我们将在其中仅包含两个TextView小部件:一个用于显示重量,另一个用于显示创建条目的时间。 使用LinearLayout小部件作为它们的容器就足够了。
因此,创建一个名为weight_entry.xml的新布局XML文件,并向其中添加以下代码:
<?xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Large"
android:id="@+id/weight_view"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Small"
android:id="@+id/time_view"/>
6.创建模型
在上一步中,您看到了每个重量条目都有一个关联的重量和时间。 要让Firestore知道这一点,我们必须为重量条目创建一个模型。
Firestore模型通常是具有所需成员变量的简单数据类。
data class WeightEntry(var weight: Double=0.0,
现在也是为每个重量条目创建视图支架的好时机。 正如您可能已经猜到的那样, RecyclerView小部件将使用视图保持器来呈现列表项。 因此,创建一个名为WeightEntryVH的新类,该类扩展了RecyclerView.ViewHolder类,并为两个TextView小部件创建了成员变量。 不要忘记使用findViewById()方法初始化它们。 以下代码向您展示了如何简洁地执行操作:
class WeightEntryVH(itemView: View?)
: RecyclerView.ViewHolder(itemView) {
var weightView: TextView? =
itemView?.findViewById(R.id.weight_view)
var timeView: TextView? =
itemView?.findViewById(R.id.time_view)
7.创建唯一的用户文档
当用户首次尝试创建权重条目时,我们的应用必须在Firestore上的users集合内为该users创建一个单独的文档。 正如我们之前所决定的那样,文档的ID就是用户的Firebase身份验证ID,可以使用currentUser对象的uid属性获得。
要获得对users集合的引用,我们必须使用FirebaseFirestore类的collection()方法。 然后,我们可以调用其document()方法并将uid作为参数传递,以创建用户的文档。
在读取和创建重量条目时,我们将需要访问用户特定的文档。 为避免对上述逻辑进行两次编码,建议您为其创建一个单独的方法。
private fun getUserDocument():DocumentReference {
val db = FirebaseFirestore.getInstance()
val users = db.collection("users")
val uid = FirebaseAuth.getInstance().currentUser!!.uid
return users.document(uid)
请注意,每个用户只能创建一次文档。 换句话说,只要用户使用相同的Google帐户,多次调用上述方法将始终返回同一文档。
8.添加体重条目
当用户按下我们应用程序的浮动操作按钮时,他们必须能够创建新的体重条目。 为了允许他们输入权重,让我们现在创建一个包含EditText小部件的对话框。 使用“材质对话框”库,这样做非常直观。
在用作按钮的单击事件处理程序的addWeight()方法内部,创建MaterialDialog.Builder实例,并调用其title()和content()方法为对话框提供标题和有意义的消息。 同样,调用inputType()方法并传递TYPE_CLASS_NUMBER作为其参数,以确保用户只能在对话框中键入数字。
接下来,调用input()方法以指定提示,并将事件处理程序与对话框相关联。 处理程序将接收用户输入的权重作为参数。
最后,请确保调用show()方法以显示对话框。
MaterialDialog.Builder(this)
.content("What's your weight today?")
.inputType(InputType.TYPE_CLASS_NUMBER
or InputType.TYPE_NUMBER_FLAG_DECIMAL)
.input("weight in pounds", "", false,
在事件处理程序内部,我们现在必须添加代码以实际创建和填充新的重量输入文档。 因为该文档必须属于用户唯一文档的weights集合,所以要访问该集合,必须调用getUserDocument()方法返回的文档的collection() getUserDocument()方法。
有了集合后,就可以调用其add()方法,并将WeightEntry类的新实例传递给它来存储条目。
weight.toString().toDouble(),
在上面的代码中,您可以看到我们正在使用Date类的time属性将Date戳记与条目相关联。
如果立即运行该应用程序,则应该可以将新的重量条目添加到Firestore。 您暂时不会在应用程序中看到它们,但是它们将在Firebase控制台中可见。

9.显示重量条目
现在是时候填充我们布局的RecyclerView小部件了。 因此,首先使用findViewById()方法为其创建引用,并为其分配LinearLayoutManager类的新实例。 这必须在我们之前创建的showUI()方法中完成。
val weightsView = findViewById<RecyclerView>(R.id.weights)
weightsView.layoutManager = LinearLayoutManager(this)
RecyclerView小部件必须显示用户文档的weights集合中存在的所有文档。 此外,最新文件应首先出现。 为了满足这些要求,我们现在必须通过调用collection()和orderBy()方法来创建查询。
为了提高效率,可以通过调用limit()方法来limit()查询返回的值的数量。
以下代码创建一个查询,该查询返回用户创建的最后90个重量条目:
val query = getUserDocument().collection("weights")
.orderBy("timestamp", Query.Direction.DESCENDING)
现在,使用该查询,我们必须创建一个FirestoreRecyclerOptions对象,稍后将使用该对象来配置RecyclerView小部件的适配器。 将query实例传递给其生成器的setQuery()方法时,请确保指定返回的结果为WeightEntry对象的形式。 以下代码显示了如何执行此操作:
val options = FirestoreRecyclerOptions.Builder<WeightEntry>()
.setQuery(query, WeightEntry::class.java)
您可能已经注意到,我们正在使当前活动成为FirestoreRecyclerOptions对象的生命周期所有者。 这样做很重要,因为我们希望适配器对常见的生命周期事件做出适当的响应,例如用户打开或关闭应用程序。
此时,我们可以创建一个FirestoreRecyclerAdapter对象,该对象使用FirestoreRecyclerOptions对象进行自我配置。 由于FirestoreRecyclerAdapter类是抽象类,因此Android Studio应该自动覆盖其方法来生成如下代码:
val adapter = object:FirestoreRecyclerAdapter<WeightEntry,
WeightEntryVH>(options) {
override fun onBindViewHolder(holder: WeightEntryVH,
position: Int, model: WeightEntry) {
override fun onCreateViewHolder(parent: ViewGroup?,
viewType: Int): WeightEntryVH {
如您所见, FirestoreRecyclerAdapter类与RecyclerView.Adapter类非常相似。 实际上,它是从它派生的。 这意味着您可以像使用RecyclerView.Adapter类一样使用它。
在onCreateViewHolder()方法内部,您所需要做的就是膨胀weight_entry.xml布局文件,并基于该文件返回一个WeightEntryVH视图持有者对象。
val layout = layoutInflater.inflate(R.layout.weight_entry, null)
return WeightEntryVH(layout)
在onBindViewHolder()方法内部,必须使用model参数更新视图持有人内部存在的TextView小部件的内容。
尽管更新weightView小部件很简单,但更新timeView小部件却有些复杂,因为我们不想直接向用户显示时间戳(以毫秒为单位)。
将时间戳转换为可读的日期和时间的最简单方法是使用DateUtils类的formatDateTime()方法。 除了时间戳之外,该方法还可以接受几个不同的标志,这些标志将用于格式化日期和时间。 您可以自由使用与您的首选项匹配的标志。
holder.weightView?.text = "${model.weight} lb"
val formattedDate = DateUtils.formatDateTime(applicationContext,
DateUtils.FORMAT_SHOW_DATE or
DateUtils.FORMAT_SHOW_TIME or
DateUtils.FORMAT_SHOW_YEAR)
holder.timeView?.text = "On $formattedDate"
最后,不要忘记将RecyclerView小部件指向我们刚刚创建的适配器。
weightsView.adapter = adapter
该应用已准备就绪。 现在,您应该能够添加新条目,并几乎立即看到它们出现在列表中。 如果您在具有相同Google帐户的另一台设备上运行该应用程序,则也会看到相同的重量条目。

结论
在本教程中,您了解了使用Cloud Firestore作为数据库为Android创建功能齐全的体重跟踪器应用程序的便捷性。 随时为其添加更多功能! 我也建议您尝试将其发布在Google Play上。 有了Firebase Spark计划,该计划目前免费提供1 GB的数据存储,因此为至少数千名用户提供服务不会有任何问题。
翻译自: https://code.tutsplus.com/tutorials/create-a-weight-tracker-app-with-cloud-firestore--cms-30772
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70000523/viewspace-3127057/
,如需转载,请注明出处,否则将追究法律责任。