在Kotlin中为Android自动保存草稿

Automatically saving content as it’s written is an essential part of the user experience. It’s used everywhere from Medium to Microsoft Word. Here’s how to implement this in Kotlin for Android.

在编写内容时自动保存内容是用户体验的重要组成部分。 从中型到Microsoft Word,无处不在。 这是在Android的Kotlin中实现此方法的方法。

计划 (The Plan)

To create a seamless user experience, we want to save content as user types. Rather than saving after every character that is typed, it would be best to wait for a pause in the typing and save each time the typing stops.

为了创建无缝的用户体验,我们希望将内容保存为用户类型。 与其等待每个键入的字符,不如保存它,最好是等待键入暂停并在每次键入停止时保存。

To do this we need three things. Firstly, a user content form. Secondly, a way of determining when the user is typing and thirdly a way of sending the content to our server for the draft to be stored in our database.

为此,我们需要三件事。 首先是用户内容表。 其次,一种确定用户何时键入的方法,其次是一种将内容发送到我们的服务器以将草稿存储在我们的数据库中的方法。

创建内容表单 (Creating the content form)

The styling of the content form is very much a design choice, but fundamentally it will be an EditText component. This component will allow the user to type long-form text. I’ve used the example below:

内容表单的样式很大程度上是一种设计选择,但从根本上讲,它将是一个EditText组件。 该组件将允许用户键入长格式文本。 我用下面的例子:

<EditText
android:id="@+id/edit_story"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginHorizontal="24dp"
android:background="@android:color/transparent"
android:layout_marginVertical="24dp"
android:hint="Start writing your story.."
android:inputType="textCapSentences|textAutoCorrect|textMultiLine"
app:fontFamily="@font/belgrano" />

创建我们的SaveDraft函数 (Creating our SaveDraft function)

To save the draft, we need to send the data to our server to be stored in a database table. To keep it simple, we will send the full content every time but if your users are writing particularly large documents then it may be beneficial to optimize this by only sending additions to the existing text.

要保存草稿,我们需要将数据发送到服务器以存储在数据库表中。 为简单起见,我们每次都会发送全部内容,但是如果您的用户正在编写特别大的文档,则最好仅向现有文本发送附加内容来优化此内容。

fun saveDraft(text: String){user?.getIdToken(true)
?.addOnCompleteListener { task ->if (task.isSuccessful) {
val idToken: String? = task.result?.tokenval uid = FirebaseAuth.getInstance().currentUser?.uidFuel.post("./saveDraft.php", listOf("uid" to uid, "idToken" to idToken, "creationDate" to creationDate, "text" to text, "title" to title)).responseString { request, response, result ->val (saveResult, error) = result
if (saveResult == "Success"){
Snackbar.make(findViewById<EditText>(R.id.edit_story), "Saved", Snackbar.LENGTH_LONG).show()
}
else
{
Log.e(TAG, "Failed to save user story.")
}}} else {
Log.e(TAG, "Failed to generate user token")
}}}

Here we are using FirebaseAuth for our authentication and sending the details to the server to be validated along with the user draft. We are also using Fuel to manage our HTTPS request to send the data to the server. We are sending 5 variables to the server:

在这里,我们使用FirebaseAuth进行身份验证,并将详细信息与用户草稿一起发送到要验证的服务器。 我们还使用Fuel来管理HTTPS请求,以将数据发送到服务器。 我们正在向服务器发送5个变量:

  • uid and idToken are authentication variables for FirebaseAuth

    uididToken是FirebaseAuth的身份验证变量

  • creationDate is used as an identifier for the draft so that a user can maintain multiple drafts at the same time. We can set this with val creationDate = LocalDateTime.now()

    creationDate用作草稿的标识符,以便用户可以同时维护多个草稿。 我们可以使用val creationDate = LocalDateTime.now()

  • text and title are the values for the content that we are saving.

    文字标题是我们要保存的内容的值。

如何检查用户是否已停止输入? (How to check if the user has stopped typing?)

To figure out when a pause occurs in the user’s typing, we need to flip this question around and detect when the user starts typing. We can then set a timer which will save our draft after a given time, say one second. If we detect that the user presses another key within that one second then we cancel our action to save the draft. This prevents sending data after every keystroke, which is wasteful but still saves the content after each flurry of activity.

为了弄清楚用户键入时何时出现暂停,我们需要翻转此问题并检测用户何时开始键入。 然后,我们可以设置一个计时器,该计时器将在给定的时间(例如一秒钟)后保存草稿。 如果我们检测到用户在那一秒钟内按下了另一个键,那么我们将取消保存草稿的操作。 这样可以防止在每次击键之后发送数据,这很浪费,但在每次忙碌的活动之后仍然保存内容。

To do this we need to create a Handler to run our SaveDraft function after a short delay. We can then initiate this every time the user presses a key and cancel it if another keystroke follows within this short time period.

为此,我们需要创建一个处理程序以在短暂的延迟后运行SaveDraft函数。 然后,我们可以在用户每次按下某个按键时启动此操作,如果在此短时间内又发生了另一次按键操作,则可以取消该操作。

val saveDraftHandler = Handler()

Once we have created this handler, we then need to detect user keystrokes and use this to initiate and cancel our handler.

创建此处理程序后,我们需要检测用户的击键并使用它来启动和取消我们的处理程序。

We do this using a TextWatcher component. This can be added to an EditText using a lambda function as below:

我们使用TextWatcher组件执行此操作。 可以使用lambda函数将其添加到EditText中,如下所示:

findViewById<EditText>(R.id.edit_story).addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
if (p0.toString() != "") {
findViewById<EditText>(R.id.edit_story).hint = ""
saveDraftHandler.removeCallbacksAndMessages(null);
saveDraftHandler.postDelayed({ saveDraft(p0.toString()) }, 1000)
}
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})

The TextWatcher object is created and added as a Listener for when the text changes within the EditText. We must override the three functions which comprise a TextWatcher but the main one we are interested in for now is afterTextChanged(). This is called after every keystroke the user makes within that text box.

当Text在EditText中更改时,将创建TextWatcher对象并将其添加为侦听器。 我们必须重写组成TextWatcher的三个函数,但是我们目前感兴趣的主要函数是afterTextChanged() 。 用户在该文本框中进行的每次击键后都会调用此方法。

Within this function we are using removeCallbacksAndMessages() to cancel the Handler to prevent it running the saveDraft activity if it is currently scheduled.

在此函数中,我们使用removeCallbacksAndMessages()取消处理程序,以防止其运行saveDraft活动(如果当前已计划)。

Then we are rescheduling the activity with postDelayed() to run the saveDraft activity after 1000ms.

然后,我们使用postDelayed()重新安排活动,以在1000ms之后运行saveDraft活动。

If the user is typing a stream of text then the handler keeps getting cancelled and rescheduled until the user eventually stops typing for more than a second, at which point the saveDraft activity is run and the file is saved.

如果用户正在键入文本流,则处理程序将不断取消和重新安排,直到用户最终停止键入一秒钟以上为止,此时将运行saveDraft活动并保存文件。

We have implemented the Runnable within the postDelayed() function as a lambda which in turn calls our saveDraft() function. We could have built the saveDraft() functionality directly into this lambda, however then we wouldn’t have been able to call saveDraft() in other contexts so we have decomposed these elements.

我们已在postDelayed()函数中将Runnable实现为lambda,而后者又调用了saveDraft()函数。 我们本可以直接在该lambda中内置saveDraft()功能,但是,由于无法在其他上下文中调用saveDraft() ,因此我们分解了这些元素。

结论 (Conclusion)

Creating an auto-saving form in Kotlin is very simple using a TextWatcher and the postDelayed functionality of a Handler. To improve this and make it more efficient, one could reduce the amount of data transmitted by checking if this is simply appending text to the previous draft.

使用TextWatcher和Handler的postDelayed功能,在Kotlin中创建自动保存的表单非常简单。 为了改善这一点并使其更有效,可以通过检查是否只是将文本附加到上一草案中来减少传输的数据量。

You could do this by storing lastDraft = p0.text and running the comparison if(lastDraft == p0.text.take(lastDraft.length) to check if the two start in the same way. This would give you something like the below:

您可以通过存储lastDraft = p0.text并运行比较if(lastDraft == p0.text.take(lastDraft.length)来检查两者是否以相同的方式启动来实现,这将为您提供以下内容:

if(lastDraft == text.take(lastDraft.length)){
val appendText = text.drop(lastDraft.length)
}

More advanced solutions are possible for optimising the data transfer, let me know if you come up with an interesting one.

可以使用更高级的解决方案来优化数据传输,如果您想出一个有趣的解决方案,请告诉我。

翻译自: https://medium.com/@mattgrint/auto-saving-drafts-in-kotlin-for-android-5d9b2a3d3bc1

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值