当前位置: 首页 > 编程日记 > 正文

如何使您的Kotlin Android动画可访问

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 example repo, creating a single activity with an animated view within it. The code this is based on was co-written with Calum Turner.

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

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.paddingBottomval startHeight = if (isToggled) maxHeight else 0val targetHeight = if (isToggled) 0 else maxHeightval expandAnimator = ValueAnimator.ofInt(startHeight, targetHeight).setDuration(200)expandAnimator.addUpdateListener {val value = it.animatedValue as Intbody.layoutParams.height = valuebody.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 Android view layout docs, we can use measure() along with the layout params we assigned to the view to remeasure each time the info icon is tapped.

如Android视图布局文档中所述 ,每次点击信息图标时,我们都可以使用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 Intbody.layoutParams.height = valuebody.requestLayout()}expandAnimator.doOnEnd {isToggled = !isToggled}expandAnimator.start()

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

在使用Kotlin时,我们还将androidx库添加到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 interpolator 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 AccessibilityEvent .

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

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.fontScale
val 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:

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

  • Customising CodeceptJS E2E tests

    自定义CodeceptJS E2E测试

  • Testing React with Jest and Enzyme II

    用Jest和Enzyme II测试React

有用的附加功能 (Useful Extras)

  • Joe Birch’s great androidx post on Exploring KTX for Android

    Joe Birch在Exploring KTX for Android上的精彩androidx帖子

  • Android Accessibility Tutorial: Getting Started

    Android辅助功能指南:入门

翻译自: https://www.freecodecamp.org/news/accessible-a11y-kotlin-android-animations-7432bd23e395/

相关文章:

指针空间的申请与释放

一、malloc()和free()的基本概念以及基本用法&#xff1a; 1、函数原型及说明&#xff1a; void *malloc(long NumBytes)&#xff1a;该函数分配了NumBytes个字节&#xff0c;并返回了指向这块内存的指针。如果分配失败&#xff0c;则返回一个空指针&#xff08;NULL&#xff0…

UIGraphicsBeginImageContext - 位图上下文

UIGraphicsBeginImageContext 首先&#xff0c;先来认识一个UIGraphicsBeginImageContext&#xff0c;它会创建一个基于位图的上下文(context)&#xff08;默认创建一个透明的位图上下文&#xff09;,并将其设置为当前上下文。 位图图形上下文UIKit是不会负责创建的&#xff0c…

小程序双击事件

代码&#xff1a; <button data-time"{{lastTapTime}}" data-title"标题" bindtap"doubleClick">双击</button> js data: {lastTapTime:0,}, doubleClick: function (e) {var curTime e.timeStampvar lastTime e.currentTarget…

快速了解Kubernetes微服务中的通信

by Adam Henson亚当汉森(Adam Henson) 快速了解Kubernetes微服务中的通信 (A quick look at communication in Kubernetes microservices) “服务”概念和一个Node.js示例 (The “service” concept and a Node.js example) Based on complexity, a layer of microservices ca…

连接 linux服务器

操作步骤&#xff1a; xshell 下载 https://xshell.en.softonic.com/ 点击下载后&#xff0c;会有邮箱验证&#xff0c;点击验证通过就会自动下载&#xff0c;然后安装就行。 打开工具&#xff0c;点击新建会话 然后 浏览文件后直接点击确认&#xff0c;出来这样就登录成功了…

【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态点分治

题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏&#xff0c;本来这个游戏的地图其实还不算太大&#xff0c;幽香还能管得过来&#xff0c;但是不知道为什么现在的网游厂商把游戏的地图越做越大&#xff0c;以至于幽香一眼根本看不过来&#xff0c;更别说和别人打仗了。 …

面试题05-UI控件

怎么解决缓存池满的问题(cell)ios中不存在缓存池满的情况&#xff0c;因为通常我们ios中开发&#xff0c;对象都是在需要的时候才会创建&#xff0c;有种常用的说话叫做懒加载&#xff0c;还有在UITableView中一般只会创建刚开始出现在屏幕中的cell&#xff0c;之后都是从缓存池…

全球链界科技发展大会_如何成为科技界的团队合作者

全球链界科技发展大会by Ofer Vugman由Ofer Vugman 如何成为科技界的团队合作者 (How to be a team player in the tech world) 这些技巧将增进您的关系并提高团队的工作效率 (These tips will boost your relationships and your team’s efficiency at work) When I landed …

linux驱动之i2c子系统mpu6050设备驱动

以下是mpu6050简单的驱动实现&#xff0c;mpu6050是I2C接口的6轴传感器&#xff0c;可以作为字符设备注册到内核&#xff0c;本代码运行环境是3.4.2内核&#xff0c;4.3.2版本的编译链&#xff0c;12.04版本的Ubuntu&#xff0c;硬件环境是jz2440开发板&#xff1b; 按照之前分…

小程序使用富文本完整代码及示例图

先看示例图&#xff1a; 富文本html代码&#xff1a; 效果图&#xff1a; 实现步骤&#xff1a; 1.下载 wxParse代码放到你的小程序项目目录里面 https://github.com/icindy/wxParse 基本使用方法 Copy文件夹wxParse - wxParse/-wxParse.js(必须存在)-html2json.js(必须存在…

C# 百分比的获取

这里介绍 C# 百分比转换有2种方式 例&#xff1a; double a50; double b100; a/b.ToString("0.00%"); 或 a/b.ToString("P3"); p后的数字表示能显示小数点后几位的精度数 实际如&#xff1a; 方法一&#xff1a;a/b.ToString("0.00%"); 方法二&a…

css 网格布局_我从CSS网格布局中学到的东西

css 网格布局by Jennifer Wjertzoch珍妮弗维佐奇 我从CSS网格布局中学到的东西 (Things I’ve learned about CSS grid layout) With CSS Grid you can create complex web designs. It is very intuitive and very well supported by the major browsers. In this article I …

GoF23种设计模式之行为型模式之解释器模式

一、概述 给定一种语言和其文法的一种表示&#xff0c;再定义一个解释器&#xff0c;该解释器使用表示来解释语言中的句子。 二、适用性 当需要解释一种语言&#xff0c;并且可以将该语言中的句子表示为一个抽象语法树的时候。 1.该文法简单对于复杂的文法&…

U盘安装Ubuntu14.4时遇到分区问题记录

1、在安装Ubuntu14.4时&#xff0c;遇到如果先分出 / 跟挂载的主分区时&#xff0c;后面只能再分一个swap&#xff0c;或者挂载一个/home&#xff0c;或者一个/ boot 时不能继续分区&#xff0c;当然想安装也是不能不能成功的。 解决办法&#xff1a;在这里先不要创建 / 的主挂…

请求异步js,请求完成后执行代码

要确定请求完成 js 文件&#xff0c;才执行相关的代码。 场景&#xff0c;引用了百度地图的 js-sdk&#xff0c; 需要实例化 SDK 对象&#xff0c;但是这个引用加载JS-SDK文件其实是异步的&#xff0c;在没请求完成之前就实例化对象就会报错&#xff0c;提示找不到该对象。 解…

测试驱动开发 测试前移_测试驱动开发简介

测试驱动开发 测试前移I’ve been programming for five years and, honestly, I have avoided test-driven development. I haven’t avoided it because I didn’t think it was important. In fact, it seemed very important–but rather because I was too comfortable no…

BZOJ 2957楼房重建

传送门 线段树 //Twenty #include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<queue> #include<vector> #define lc x<<1 #define rc x<<1|…

AngularJs $cacheFactory 缓存服务

可能之前的api写的有些枯燥吧&#xff0c;因为不烧脑&#xff0c;不需要很多逻辑思维来做处理&#xff0c;那么之后的文章会有趣很多&#xff0c;慢慢的开始烧脑了&#xff0c;准备好大量脑细胞的死亡吧~ 先来篇简单的缓存服务。 本文将api文档里的$cacheFactory和 $cacheFac…

H5使用百度地图SDK获取用户当前位置并且标记显示在地图

代码实现功能&#xff1a; H5使用百度地图SDK获取用户当前位置并且标记显示在地图&#xff0c;点击该标记弹出一层自定义的HTML。 效果图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" conten…

clojurescript_为什么ClojureScript在NPM上如此出色

clojurescriptby Jacek Schae由Jacek Schae 为什么ClojureScript在NPM上如此出色 (Why ClojureScript works so well with NPM) Every language that complies/transpiles to JavaScript wants to connect to npm to use this huge ecosystem. The master of this is, of cour…

微信网页开发 jssdk前后端代码,PHP实现完整代码,自定义分享

如果你页面启用了https&#xff0c;务必引入 https&#x1f615;/res.wx.qq.com/open/js/jweixin-1.0.0.js &#xff0c;否则将无法在iOS9.0以上系统中成功使用JSSDK 微信网页JS-SDK的功能实现&#xff0c;后端php&#xff0c;完整源码。分享内容自定义。 jsapi_ticket &…

delphi自定义事件处理

http://www.cnblogs.com/ywangzi/archive/2012/09/06/2673414.html delphi自定义事件处理 为什么我们点击按钮&#xff0c;就会执行按钮的onclick事件&#xff1f;这个事件是怎么和我们自己的代码关联起来的。相信很多人都有这个疑问。那么我们就通过一个自定义事件来了解这里面…

textarea输入框限制字数(JS)

第一种: <textarea οnkeyup"checkLen(this)"></textarea> <div>您还可以输入 <span id"count">200</span> 个文字</div> <script type"text/javascript"> function checkLen(obj) { var maxCha…

域名管理系统 二级域名_域名系统简介

域名管理系统 二级域名by Sumedh Nimkarde由Sumedh Nimkarde 域名系统简介 (An introduction to the Domain Name System) You all might have heard about or know about the Domain Name System (DNS) if you understand how the internet works or how computer networks w…

redis面试总结

&#xff08;1&#xff09;什么是redis? Redis 是一个基于内存的高性能key-value数据库。 &#xff08;2&#xff09;Reids的特点 Redis本质上是一个Key-Value类型的内存数据库&#xff0c;很像memcached&#xff0c;整个数据库统统加载在内存当中进行操作&#xff0c;定期通…

struts2 实现自定义标签

/*** lostingz* Created on 2015年11月18日*/ package com.test.web.tags;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import org.apache.struts2.components.Component; import org.apache.struts2.views.jsp.ComponentTa…

php修改多个字段

1.直接修改 2.按条件修改 1.代码&#xff1a; <?phpheader("Content-Type:text/html;charsetutf8"); header("Access-Control-Allow-Origin: *"); //解决跨域header(Access-Control-Allow-Methods:GET);// 响应类型 header(Access-Control-Allow-H…

备忘录吕吕没有备忘录十新建_一份备忘单,可帮助您记住CSS自定义属性

备忘录吕吕没有备忘录十新建CSS custom properties, also known as CSS variables, represent custom properties that can be declared and be called in your CSS.CSS定制属性&#xff0c;也称为CSS变量&#xff0c;表示可以在CSS中声明和调用的定制属性。 在CSS中声明自定义…

Oracle数据库一些操作信息

Oracle数据库如何查看当前用户角色权限及默认表空间查看当前用户的一些信息&#xff0c;包括用户拥有的角色权限信息、用户表空间以及用户和默认表空间的关系等--查看用户的角色权限1、查看当前用户拥有的角色权限信息&#xff1a;select * from role_sys_privs; 2、查看所有用…

Eclipse用法和技巧十:显示代码outline

在一个文件中快速找到某一个方法或者某一个作用域&#xff0c;可以使用 CtrlO或者CtrlF3&#xff0c;快速显示当前代码的outline&#xff0c;进行快速查找。效果如下&#xff1a; 这里主要是补充一些后续操作&#xff0c;能更加方便的帮助我们查找。 步骤一&#xff1a;再…