android辅助开发
As developers, we make many assumptions about how users consume our application and that can actually hinder instead of help them. We need to ensure we’re keeping our flows as simple as possible and make everything accessible to every user in a way that makes sense to them, not us.
作为开发人员,我们对用户如何使用我们的应用程序做出许多假设,但这实际上可能阻碍而不是帮助他们。 我们需要确保我们的流程尽可能简单,并以对每个用户(而不是我们)有意义的方式使所有用户可以访问所有内容。
In this article, I want to share a few tips that really do make accessibility development less daunting. Apply some of these and you’re already on your way to an accessible Android application.
在本文中,我想分享一些确实使可访问性开发不那么艰巨的技巧。 应用其中的一些,您已经在使用可访问的Android应用程序了。
The more complicated the UX flows, the more we need to customise the accessibility UX flows alongside it. This doesn’t mean we can’t have complicated flows, only that we need to ensure we’re considering all of our users when developing them.
UX流程越复杂,我们需要定制的辅助流程就越多。 这并不意味着我们不能拥有复杂的流程,而只是需要确保在开发它们时考虑到所有用户。
提示1:使用本机组件 (Tip #1: Use Native Components)
You should use or extend native views as much as possible for the purpose they were intended.
您应该尽可能地使用或扩展本机视图。
The Android framework gives us a plethora of capabilities when it comes to Android components and does a good job of translating these components in an accessible way. The ecosystem relies on us developers to fill in the blanks to tell the OS what our application does, so everyone can have the most consistent experience possible on their device.
当涉及到Android组件时,Android框架为我们提供了许多功能,并且很好地完成了以可访问的方式转换这些组件的工作。 生态系统依靠我们的开发人员来填补空白,以告诉操作系统我们的应用程序做什么,因此每个人都可以在其设备上获得最一致的体验。
If you create a custom component that doesn’t extend from the intended view you need, then you lose those accessibility capabilities for that. Even using the wrong view for the wrong job can lose information for an accessibility user. We need to ensure we’re as informative as possible in a simple way.
如果您创建的自定义组件没有从所需的预期视图中扩展出来,那么您将失去这些辅助功能。 即使对错误的工作使用了错误的视图,也可能会丢失可访问性用户的信息。 我们需要确保我们以简单的方式提供尽可能多的信息。
Example:
范例 :
If we use a TextView
, apply a click listener to it to navigate somewhere else. A non-accessible user may not notice anything different. But for an accessibility user, we lose some key talkback capabilities that come with it.
如果我们使用TextView
,则对其应用点击侦听器以导航到其他地方。 不可访问的用户可能不会注意到任何不同。 但是对于可访问性用户而言,我们将失去一些重要的对讲功能。
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Terms and conditions"/>
If we have talkback enabled and navigate to this TextView
the user will hear:
如果启用了对讲功能并导航到此TextView
则用户将听到:
“Terms and conditions — Double-tap to activate
“条款和条件-点按两次即可激活
But if we use a Button
(which is the intended component here):
但是,如果我们使用Button
(此处是预期的组件):
<Button
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Terms and conditions"/>
“Terms and conditions button — Double-tap to activate”
“条款和条件按钮-点按两次即可激活”
These subtle changes make it consistent and provide more information for the accessibility users who are not only using your application, but other apps in the ecosystem.
这些细微的变化使其保持一致,并不仅为使用您的应用程序,而且还使用生态系统中其他应用程序的可访问性用户提供了更多信息。
提示2:避免将注意力集中在视图上 (Tip #2: Avoid forcing focus on views)
Focus should not be forced on any component for accessible users without a clear intent or interaction from them.
如果没有明确的意图或与他们的互动,则不应强加于可访问用户的任何组件上。
Accessibility users need to have a consistent flow when navigating; either touch by exploration, or via a switch device (as defined by WCAG 2.0 accessibility guidelines 3.2.1 and 3.2.3 respectively). If we decide to not only focus the OS accessibility delegate or any component without a clear interaction from the user, then it will create inconsistent behaviour for the user, it can also create bugs from the accessibility side because we’re overriding the OS’s accessibility event hierarchy;
辅助功能用户在导航时需要保持一致的流程; 通过探索或通过开关设备触摸(分别由WCAG 2.0可访问性准则3.2.1和3.2.3定义)。 如果我们决定不只关注操作系统可访问性委托或任何组件,而无需用户的明确交互,那么它将为用户造成不一致的行为,它还可以从可访问性方面创建错误,因为我们将覆盖操作系统的可访问性事件等级
Examples:
例子 :
<requestFocus/>
in your layout’s xml布局的xml中的
<requestFocus/>
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
android:accessibilityTraversalBefore
andandroid:accessibilityTraversalAfter
android:accessibilityTraversalBefore
和android:accessibilityTraversalAfter
If you still need to request focus for better user experience, you can query the AccessibilityManager
service to determine if a particular accessibility service is enabled and then only request focus when it isn’t.
如果仍然需要请求焦点以获得更好的用户体验,则可以查询AccessibilityManager
服务以确定是否启用了特定的辅助功能,然后仅在未启用时请求焦点。
Example:
范例 :
import android.accessibilityservice.AccessibilityServiceInfo
import android.view.accessibility.AccessibilityManager
class Accessibility(private val am: AccessibilityManager) {
fun isTalkbackEnabled() = am.isEnabled &&
am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN)
.isNotEmpty()
}
You can call it like this:
您可以这样称呼它:
private fun requestFocus(view: View) {
if (!accessibility.isTalkbackEnabled()) {
view.requestFocus()
}
}
If you want more control of focus when talkback is enabled take a look at android:ScreenReaderFocusable (which was introduced in API 28). If you are using AndroidX, you can also achieve this in a backwards compatible way (API 19 and above) like below:
如果您希望在启用对讲功能后对焦点进行更多控制,请查看android:ScreenReaderFocusable (在API 28中引入)。 如果您使用的是AndroidX,还可以通过向后兼容的方式(API 19及更高版本)实现此目的,如下所示:
fun View.screenReaderFocusable() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
isScreenReaderFocusable = true
} else {
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
host: View,
info: AccessibilityNodeInfoCompat
) {
info.isScreenReaderFocusable = true
super.onInitializeAccessibilityNodeInfo(host, info)
}
})
}
提示3:触摸目标尺寸 (Tip #3: Touch Target Size)
As a rule of thumb, all touchable components should be at least 48dp in height by 48dp in width so users with dexterity issues can navigate the application easier. You can achieve this in several ways:
根据经验,所有可触摸组件的高度至少应为48dp乘以48dp宽度,以便具有灵活性问题的用户可以更轻松地浏览应用程序。 您可以通过以下几种方法来实现:
Global
android:minWidth
andandroid:minWidth
styling for all separate touchable components using your application theme. Or, apply individual styles to your views in xml.使用您的应用程序主题为所有单独的可触摸组件设置全局
android:minWidth
和android:minWidth
样式。 或者,将个别样式应用于xml中的视图。- Apply padding to your components to make the touch area larger. 在您的组件上应用填充以使触摸区域更大。
Implement a TouchDelegate to expand the touch area to be larger than the bounds of the view.
实现一个TouchDelegate来扩展触摸区域,使其大于视图的边界。
提示4:视图对可访问性重要吗? (Tip #4: Is the view important for accessibility?)
Is the view important for a user who may be navigating with switch access or exploration by touch?
对于可能正在使用开关访问或通过触摸进行浏览的用户而言,视图是否重要?
When navigating through UI flows, we want the experience to be as seamless as possible. If we have components that purely exist to make the application user experience nicer, but doesn’t add too much value for accessibility users then utilise the importantForAccessibility attribute (API 16 or higher).
浏览UI流程时,我们希望体验尽可能无缝。 如果我们有一个纯粹的存在,使应用程序的用户体验更好,但不会增加太多价值的可访问性的用户,然后利用组件importantForAccessibility属性(API 16或更高)。
Note: For components that do not add too much value to accessibility, or exist purely for decoration, you can also set the contentDescription="@null"
which won’t hurt the navigation experience for switch access users.
注意 :对于那些不会给可访问性带来太多价值的组件,或者纯粹用于装饰的组件,您还可以设置contentDescription="@null"
,这不会损害切换访问用户的导航体验。
提示5:帮助对讲用户更快地导航 (Tip #5: Help talkback users navigate faster)
Talkback users can swipe up to change the controls for how screen readers navigates the content on the screen. The screen reader can be followed in multiple ways (Headings, Paragraphs, Lines, Words, Characters, Controls, Links). I’ll cover examples for headings and links below, you can find out about the rest here.
对讲用户可以向上滑动以更改屏幕阅读器如何浏览屏幕内容的控件。 屏幕阅读器可以多种方式使用(标题,段落,行,单词,字符,控件,链接)。 我将在下面介绍标题和链接的示例,您可以在此处找到其他内容。
标题 (Headings)
The Android OS doesn’t know too much about what a heading is in the context of your application, so you need to tell it. This is where the accessibilityHeading attribute comes in.
Android操作系统对应用程序上下文中的标题了解不多,因此您需要告诉它。 这是accessibilityHeading属性的来源。
For those targeting API 28 or above, you can simply add this to your view xml attribute as true. Otherwise, you can override the node information on a new AccessibilityDelegate
for the view you want to declare a header and set the isHeading
value true.
对于那些针对API 28或更高版本的用户,您可以将其作为true添加到视图xml属性中。 否则,您可以在要声明标题的视图的新AccessibilityDelegate
上覆盖节点信息,并将isHeading
值设置为true。
fun View.headingForAccessibility() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
isAccessibilityHeading = true
} else {
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
host: View,
info: AccessibilityNodeInfoCompat
) {
info.isHeading = true
super.onInitializeAccessibilityNodeInfo(host, info)
}
})
}
You can then use it like so:
然后可以像这样使用它:
headingTextView.headingForAccessibility()
This will now not only read out the text with “heading” at the end, but it’ll also allow the user to only gain focus on views which are defined as headings within the application to make navigation much faster when they have headings navigation control turned on.
现在,这不仅可以读出结尾处带有“标题”的文本,还可以使用户仅将注意力集中在应用程序中定义为标题的视图上,从而在拥有标题导航控件时可以更快地导航打开。
链接 (Links)
Links allows you to navigate URLSpan components or TextViews that have been filtered using Linkify (it’ll search the text and find a corresponding link). A link can be defined as a home address, phone number, email address or web URL.
链接允许您浏览使用Linkify过滤的URLSpan组件或TextView(它将搜索文本并找到相应的链接)。 链接可以定义为家庭住址,电话号码,电子邮件地址或Web URL。
By using the same principles mentioned above around native components — we can create a simple custom TextView
that will not just set a minWidth
and minHeight
to the view (now that we know it’s a touchable component) it’ll also filter out the text to find a link and apply it to the text.
通过围绕本机组件使用上述相同的原理,我们可以创建一个简单的自定义TextView
,不仅将view设置为minWidth
和minHeight
(现在我们知道它是可触摸的组件),还将过滤出文本以查找链接并将其应用于文本。
Example:
范例 :
class LinkTextView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : AppCompatTextView(context, attrs, defStyle) {
init {
minWidth = context.resources.getDimension(R.dimen.min_touchable_width).toInt()
minHeight = context.resources.getDimension(R.dimen.min_touchable_height).toInt()
Linkify.addLinks(this, Linkify.ALL)
}
}
You would implement it like so:
您可以这样实现它:
<com.bradley.wilson.accessibility.views.LinkTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Go to www.google.com" />
www.google.com will be hyperlinked. Now when you turn talkback on, swipe up the navigation to links. You can navigate all links on the screen without navigating through every component to find it.
www.google.com将超链接。 现在,当您打开对讲功能时,向上滑动导航至链接。 您可以浏览屏幕上的所有链接,而无需浏览每个组件以找到它。
Other key navigation techniques such as controls rely on finding Android components (checkboxes, radio buttons, switches, sliders (seek controls), text fields, and buttons) to navigate to, so if you’re using native components or custom components that extend the correct native component, the OS should do all the work for you.
其他关键导航技术(例如控件)依赖于查找Android组件(复选框,单选按钮,开关,滑块(搜索控件),文本字段和按钮)进行导航,因此,如果您使用的是本机组件或扩展了正确的本机组件,操作系统应为您完成所有工作。
The rest is simply text navigation, as long as you have text to consume on the screen the OS will take care of the rest.
其余的只是文本导航,只要您有要在屏幕上使用的文本,操作系统就会处理其余的内容。
提示6:有关屏幕内容的信息 (Tip #6: Information about screen content)
Also introduced in API 28 was the ability to set titles to particular areas of a screen, these are referred to as panes. They can refer to a ViewGroup
or Fragment
content.
API 28中还引入了将标题设置为屏幕特定区域的功能,这些区域称为“ 窗格” 。 他们可以引用ViewGroup
或Fragment
内容。
fun View.paneTitleForAccessibility(title: String) =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
accessibilityPaneTitle = title
} else {
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
host: View,
info: AccessibilityNodeInfoCompat
) {
info.paneTitle = title
super.onInitializeAccessibilityNodeInfo(host, info)
}
})
}
It’d look something like this:
看起来像这样:
paneView.paneTitleForAccessibility(getString(R.string.pain_title))
Note: As accessibility is user facing, always use strings from the strings.xml so you can take advantage of localisation.
注意 :由于可访问性是面向用户的,因此请始终使用strings.xml中的字符串,以便您可以利用localization 。
提示7:测试很容易! (Tip #7: Testing can be easy!)
Analysis tools
分析工具
Testing from within our development team (outside of actually turning on the accessibility services ourselves) we can utilise some key tools introduced by the Android accessibility team to make our lives easier:
通过我们的开发团队进行的测试(除了实际自己打开辅助功能之外),我们可以利用Android辅助功能团队引入的一些关键工具来简化我们的生活:
Pre launch report on the Google Play console. This tool takes advantage of firebase labs to run tests on the app and then generates a report which includes accessibility issues found in the scanned areas of your application. You can find more information here.
Google Play控制台上的启动前报告。 该工具利用Firebase实验室对应用程序进行测试,然后生成报告,其中包括在应用程序扫描区域中发现的可访问性问题。 您可以在此处找到更多信息。
UI testing — The team over at Android Accessibility Test Framework have integrated their tools into Roboelectric and Espresso. You can find an example of Espresso UI testing in the playground project.
UI测试-Android Accessibility Test Framework的团队已将其工具集成到Roboelectric和Espresso中。 您可以在游乐场项目中找到Espresso UI测试的示例 。
Accessibility Scanner app — They have also made it super easy for non-technical testing to take place to verify whether your application is accessible or not by using a pre-existing application that is also built around the Android Accessibility Test Framework. Simply download the app, enable the accessibility scanner in your device accessibility settings and then follow the options you can take in the app.
Accessibility Scanner应用程序 —通过使用同样基于Android Accessibility Test Framework构建的现有应用程序,它们还使非技术测试变得非常容易,以进行应用程序的可访问性验证。 只需下载该应用程序,在您的设备辅助功能设置中启用辅助功能扫描器,然后按照您可以在该应用程序中使用的选项进行操作即可。
Note: As the time of writing the Android Accessibility Test Framework migration to AndroidX (version 3.1) hasn’t been released. So I still utilise the support version of espresso-accessibility to have AccessibilityChecks
run in my UI tests.
注意 :在撰写本文时,尚未发布向AndroidX(版本3.1)迁移的Android Accessibility Test Framework。 因此,我仍然使用espresso-accessibility的支持版本在我的UI测试中运行AccessibilityChecks
。
Emulator
仿真器
Many developers won’t always be using a real device to test, If you’d like to access key accessibility features on an emulator, make sure you download the Android Accessibility Suite application onto your emulator and then go to the device settings to find the accessibility services you’d like to enable.
许多开发人员不会总是使用真实的设备进行测试。如果您想访问模拟器上的关键可访问性功能,请确保将Android Accessibility Suite应用程序下载到模拟器上,然后转到设备设置以查找您要启用的辅助功能。
It’s harder for someone to use an inaccessible app than it is to make it accessible.
对于某人来说,使用难以访问的应用程序要比使其易于访问更难。
结论 (Conclusion)
I hope you’ve learnt something new. I would really appreciate any feedback; if you have any better ways to attack a problem; please don’t hesitate to leave a comment, post a Github issue or raise a pull request to the playground project.
希望您学到了一些新知识。 我真的很感谢任何反馈; 如果您有更好的方法来解决问题; 请随时发表评论,发布Github问题或向游乐场项目提出拉动请求。
Keep an eye on my Medium/Github in the meantime — I’ll be taking a deeper dive into some of the more complicated problems (e.g. Virtual views, Gestures, Custom Views, Tabs, Advanced Lists etc..) for accessibility and to see how we can make your development life and user’s lives easier.
在此期间,请密切注意我的Medium / Github-我将更深入地研究一些更复杂的问题(例如,虚拟视图,手势,自定义视图,选项卡,高级列表等)。我们如何使您的开发生活和用户的生活更轻松。
Useful resources:
有用的资源:
https://developer.android.com/guide/topics/ui/accessibility
https://developer.android.com/guide/topics/ui/accessibility
https://material.io/design/usability/accessibility.html#understanding-accessibility
https://material.io/design/usability/accessibility.html#understanding-accessibility
翻译自: https://medium.com/swlh/android-accessibility-development-doesnt-have-to-be-scary-971cfe713a0e
android辅助开发