android tts
Originally shared on my personal blog post.
最初在我的 个人博客文章 上分享 。
A great talk from the Google IO 2019 called “Demystifying Android Accessibility Development” mentions that when designing apps, we often miss to account for the users with accessibility needs. Users with accessibility needs won’t interact with the app directly, but instead they will use tools such as the Android Accessibility Suite (includes Talkback and Switch Access). The user will interact with the Accessibility service and then the service will interact with the app.
Google IO 2019的一篇精彩演讲称为“ 揭开Android可访问性开发的神秘面纱 ”,提到在设计应用程序时,我们经常会忽略考虑到具有可访问性需求的用户。 具有可访问性需求的用户不会直接与该应用进行交互,而是会使用诸如Android Accessibility Suite (包括对讲和切换访问)之类的工具。 用户将与辅助功能服务交互,然后该服务将与应用程序交互。
Accessibility services need information on what the screen has or shows to be able to provide the correct contextual information to the user or to be able to navigate through the app. An example of that information can be provided using Content Descriptions.
辅助功能服务需要有关屏幕上具有或显示的内容的信息,以便能够向用户提供正确的上下文信息或能够浏览应用程序。 可以使用“ 内容描述”提供该信息的示例。
In this blog post we’ll talk about Spans in Android and how to enrich Spannables to provide a better UX to users with accessibility needs.
在此博客文章中,我们将讨论Android中的Spans以及如何丰富Spannable s以便为具有可访问性需求的用户提供更好的UX。
Spans are powerful markup objects that you can use to style text at a character or paragraph level.
跨度是功能强大的标记对象,可用于在字符或段落级别设置文本样式。
With Spans we can change the text color of a substring or have a link-clickable part within a string, or even different size substrings. Sky is the limit 🚀
使用Spans,我们可以更改子字符串的文本颜色,或者在字符串中甚至具有不同大小的子字符串中具有可链接单击的部分。 天空是极限🚀
In this post, we’ll specifically talk about TtsSpan.
在本文中,我们将专门讨论TtsSpan 。
跨度 (TtsSpan)
A TtsSpan can provide metadata for a Spannable. The metadata will be supplied to Text-To-Speech Engines such as Talkback.
TtsSpan可以提供Spannable的元数据。 元数据将提供给文本语音转换引擎,例如Talkback 。
This span comes with several builders and each builder helps building metadata for a different type. The types supported by the builders are:
该范围包含多个构建器,每个构建器都可以帮助构建不同类型的元数据。 建设者支持的类型是:
To demonstrate their benefits, we’ll explore the following types:
为了展示它们的好处,我们将探索以下类型:
TtsSpan.TYPE_DATE
TtsSpan.TYPE_DATE
TtsSpan.TYPE_MEASURE
TtsSpan.TYPE_MEASURE
TtsSpan.TYPE_TIME
TtsSpan.TYPE_TIME
TtsSpan.TYPE_ELECTRONIC
TtsSpan.TYPE_ELECTRONIC
演示简介 (Brief introduction to demo)
The demo application has a list of items. Each item is duplicated, one without TtsSpan and one with TtsSpan to highlight the differences.
演示应用程序具有项目列表。 每一项都是重复的,一项没有TtsSpan,另一项有TtsSpan以突出显示差异。
When an item is clicked, we pass the Spannable (with or without TtsSpan) to the TextToSpeech service to output the metadata.
单击一个项目时,我们将Spannable(带有或不带有TtsSpan)传递给TextToSpeech服务以输出元数据。
验证 (Verification)
To verify that the metadata are supplied to the TextToSpeech engines correctly, we could do the following:
为了验证元数据是否正确提供给TextToSpeech引擎,我们可以执行以下操作:
Supply the spannables to the TextToSpeech.speak method which will output the data
将spannable提供给TextToSpeech。 会输出数据的语音方法
- Turn on Talkback and navigate the demo using the service 开启“话语提示”并使用该服务浏览演示
The demo videos use the first point + Live Caption to verify and present you the output.
演示视频使用第一点+ 实时字幕来验证并向您展示输出。
建立清单 (Building the list)
The list is built using RecyclerView with TtsItem
classes. Each item has a title, a caption and a nullable type of TtsSpan (if null then no TtsSpan is built).
该列表是使用RecyclerView和TtsItem
类构建的。 每个项目都有一个TtsSpan的标题 ,标题和可为null的类型(如果为null,则不构建任何TtsSpan)。
data class TtsItem(
val title: String,
val caption: String,
private val ttsSpanType: String?
) {
var id: Int = 0 fun toSpannable(): SpannableString? { ... }
}
To produce the different TtsItem, we have a data factory called DummyDataFactory
.
为了产生不同的TtsItem,我们有一个名为DummyDataFactory
的数据工厂。
object DummyDataFactory { fun getListOfTtsItem(): List<TtsItem> = listOf(
TtsItem("18/04/2020", "Date without TTSSpan", null),
TtsItem("18/04/2020", "Date with TtsSpan.DateBuilder", TtsSpan.TYPE_DATE), TtsItem("5 meter", "Measure without TTSSpan", null),
TtsItem("5 meter", "Measure with TTSSpan", TtsSpan.TYPE_MEASURE), TtsItem("14:00", "Time without TTSSpan", null),
TtsItem("14:00", "Time with TTSSpan", TtsSpan.TYPE_TIME), TtsItem("admin:123456789", "Password without TTSSpan", null),
TtsItem("admin:123456789", "Password with TTSSpan", TtsSpan.TYPE_ELECTRONIC)
).also { list ->
list.forEachIndexed { index, ttsItem -> ttsItem.id = index }
}
}
从TtsItem探索toSpannable() (Explore toSpannable() from TtsItem)
Disclaimer: Please note that some of the code shown below is for demonstration purposes only and mapping strings to TtsSpan most likely won’t work like that in real life projects.
免责声明 :请注意,下面显示的某些代码仅用于演示目的,并且将字符串映射到TtsSpan很有可能在现实项目中无法正常工作。
Note: Have a look at the captions to see the difference with and without TtsSpan.
注意:查看字幕以了解使用和不使用TtsSpan的区别。
TtsSpan.TYPE_DATE (TtsSpan.TYPE_DATE)
val calendar = Calendar.getInstance()
calendar.time = simpleDataFormat.parse(title)
?: throw IllegalStateException("Not expected null Date")TtsSpan.DateBuilder()
.setWeekday(calendar.get(Calendar.DAY_OF_WEEK))
.setDay(calendar.get(Calendar.DAY_OF_MONTH))
.setMonth(calendar.get(Calendar.MONTH))
.setYear(calendar.get(Calendar.YEAR))
The above code block will take a String date, parse it into a Date object which is then supplied to a Calendar. Then the Calendar object is used to extract different information that would be useful to TtsSpan.DateBuilder().
上面的代码块将使用String日期,将其解析为Date对象,然后将其提供给Calendar。 然后,将Calendar对象用于提取对TtsSpan.DateBuilder()有用的不同信息。
Caption without TtsSpan: “18 slash 04 slash 2020”
没有TtsSpan的标题: “ 18斜杠04斜杠2020 ”
Caption with TtsSpan: “Sunday the 18th of April 2020”
TtsSpan的标题: “ 2020年4月18日,星期日 ”
TtsSpan.TYPE_MEASURE (TtsSpan.TYPE_MEASURE)
val number = digitsPattern.find(title)?.value // extracts digits
val unit = stringPattern.find(title)?.value // extracts string
TtsSpan.MeasureBuilder()
.setNumber(number)
.setUnit(unit)
The above code block will extract the digits from the string which will be treated as the number and then extract the text from the string which will be treated as the Measurement unit. All the extracted data are supplied to the TtsSpan.MeasureBuilder.
上面的代码块将从字符串中提取数字,将其视为数字 ,然后从字符串中提取文本,将其视为度量单位。 所有提取的数据都将提供给TtsSpan.MeasureBuilder 。
Caption without TtsSpan: “5 metre”
没有TtsSpan的标题: “ 5米 ”
Caption with TtsSpan: “5 metres”
TtsSpan的标题: “ 5米 ”
As you can see the metadata helps identify whether the measurement is singular or plural.
如您所见,元数据有助于确定度量是单数还是复数。
TtsSpan.TYPE_TIME (TtsSpan.TYPE_TIME)
val hours = title.split(":")[0]
val minutes = title.split(":")[1]
TtsSpan.TimeBuilder()
.setHours(hours.toInt())
.setMinutes(minutes.toInt())
The above code block builds metadata needed for time. It simply extracts hours and minutes from string and supplies them to the TtsSpan.TimeBuilder().
上面的代码块构建了时间所需的元数据。 它只是从字符串中提取小时和分钟,并将它们提供给TtsSpan.TimeBuilder() 。
Caption without TtsSpan: “14 colon zero zero”
没有TtsSpan的标题: “ 14冒号零零 ”
Caption with TtsSpan: “14 hundred”
TtsSpan的标题: “ 14百 ”
TtsSpan.TYPE_ELECTRONIC (TtsSpan.TYPE_ELECTRONIC)
This particular type can be used to build several “electronic” metadata. In our example we’ll build metadata for a username and password.
此特定类型可用于构建多个“电子”元数据。 在我们的示例中,我们将为用户名和密码构建元数据。
val username = title.split(":")[0]
val password = title.split(":")[1]
TtsSpan.ElectronicBuilder()
.setPassword(password)
.setUsername(username)
The above code block uses the TtsSpan.ElectronicBuilder to build the metadata. The first part of the string is treated as the username and the second part as the password.
上面的代码块使用TtsSpan.ElectronicBuilder构建元数据。 字符串的第一部分被视为用户名,第二部分被视为密码。
Caption without TtsSpan: “admin 123 million 456 thousands 7 hundred and 89”
没有TtsSpan的标题: “ 管理员1.23亿456千7百89 ”
Caption with TtsSpan: “admin passoword 1 2 3 4 5 6 7 8 9”
带TtsSpan的标题: “ admin password 1 2 3 4 5 6 7 8 9 ”
The above example is my favourite as it demonstrates how powerful the Text-to-Speech engine can be with the correct metadata.
上面的示例是我的最爱,因为它演示了正确的元数据,文本到语音引擎的功能多么强大。
结论 (Conclusion)
Providing rich UX is important, and we need to make sure that our apps are accessible for all users. We have seen some examples on how to add some metadata in apps so that Text to Speech services provide contextual information.
提供丰富的UX很重要,我们需要确保所有用户都可以访问我们的应用程序。 我们已经看到了一些有关如何在应用程序中添加一些元数据的示例,以便“文本到语音”服务提供上下文信息。
➡ All the above examples can be found at the sample project on Github.
above以上所有示例都可以 在Github 上的 示例项目中 找到 。
Feel free to ping me on Twitter or check out my personal blog.
Till next time! 👋
直到下一次! 👋
推荐阅读和听力 (Recommended Reading & Listening)
翻译自: https://proandroiddev.com/lets-talk-tts-spans-in-android-accessibility-fc79c57754eb
android tts