AutomationId 在 Android 上存在多个问题。
Android - Using AutomationId prevents TalkBack screenreader accessibility 讨论了根本问题:
Xamarin.Forms“借用”Android 上的 ContentDescription 属性用于自动化 ID。这些 ID 污染了 Android 的 TalkBack 可访问性树,使应用几乎无法导航。
这意味着您可以支持测试自动化或可访问性,而不是两者。我们的应用需要同时支持两者。
在Tabs 的情况下,大概Xamarin 代码正在执行您所看到的:将Title 复制到content-desc,以便Android 文本阅读器会说出它。
建议的解决方法是编写自定义渲染器来满足您的需求。在comment by codingL3gend 中描述:
我能够通过创建 customrenderer 和相应的自定义组件来解决此问题,以允许覆盖触发可访问性事件时触发的本机 android 方法。您需要在自定义组件上创建一些可绑定属性,您可以在自定义渲染器中访问这些属性,以便将内容描述值设置为您想要的值,但这很简单。
每当触发可访问性事件时,都会在控件/自定义渲染器中触发此方法
public override bool OnRequestSendAccessibilityEvent(Android.Views.View child, AccessibilityEvent e)
{
if (AccessibilityHandler.IsAccessibilityEnabled(_context) && child != null)
{
if (!string.IsNullOrEmpty(_automationId) && _automationId.Equals(child.ContentDescription))
{
child.ContentDescription = $"{_automationName} {_helpText}";
}
}
return base.OnRequestSendAccessibilityEvent(child, e);
}
然后您可以将控件/自定义渲染器的 contentDescription 值设置回控件/自定义渲染器与视图分离时的automationId 值。
protected override void OnDetachedFromWindow()
{
base.OnDetachedFromWindow();
if (!string.IsNullOrEmpty(_automationId))
{
Control.ContentDescription = _automationId;
}
}
帮助类
public static class AccessibilityHandler
{
public static bool IsAccessibilityEnabled(Context context)
{
var accessibility = (AccessibilityManager)context.GetSystemService(MainActivity.AccessibilityService);
return accessibility?.GetEnabledAccessibilityServiceList(Android.AccessibilityServices.FeedbackFlags.Spoken)?.Count > 0;
}
}
如果您在测试期间只需要 AutomationId,或者您可以忍受它对辅助功能屏幕阅读器的影响(尤其是它不会是多语言的),那么您可以做很多测试时使用的更简单的自定义渲染器。
将其放入您的自定义渲染器中(如果不是 Tab,则将 <Tab> 更改为适当的 Xamarin 类):
protected override void OnElementChanged( ElementChangedEventArgs<Tab> e )
{
base.OnElementChanged( e );
if (e.OldElement != null)
{
// Removing previous element.
// TBD: Remove obsolete references. (usually not needed)
}
if (Element == null)
// Going away with no replacement.
return;
UpdateAutomationId();
}
void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Element.AutomationId))
{
UpdateAutomationId();
}
}
void UpdateAutomationId()
{
var _automationId = Element.AutomationId;
if (!string.IsNullOrEmpty(_automationId))
{
Control.ContentDescription = _automationId;
}
}