正文
抖音无障碍布景

国度近期开展了无障碍建立活动。为了积极响应国度号召安卓抖音无人曲播软件下载地址,为抖音视障用户可以得到更好的交互体验,对抖音无障碍功用停止了专项治理和革新。

无障碍形式下的利用办法

抖音的无障碍功用实现次要是通过开启 Google TalkBack(或第三方屏幕阅读)功用,将用户在屏幕上触摸选中区域的内容朗读出来,使得视障人士能够按照朗读的内容获取本身当前操做区域的信息,从而提拔视障人士的利用和交互体验。

常用的操做手势:

阅读某个 View:单击点击某个 View:双击沿某个标的目的滑动:双指沿所需标的目的滑动挨次阅读页面:单指摆布滑动本文的目标

使研发同窗对无障碍功用有一个愈加全面的认识和领会,便利研发同窗停止无障碍功用的开发。

本文将分为无障碍功用实现原理和无障碍功用实现实例两部门停止介绍。

无障碍功用实现原理系统构造

无障碍功用的实现需要以下三个部门的撑持:辅助 App(例如 TalkBack)、被辅助 app(用户利用的 app,例如抖音头条等)以及系统办事 AccessibilityManagerService,那三者之间的关系如下图所示:

安卓抖音无人曲播软件下载地址

从上图中能够看出,以上的流程次要涉及到三个历程的通信。辅助 app 和被辅助 app 不需要间接跟被辅助的 app 通信,而是通过 SystemServer 停止直达通信,那个过程次要涉及到了四个 aidl 接口:

被辅助 app->SystemServer(IAccessibilityManager.aidl)

当被辅助 app 产生触摸事务后,会通过该接口发送无障碍事务给 SystemServer 历程的 AccessibilityManagerService。

SystemServer->辅助 app(IAccessibilityServiceClient.aidl)

当 SystemServer 领受到被辅助 app 发送的无障碍事务时,会将事务通过该接口传递给辅助 app(例如 TalkBack)停止处置。

辅助 app->SystemServer(IAccessibilityServiceConnection.aidl)SystemServer->被辅助 app(IAccessibilityInteractionConnection.aidl)

当需要被辅助 app 的某个 View 的信息时,能够通过那两个接口的 findAccessibilityNodeInfosByViewId 办法实现。

无障碍事务传递流程

当用户触摸屏幕时,会颠末以下的流程将触摸事务传递给被触摸的 View:

安卓抖音无人曲播软件下载地址

下面本文将次要阐发以上流程中四个重点部门的内容:无障碍形式下的事务转换、触摸事务到 Activity 的传递过程、事务传递给详细的 View 的分发过程以及最末无障碍事务的施行流程。

1.无障碍形式下的事务转换

在 TalkBack 开启的形态下,因为 TalkBack 的无障碍办事中声了然 android:canRequestTouchExplorationMode=''true'' ,因而开启 TalkBack 后 AccessibilityManagerService 会更新 AccessibilityInputFilter 的FLAG_FEATURE_TOUCH_EXPLORATION(触摸阅读)属性置为 true。

安卓抖音无人曲播软件下载地址

在 FLAG_FEATURE_TOUCH_EXPLORATION 形式下会创建一个 TouchExplorer 对象。AccessibilityInputFilter 继承了 InputFilter,对输入事务停止过滤,通过和 TouchExplorer 配合实现 TalkBack 形式下的触摸阅读手势。TouchExplorer 负责将通俗触摸事务转换为触摸阅读手势,例如将 MotionEvent.ACTION_DOWN 事务转换为 MotionEvent.ACTION_HOVER_ENTER(悬停事务)。因而在 TalkBack 开启的情况下,用户单击 View 时,App 施行的是 ACTION_HOVER_ENTER 事务,双击 View 时才会施行 ACTION_DOWN 事务。

2.触摸事务到 Activity 的传递过程

在 Android 中,动静机造是 handler 机造,通过将动静封拆到 Message 中,并将该动静发送到 handler 所在的 MessageQueue 中,通过 Looper 不竭挪用 MessageQueue 的 next 办法停止动静的处置。

当用户触摸屏幕上的某个 View 时,handler 会对收到的动静停止以下的处置:

安卓抖音无人曲播软件下载地址

那里需要重点看一下 View 的 dispatchPointerEvent() 办法:

public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); }}

在该办法中对 event 停止判断,若是是 touchEvent 就挪用 dispatchTouchEvent() 办法,不然挪用 dispatchGenericMotionEvent() 办法。判断能否为 touch 事务的逻辑如下:

bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { if (source & AINPUT_SOURCE_CLASS_POINTER) { // Specifically excludes HOVER_MOVE and SCROLL. switch (action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_MOVE: case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_POINTER_UP: case AMOTION_EVENT_ACTION_CANCEL: case AMOTION_EVENT_ACTION_OUTSIDE: return true; } } return false;}

契合以上 case 的 event 即为 TouchEvent。

起首来看一下 dispatchPointerEvent 办法中对 TouchEvent 事务的处置,进入 DecorView 的 dispatchTouchEvent() 办法中:

@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback(); return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);}

在该办法中,mWindow 是与 Activity 联系关系的 PhoneWindow 对象,因为 DecorView 是由 PhoneWindow 创建的,而且通过 setWindow() 办法,DecoView 对象持有 PhoneWindow 对象的引用。通过 getCallback() 办法,获得了实现了 Window.Callback 的对象,而 Activity 实现了那个接口,因而当挪用cb.dispatchTouchEvent(ev) 时,现实上挪用的是 Activity 中的 dispatchTouchEvent() 办法。

同样的在 dispatchGenericMotionEvent() 办法中,也有类似的代码逻辑:

@Overridepublic boolean dispatchGenericMotionEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback(); return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);}

此办法中现实上也是挪用了 Activity 的 dispatchGenericMotionEvent() 办法对事务停止后续的分发和处置。此时事务就已经传递到了 Activity,由 Activity 进一步停止事务分发。

3.触摸事务传递到详细 View 的过程

在研究无障碍形式下的事务传递过程之前,起首来回忆一下通俗形式下的事务传递机造:

3.1 通俗形式的事务分发

3.1.1 通俗形式下事务分发 Key Method

当一个 MotionEvent 产生之后,系统需要将该事务传递给一个详细的 view,那个传递过程就是事务的分发过程。分发过程依赖于以下三个重要办法:

public boolean dispatchTouchEvent(MotionEvent ev)

该办法用来停止事务的分发,办法的返回值取决于当前 View 的 onTouchEvent() 办法和子 View 的 dispatchTouchEvent() 办法的影响。

public boolean onInterceptTouchEvent(MotionEvent ev)

仅 ViewGroup 拥有的办法,用来判断能否拦截某个事务。

public boolean onTouchEvent(MotionEvent event)

在 dispatchTouchEvent() 办法中停止挪用,用来处置点击事务。

3.1.2 通俗形式下的事务分发

整个分发过程能够用以下的流程图来暗示:

安卓抖音无人曲播软件下载地址

3.2 无障碍形式下的事务分发

无障碍形式下的事务分发与通俗形式下的事务分发有良多类似之处:

3.2.1 无障碍形式下的事务分发 Key Method:

与通俗事务触摸事务的分发类似,无障碍事务触发事务分发也有类似的三个重要办法:

protected boolean dispatchHoverEvent(MotionEvent event)

该办法用来停止事务的分发,办法的返回值取决于当前 View 的 onHoverEvent() 办法和子 View 的 dispatchHoverEvent() 办法的影响。

public boolean onInterceptHoverEvent(MotionEvent event)

仅 ViewGroup 拥有的办法,用来判断能否拦截某个事务。

public boolean onHoverEvent(MotionEvent event)

在 dispatchHoverEvent() 办法中停止挪用,用来处置 hover 事务。

3.2.2 无障碍形式下的事务分发

当用户处于无障碍形式下,用户停止点击屏幕时,会挪用 dispatchPointerEvent 办法中的 dispatchGenericMotionEvent 办法:

public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); }}

现实上挪用的是 Activity 的 dispatchGenericMotionEvent() 办法,Activity 领受到事务后,会传递给 PhoneWindow 再传递给 DecorView。DecorView 会挪用 View 的 dispatchGenericMotionEvent() 办法:

public boolean dispatchGenericMotionEvent(MotionEvent event) { ··· final int source = event.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { final int action = event.getAction(); //判断事务类型属于Hover,挪用dispatch办法起头停止分发 if (action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_MOVE || action == MotionEvent.ACTION_HOVER_EXIT) { if (dispatchHoverEvent(event)) { return true; } } ... return false;}

在该办法中,若是判断事务为 HoverEvent,就挪用 ViewGroup 的 dispatchHoverEvent() 办法起头停止事务分发。

若是某个 ViewGroup 的 onInterceptHoverEvent() 办法返回 true,暗示它要拦截当前事务,并交给本身处置,反之返回 false 暗示不拦截当前事务,并将当前事务继续传递给子 View,子 View 会挪用本身的 dispatchHoverEvent() 办法,如斯轮回往复曲到事务最末被处置。

在事务处置阶段,View/ViewGroup 起首会判断能否设置了 OnHoverListener,并判断它的 onHover 办法的返回值能否为 true,若是返回值为 true,则不会挪用 onHoverEvent() ,反之会挪用 onHoverEvent() 办法对事务停止处置。

整个处置过程能够用下面的流程图停止暗示:

安卓抖音无人曲播软件下载地址

在 onHoverEvent() 办法中,会挪用到 sendAccessibilityHoverEvent()办法,该办法后续会挪用以下办法:

sendAccessibilityEventsendAccessibilityEventUncheckedonInitializeAccessibilityEventdispatchPopulateAccessibilityEventonPopulateAccessibilityEventonRequestSendAccessibilityEvent(仅在 ViewGroup 中有默认实现)

以上 6 种办法为当自定义 View 时适配无障碍形式能够笼盖实现的办法,能够重写 View 的那些办法或者实现 View.AccessibilityDelegate 来处理一些特殊场景下 TalkBack 播报的问题。

此中的 sendAccessibilityEventUnchecked 办法会向上传递到 ViewRootImpl 的 requestSendAccessibilityEvent 办法中,从仓库信息中就能够证明那一点:

安卓抖音无人曲播软件下载地址

接着无障碍事务会通过 AccessibilityManager 的 sendAccessibilityEvent 办法跨历程挪用 system_process 历程的 AccessibilityManagerService,将 AccessibilityEvent 事务传递到 TalkBack 的 TalkBackService 中。

4.无障碍事务的施行流程

那一节次要阐发从 TalkBack 发出无障碍事务,到被辅助 app 在屏幕上绘造出绿框的过程。

TalkBack 将无障碍事务发送给被辅助 APP 时,需要 system_process 历程做为直达,对应的接口为 IAccessibilityServiceConnection.aidl 和 IAccessibilityInteractionConnection.aidl。颠末直达后,最末会挪用到被触摸 View 的 performAccessibilityAction 办法中,在没有 delegate 的情况下,会施行 performAccessibilityActionInternal 办法。在该办法中,若是是 ACTION_ACCESSIBILITY_FOCUS 事务,会施行 requestAccessibilityFocus 办法:

安卓抖音无人曲播软件下载地址

那个办法会施行两个关键操做:

挪用 ViewRootImpl 的 setAccessibilityFocus 办法将本身设置为 focus,然后挪用 invalidate() 触发重绘操做,ViewRootImpl 会在 onPostDraw 办法中施行 drawAccessibilityFocusedDrawableIfNeeded 来绘造绿框。安卓抖音无人曲播软件下载地址

挪用 sendAccessibilityEvent 办法,将 TYPE_VIEW_ACCESSIBILITY_FOCUSED 事务发送进来,那个事务被 talkback 领受后,会挪用朗读引擎 TTS 读出 View 的内容,实现了无障碍形式下对触摸区域内容的播报。无障碍功用实现实例Case 1:无障碍形式下点击 View 播报“未加标签”

处理计划:在该 View 的 android:contentDescription 属性上设置需要播报的 String。

Case 2:焦点过多,需要删除多余焦点或需要某个 View 可以停止播报

处理计划:将不需要播报的 View 的 android:importantForAccessibility 属性设置为 no,将需要播报的 View 的该属性设置为 yes。

Case 3:无障碍形式下在上层页面点击仍能选中下层 View

处理计划:将下层的根 View 的 android:importantForAccessibility 属性设置为"noHideDescendants"

Case 4:利用的自定义 Toast 不播报内容

处理计划:在自定义 Toast 展现的时候,主动发送一个 AccessibilityEvent 事务

mText.postDelayed(new Runnable() { @Override public void run() { mText.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); }}, 1);

设置延时是为了制止不生效的问题。

Case 5:设置自定义 View 的播报内容

处理办法:override View 的 onPopulateAccessibilityEvent()办法。

举例:设置自定义 View 开/关形态(已开启/已封闭)的播报内容。

@Overridepublic void onPopulateAccessibilityEvent(AccessibilityEvent event) { super.onPopulateAccessibilityEvent(event); final CharSequence text = isChecked() ? "已开启" : "已封闭"; if (text != null) { event.getText().add(text); }}Case 6:设置自定义 View 播报的控件类型及选中形态

处理办法:利用 AccessibilityDelegate

ViewCompat.setAccessibilityDelegate(targetView, new AccessibilityDelegateCompat() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { super.onInitializeAccessibilityNodeInfo(host, info); info.setRoleDescription("标签类型");//设置播报的标签类型 info.setCheckable(true); info.setChecked(checked);//设置播报的被选中形态 }});参加安卓抖音无人曲播软件下载地址我们

欢送参加抖音-关系与办事团队,我们专注于抖音多个核心营业场景的落地与迭代,在营业、架构、手艺等方面都有投入,等待安卓抖音无人曲播软件下载地址你的参加!

抖音-关系与办事团队正在热招 Android & iOS 研发,在北京,成都均有职位,欢送送达简历!

联络邮箱:liutianxiang.kid@bytedance.com邮件题目:简历-姓名-工做年限-期望工做地点

本文TAG:

幕言互游在线咨询

上班时间:9:00-22:00
周六、周日:14:00-22:00
wechat
打开微信扫一扫,加我好友!

无限流量卡免费领取

点击预约
免费领取 先到先得