博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何使您的Kotlin Android动画可访问
阅读量:2539 次
发布时间:2019-05-11

本文共 9321 字,大约阅读时间需要 31 分钟。

When researching examples for a first ever Android contribution, few examples existed for animations written in Kotlin. There were also few code examples of accessibility considerations within native animations.

在研究有史以来第一个Android贡献的示例时,很少有用Kotlin编写的动画示例。 在本地动画中,也很少有可访问性注意事项的代码示例。

So here we go! Let’s look at writing a native ‘expand’ animation in Kotlin, and talk about how to assist those with TalkBack or enlarged text turned on. All code is available in this , creating a single activity with an animated view within it. The code this is based on was co-written with .

所以我们开始! 让我们看一下用Kotlin编写本机的“扩展”动画,并讨论如何在启用“话语提示”或“放大文本”的情况下为用户提供帮助。 此提供了所有代码,可在其中创建带有动画视图的单个活动。 这个基于的代码是与共同编写的。

Android辅助功能(a11y) (Android accessibility (a11y))

All Android devices come with a built in screen reader named TalkBack. This can be turned on from the device’s settings, and also has a first time use guide built in. Gestures are used to navigate around the page, with descriptions of focused elements read aloud. Without this, an app becomes unusable for many visually impaired users.

所有Android设备均配有名为“话语提示”的内置屏幕阅读器。 可以从设备的设置中启用它,并且还内置有首次使用指南。手势用于在页面中导航,并大声读出重点内容的说明。 如果没有此功能,则对于许多视障用户来说,应用将无法使用。

Of key importance is that the correct elements are focusable, have descriptions, and changes to the view are announced.

至关重要的是,正确的元素应具有焦点,具有描述,并宣布对视图的更改。

Within the same settings menu the default base font size can be adjusted, scaling from 1.0. Views should react to this change in font size, with all elements still present and functioning.

在相同的设置菜单中,可以调整默认的基本字体大小,从1.0开始缩放。 视图应该对字体大小的这种变化做出React,所有元素仍然存在并起作用。

布局 (Layout)

We won’t look at the styling specifics of the layout here as they are fairly unique to this example, but the accessibility touches are worth highlighting.

我们不会在这里查看布局的样式细节,因为它们在此示例中是非常独特的,但是可访问性方面的内容值得强调。

Two properties are used: android:contentDescription and android:importantForAccessibility.

使用了两个属性: android:contentDescriptionandroid:importantForAccessibility

The contentDescription is what is read when an element gains focus. For any ImageView that gains focus this is essential, otherwise a screen reader will instead read the useless ‘unlabelled’ to the user.

contentDescription是当元素获得焦点时读取的内容。 对于任何获得焦点的ImageView来说,这都是至关重要的,否则屏幕阅读器将向用户读取无用的“未标记”。

If this was a button it would read ‘<description> button, double tap to activate’ by default, but for our ImageView icon we manually specify the action as we do not have this default.

如果这是一个按钮,则默认情况下将显示为“ <说明>按钮,双击以激活”,但是对于我们的ImageView图标,我们手动指定了操作,因为我们没有此默认设置。

android:contentDescription="tap to toggle extra person information"

We also use importantForAccessibility:no to turn OFF focus for the ‘+’ TextView, as the text beneath the two badges provides a description and so the ‘+’ is more confusing than helpful if read aloud.

我们还使用importantForAccessibility:no来关闭“ +” TextView的焦点,因为两个徽章下方的文本提供了描述,因此如果大声阅读,“ +”比起帮助更令人困惑。

For both of these, manual testing on a real device with TalkBack turned on is the best indication of whether the context makes sense without visuals.

对于这两种情况,在启用“话语提示”的情况下在真实设备上进行手动测试,可以最好地表明上下文在没有视觉效果的情况下是否有意义。

扩展动画 (Expand Animation)

Our animation will activate on an ‘info’ icon tap, toggling the expanding of a details section.

我们的动画将在“信息”图标点击时激活,切换细节部分的扩展。

We will do this all inside a single activity to allow focus simply on the animation code. In a real world app, the view this is applied to is more likely to be within its own fragment or recycler view, so a more abstracted code structure would be used.

我们将在一个活动中完成所有这些操作,以使焦点仅放在动画代码上。 在现实世界的应用程序中,应用于此视图的视图更有可能位于其自己的片段或回收器视图中,因此将使用更抽象的代码结构。

设置听众 (Setting a listener)

Within our example activity’s onCreate we must first set a listener on our icon and pass in the view that is to be toggled.

在示例活动的onCreate我们必须首先在图标上设置一个侦听器,然后传递要切换的视图。

infoIcon.setOnClickListener { toggleCardBody(root.personEntryBody) }

We also set up a variable within the class to track if the view is toggled, setting it to initially be closed.

我们还在类中设置了一个变量来跟踪视图是否被切换,将其设置为最初关闭。

private var isToggled = false

切换展开动画 (Toggle expand animation)

Within our layout, we have set the height of personEntryBody to 0dp.

在我们的布局中,我们将personEntryBody的高度设置为0dp

To toggle this open we need to know the new height to set it to, how long the animation should be, and what height it should be at each moment of the animation.

要切换此打开状态,我们需要知道将其设置为新的高度,动画应持续多长时间以及动画的每个瞬间应达到的高度。

We then need to set isToggled to its inverse, and ensure that when tapped again it does the reverse.

然后,我们需要将isToggled设置为它的反函数,并确保在再次轻击时将它反转。

private fun toggleCardBody(body: View) {    body.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)    val maxHeight = body.measuredHeight + body.paddingTop + body.paddingBottom    val startHeight = if (isToggled) maxHeight else 0    val targetHeight = if (isToggled) 0 else maxHeight    val expandAnimator = ValueAnimator        .ofInt(startHeight, targetHeight)        .setDuration(200)        expandAnimator.addUpdateListener {        val value = it.animatedValue as Int        body.layoutParams.height = value        body.requestLayout()    }    expandAnimator.doOnEnd {        isToggled = !isToggled    }    expandAnimator.start()}

As the height when the view is initially drawn is 0, we must calculate its new size by remeasuring its layout.

由于最初绘制视图时的高度为0,因此必须通过重新布局来计算其新大小。

As described in the , we can use measure() along with the layout params we assigned to the view to remeasure each time the info icon is tapped.

如 ,每次点击信息图标时,我们都可以使用measure()以及分配给视图的布局参数来重新测量。

To calculate the max height we must manually add the top and bottom padding to this, as these are not included in the measured height.

要计算最大高度,我们必须手动向其添加顶部和底部填充,因为这些高度不包括在所测量的高度中。

Depending on isToggled we then know if we are starting from 0 or starting from the expanded max height, and so the opposing target height.

然后根据isToggled知道是从0开始还是从扩展的最大高度开始,因此是相反的目标高度。

We use a Value Animator to move from the starting value to the target end value, and set the duration in ms. This duration is based purely on later manual testing for UX feel.

我们使用Value Animator从起始值移动到目标最终值,并以ms为单位设置持续时间。 此持续时间纯粹基于以后对UX感觉的手动测试。

ValueAnimator        .ofInt(startHeight, targetHeight)        .setDuration(200)

We tie the duration to the height with an update listener, requesting a new layout to be drawn after each update and adjusting the height each time.

我们使用更新监听器将持续时间与高度相关联,要求在每次更新后绘制新的布局并每次调整高度。

expandAnimator.addUpdateListener {        val value = it.animatedValue as Int        body.layoutParams.height = value        body.requestLayout()    }    expandAnimator.doOnEnd {        isToggled = !isToggled    }    expandAnimator.start()

As we are using Kotlin, we also add the library to our build.gradle to benefit from its doOnEnd extension. This allows us to very easily inverse the isToggled variable.

在使用Kotlin时,我们还将库添加到build.gradle以受益于其doOnEnd扩展。 这使我们可以非常轻松地将isToggled变量求逆。

Finally we start our animation! Already we have a body that expands and contracts on an icon touch!

最后,我们开始动画! 我们已经有了一个可以通过图标触摸扩展和收缩的物体!

更流畅的动画 (Smoother animations)

While our animation technically works as is, a nice extra step is to add an so that the movement feels more natural.

虽然我们的动画在技术上可以按原样工作,但是一个不错的额外步骤是添加一个 ,使运动感觉更自然。

expandAnimator.interpolator = FastOutSlowInInterpolator()

增加我们的可及性 (Increasing our accessibility)

We’ll add two final things to hopefully help our a11y users.

我们将添加最后两件事,希望对我们的a11y用户有所帮助。

First we can help with navigation using an .

首先,我们可以使用帮助导航。

expandAnimator.doOnEnd {    if (!isToggled)       body.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)    isToggled = !isToggled}

This means that when the animation moves from closed to open, the focus will immediately jump to the focus on the first item in the body, in this case the description. In the layout, we set the description of the info icon action, but as we may not be able to rely on a visual indicator for the user to move to the next item we can handle this for them.

这意味着,当动画从关闭移动到打开时,焦点将立即跳到主体中第一项(在本例中为描述)的焦点。 在布局中,我们设置了信息图标操作的描述,但是由于我们可能无法依靠视觉指示器让用户移至下一个项目,因此我们可以为他们处理。

Second we allow for different font sizes. The measured height returned from measure() does not account for font scaling set in the devices accessibility settings, and so when at a large scale the bottom of the description will be cropped as it is too large to fit.

其次,我们允许使用不同的字体大小。 从measure()返回的测量高度不考虑设备可访问性设置中设置的字体缩放比例,因此,如果缩放比例过大,说明的底部将被裁剪,因为它太大而无法容纳。

We can access the font scale programmatically, and scale our height based on this. We convert it to an integer as the font scale may result in a float which would not work as a layout height.

我们可以以编程方式访问字体比例,并据此缩放高度。 我们将其转换为整数,因为字体比例可能会导致浮点数,而浮点数不能用作布局高度。

val a11yFontScale = body.context.resources.configuration.fontScaleval maxHeight = ((body.measuredHeight + body.paddingTop + body.paddingBottom) * a11yFontScale).toInt()

完蛋了! (Finished!)

And there we have it, we have arrived at our final animation! With just a few extra lines we have greatly increased its a11y coverage and have a smooth expanding section revealing a Kotlin and Android badge ?

在这里,我们已经完成了最终的动画! 仅需几行,我们就大大提高了它的a11y覆盖率,并在平滑的区域显示了Kotlin和Android徽章?

Thanks for reading ?

谢谢阅读 ?

Here are a couple of other things I’ve written recently:

这是我最近写的其他几件事:

有用的附加功能 (Useful Extras)

  • ’s great androidx post on

    在上的精彩androidx帖子

翻译自:

转载地址:http://fvuzd.baihongyu.com/

你可能感兴趣的文章
HTTP协议
查看>>
CentOS7 重置root密码
查看>>
Centos安装Python3
查看>>
PHP批量插入
查看>>
laravel连接sql server 2008
查看>>
Laravel框架学习笔记之任务调度(定时任务)
查看>>
Laravel 的生命周期
查看>>
Nginx
查看>>
Navicat远程连接云主机数据库
查看>>
Nginx配置文件nginx.conf中文详解(总结)
查看>>
jxl写入excel实现数据导出功能
查看>>
linux文件目录类命令|--cp指令
查看>>
.net MVC 404错误解决方法
查看>>
linux系统目录结构
查看>>
学习进度
查看>>
使用Postmark测试后端存储性能
查看>>
NSTextView 文字链接的定制化
查看>>
第五天站立会议内容
查看>>
最短路径(SP)问题相关算法与模板
查看>>
js算法之最常用的排序
查看>>