
什麼是無障礙?
無障礙範圍很廣,一般是指在發展過程中沒有阻礙,活動能夠順利進行。比如給腿腳不便的人在一些公共場合比如火車站、機場、商場等地方設定無障礙電梯,無障礙廁所,或者給聽覺障礙的人提供助聽器等等。
換句話說:為失能人士提供與非失能人士同等機會。這裡所說的失能根據具體形式和嚴重程度各不相同,但主要可以分為四種:認知、視覺、聽覺,以及活動能力。
當然失能也分為兩種永久性失能和情境性失能。
永久性失能
視覺障礙、聽覺障礙、坐輪椅或者行動不便等肢體障礙。
情境性失能
- 開車的時候試圖用手機:汽車晃動導緻情境性的視覺障礙、肢體障礙、注意力障礙。
- 開會時聊天軟體發來語音:檢視消息發出聲音會影響周圍同僚(聊天軟體提供語音轉文字的功能便是無障礙的一種)。
- 出國旅遊語言不通:情境性的口頭溝通障礙。
- 買東西時拎着大包小包:情境性的肢體障礙。
無障礙的新定義:確定每個使用者意圖都被了解。
某種程度而言,資訊無障礙是智能産品互動設計中針對特殊人群的一個功能,它可以讓人們更加平等地享用産品在硬體和軟體上的各項功能。
DinamiX 對無障礙的支援
本文接下去講的 DinamicX 對無障礙的支援主要是資訊無障礙,一般是針對視覺障礙的人群。通過技術手段,幫助視障人群更好地感受世界的美好,讓使用者在使用 app 的時候能夠順暢的擷取資訊、利用資訊。
手淘基礎核心鍊路
手淘首頁、詳情、購物車、下單、訂單、訂單清單、我的淘寶都屬于手淘核心鍊路,目前上述頁面 UI 都是使用 DinamicX 作為渲染引擎來繪制。
DinamicX SDK 作為支撐手淘基礎核心鍊路重要的一環,對無障礙的支援責無旁貸。
關于 DinamicX
DinamicX 的定位是一個提供三端統一能力的用戶端動态化解決方案,為無線基礎鍊路上的高性能和高可用提供基礎保障。我們希望通過社群化營運不斷豐富 DinamicX 的能力和内容,提高渲染性能和穩定性,将 DinamicX 打造成一個集團内的用戶端動态化體系的标準化方案。
動态模闆解決方案核心技術:一個包含模闆的下載下傳、加載、解析、渲染的引擎,幫你動态生成 View。
DinamicX 對無障礙的支援主要分為兩部分:
- SDK 本身對無障礙的跨平台支援
- 模闆開發平台進行卡口校驗
作為一個跨平台統一的動态化解決方案,勢必要抹平端與端的差異,以及降低業務方(模闆開發者)想支援無障礙的認知成本,我們團隊全體成員包括 Android 開發、iOS 開發以及測試同學讨論了很久,確定兩端統一的情況下,勾勒出統一無障礙行為。
技術方案
系統原生的無障礙
iOS 系統原生的無障礙
原生 iOS 的幾種邏輯:
- View 設定了 isAccessibilityElement=YES,無論是否設定了 accesibilityLabel,所有它的子節點,都不可獲得焦點。
- UILabel 的 isAccessibilityElement 屬性預設是 NO,但隻要主動地設定過值,就算設定的是 NO,也無法在父容器下自動讀出。
- 如果需要父容器獲得焦點後自動讀取出裡面所有 UILabel 的文字,需要 isAccessibilityElement = NO,并且 accessibilityElementsHidden = NO。Label 的 isAccessibilityElement 必須保持原始預設值,不能設定任何值。
- 如果父節點嵌套,并且所有父節點的 accessibilityElement 都設定為 off,會自動将這個父節點所有的子節點的 TextView 的 accessibilityLabel 順序讀出,這意味着所有自動閱讀的文字最終都在根節點上被讀出。
系統無障礙 API:
Android 系統原生的無障礙
Android 的 View 無障礙狀态總共分為 3 種:
- 沒有無障礙資訊,如 ImageView、View 等預設就是沒有無障礙資訊。
- 有無障礙資訊,如 ImageView 設定 setContentDescription,或者 TextView 自帶無障礙資訊就是它本身的 text。
- 有無障礙資訊的可互動控件,比如 ImageView 設定 setContentDescription 的同時,又設定了 setOnClickListener, 比如 TextView 設定了setFocusable(true),或者 EditText、CheckBox 這種預設就是有無障礙資訊的可互動控件。
這 3 種無障礙狀态在它的父 layout 之中的關系:
DinamicX SDK 定義無障礙屬性
抹平兩端差異,簡化無障礙邏輯,DinamicX 提供了兩個無障礙屬性來支援無障礙功能。
xml 示例
如下表示該控件在觸摸到的時候,會被選中,且朗讀出“跳往詳情頁”的文案:
<ImageView
width="100"
height="100"
accessibility="on"
accessibilityText="跳往詳情頁"
onTap="@openUrl{'detail'}"
imageUrl="https://img.alicdn.com/tfs/TB1FuMQQFXXXXXLXXXXXXXXXXXX-420-420.jpg"
/>
統一兩端無障礙行為
下圖代表的是兩端目前統一行為,描述了 Layout 與子節點在無障礙屬性各種 value 值碰撞下的情況。
端上的處理
為達到上圖所展示的兩端一緻的行為,端上各自做了自己的處理。
iOS
下圖表示 SDK 根據模闆屬性到系統 API 的映射:
Android
下圖表示 SDK 根據模闆屬性到系統 API 的映射,Android 對 Layout 和非 Layout 的 View 需要差別對待。
Layout 節點對無障礙的處理:
非 Layout 節點對無障礙的處理:
案例示範
模闆示例
<LinearLayout
backgroundColor="#eeeeee"
height="match_content"
width="375"
orientation="vertical"
disableFlatten="true"
>
<LinearLayout
marginLeft="@triple{@data{cellType},20,50}"
backgroundColor="#f2f2f2"
height="match_content"
width="match_parent"
orientation="vertical"
disableFlatten="true"
accessibility="auto"
>
<!--auto代表點選的時候,該layout下面的text資訊都可以讀出來-->
<TextView
width="match_content"
height="match_content"
textColor="#ff051b28"
textSize="12"
marginTop="20"
marginBottom="20"
text="這是一個textView"
/>
<TextView
width="match_content"
height="match_content"
textColor="#ff051b28"
textSize="12"
marginTop="20"
marginBottom="20"
text="這是一個有焦點的textView"
accessibility="on"
onTap="@rTap{}"
accessibilityText="這是一個有焦點的textView"
/>
<FastTextView
width="match_content"
height="match_content"
textColor="#ff051b28"
textSize="12"
marginTop="20"
marginBottom="20"
text="這是一個FastTextView"
/>
<TextView
width="match_content"
height="match_content"
textColor="#ff051b28"
textSize="12"
marginTop="20"
marginBottom="20"
accessibility="off"
text="這是一個不需要被朗讀的textView"
/>
<ImageView
width="100"
height="100"
marginLeft="20"
marginTop="12"
borderWidth="3ap"
borderColor="#FF0000"
accessibility="on"
accessibilityText="這是一個ImageView點選"
onTap="@rTap{'測試'}"
imageUrl="https://img.alicdn.com/tfs/TB1FuMQQFXXXXXLXXXXXXXXXXXX-420-420.jpg"
/>
</LinearLayout>
</LinearLayout>
模闆示例手機示範
看如下視訊,由于 Layout accessibility 設定了 auto 屬性,是以該 Layout 會被選中,并朗讀内部含有無障礙資訊的 Text,但是第二個和第四個 TextView 是不會朗讀的,第二個配置了 onTap&accessibility=on,是以此時它屬于一個可互動的控件,是需要單獨被選中的,第四個 accessibility=off,是以此時它是關閉無障礙這個功能的,是以也沒法選中朗讀,且不會被 Layout 選中朗讀。
無障礙校驗卡口
支援是一方面,引導開發同學去寫是另一方面。
事實上現在好多動态化的方案,包括 native 本身都會支援無障礙功能,但是這種支援是單向的,如果你隻是支援,但是開發者不去支援,那最終這個産品無障礙功能依舊是缺失。
開發者為什麼不去支援呢?
- 第一,無障礙的公益宣導不夠,優先級不高,開發本身沒有這個意識,無障礙測試用例缺失。
- 第二,無障礙功能的支援有一定的成本,且沒有一套标準和規範告知什麼情況下需要無障礙,且如何支援。
- 第三,流程上沒有監督和管控,開發有可能會忘記。
為了更好的支援幫助視障使用者使用手機淘寶,同時幫助業務方定位發現無障礙的錯誤,減少無障礙的測試回歸工作量,我們發起了無障礙校驗卡口,智能檢測無障礙問題,通過調用無障礙服務來判斷模闆是否合格,以此確定每一個模闆的釋出都是支援無障礙的。
添加無障礙校驗卡口這才是無障礙工作最關鍵的一環,目前由于手淘的核心鍊路都使用的 DinamicX,且 DinamicX 模闆都在元件平台開發,是以隻要我們加上這卡口,你想不支援無障礙都不行,否則你的動态模闆釋出不了。
擁有無障礙校驗卡口功能的 DinamicX 開發模式流程圖:
目前無障礙卡口校驗的相關規則(有些校驗規則也是為了抹平兩端差異而加的):
- 非互動性控件,如 ImageView、FrameLayout、LinearLayout 等,若有設定 onTap 屬性,則會檢查是否含有無障礙屬性,若沒有則校驗不通過,并給出建議:需要設定accessibility=on, 開啟無障礙焦點,同時設定 accessibilityText= xx 屬性。
- 非互動性控件,如 ImageView、FrameLayout、LinearLayout 等,若有設定 accessibility=on 的時候,必須同時設定 accessibilityText=xx。
- 子 View 設定 onTap 屬性的時候,必須保證它的父 Layout 沒有設定 accessibility=on,否則該子 View 是不能擷取焦點的。
- 如果 Layout 設定了 auto 屬性,TextView 不能隻設定 onTap,還要設定 accessibility=on,否則擷取不到焦點。
- accessibility 屬性不能設定動态表達式。
如下視訊所示,假設 Layout 節點上面設定 onTap 點選事件,那麼校驗卡口會提醒你該節點需要設定無障礙資訊:
現階段整個手淘首頁、詳情、購物車、我的淘寶、訂單詳情、訂單清單等核心頁面所開發的模闆都會經過該卡口的校驗。
願景
也許我們做的不一定是最好的,但是我們會一直努力去做,不為别的,隻是為了讓手淘在大衆心中特别是盲人的心中除了是一個購物 app 之外,更是一個有溫暖的産品,一個讓盲人感動的産品。
希望有一天我們的開發同學開發模闆的時候,再也不需要彈起那個卡口的校驗,而是寫模闆的那一刻,已經想起了那些拿着手機耳邊聽的人群。
希望有一天手淘是盲人心目中最喜歡的購物産品。
希望有一天看到這篇文章的同學們能夠在心裡有那麼一絲觸動,不是道德綁架,而是在未來某一天盲人談起手淘那種由衷的感謝可以帶給自己心靈的那種慰藉。
希望有一天看到這篇文章的同學能夠感受到:無障礙是一件公益,做完心裡暖暖的,技術除了有價值以外,還可以有溫度。
重視無障礙,重視公益,從我們做起!
借助手機旁白功能,視障者用耳朵購物
淘寶無障礙實驗室工程師閉着眼睛做無障礙測試
Git 公開課 | 10 個課時教你上手
如何在各平台下安裝 Git?Git 的工作流程是怎樣的?如何了解工作區、暫存區和版本庫的概念?Git 有哪些基本操作?如何搭建 Git 伺服器作為自己的私有倉庫?識别下方二維碼,或點選文末“
閱讀原文”來學習 Git