触控交互开发人员指南

设计应用时确保用户主要通过触摸屏进行输入。 如果使用 UWP 控件,则无需其他编程即可支持触控板、鼠标和笔/触笔,因为 UWP 应用免费提供此支持。

但是,切记,针对触摸屏优化的 UI 并不总是优于传统 UI。 这两种 UI 都具有技术和应用独有的优点和缺点。 在转换到注重触摸的 UI 时,重要的是了解触摸、触摸板、笔/触笔、鼠标和键盘输入之间的核心差别。

重要 APIWindows.UI.Xaml.InputWindows.UI.CoreWindows.Devices.Input

许多设备都具有多点触摸屏,支持使用一个或多个手指(或触控交互)输入。 触控交互及其移动解释为触控手势和操作,以支持各种用户交互。

Windows 应用具有多种处理触控输入的不同机制,使你能够创建用户能够放心浏览的沉浸式体验。 下面我们将介绍在 Windows 应用中使用触摸输入的基本知识。

触控交互需要满足三个条件:

  • 触控敏感显示屏。
  • 一根或多根手指直接触摸(或临近,如果显示屏具有临近传感器并支持悬停检测)该显示屏。
  • 触控交互移动(或缺乏移动,基于时间阈值)。

触控传感器提供的输入数据可以:

  • 解释为直接操作一个或多个 UI 元素的物理手势,例如平移、旋转、调整大小或移动。 (相比之下,通过元素的属性窗口、对话框或其他 UI 提示与元素进行交互视为间接操作。)
  • 识别为替代输入法,例如鼠标或笔。
  • 用于补充或修改其他输入方法的各个方面,例如模糊化用笔绘制的笔划墨迹。

触控输入通常涉及在屏幕上直接操作元素。 该元素会立即对其命中测试区域内的任何触控交互作出响应,并对包括移除在内的任何后续触控交互移动进行适当的回应。

应仔细设计自定义触控手势和交互。 它们应直观、响应迅速且可发现,并且应该让用户放心地探索你的应用。

确保应用功能在所有受支持的输入设备类型中一致地呈现。 如有必要,使用某种形式的间接输入模式,例如适用于键盘交互的文本输入,或适用于鼠标和笔的 UI 提示。

切记,传统输入设备(如鼠标和键盘)对于许多用户来说熟悉且有吸引力。 它们可以提供触控无法实现的速度、准确性和触觉反馈。

可通过为所有输入设备提供与众不同的交互体验支持最广泛的功能和首选项,尽可能吸引更多的受众,并让应用受到更多客户的青睐。

比较触控交互要求

下表显示了设计优化触摸的 Windows 应用时应考虑的一些输入设备之间的不同。

因子触控交互鼠标、键盘、笔/触笔交互触摸板
精准率指尖的触控区域大于单一 x-y 坐标,这会增加意外激活命令的可能性。鼠标和笔/触笔提供精确的 x-y 坐标。与鼠标相同。
触控区域的形状在整个移动过程中会发生变化。 鼠标移动和笔/触笔笔划提供精确的 x-y 坐标。 键盘焦点为显式。与鼠标相同。
没有鼠标光标可协助定位。鼠标光标、笔/触笔光标和键盘焦点均有助于定位。与鼠标相同。
人体解剖学指尖移动不精确,因为通过一根或多根手指很难实现直线运动。 这与手关节的弯曲度和运动中涉及的关节数有关。使用鼠标或笔/触笔更容易执行直线运动,因为控制它们的手部的物理移动距离比光标在屏幕上的移动距离更短。与鼠标相同。
由于手指姿势和用户对设备的抓握,可能难以到达显示设备触控表面的某些区域。鼠标和笔/触笔可以到达屏幕的任何部分,而任何控件都应可通过键盘上的 Tab 键顺序进行访问。 手指姿势和抓握可能是个问题。
对象可能被一个或多个指尖或用户手部阻挡。 这称为闭塞。间接输入设备不会造成闭塞。与鼠标相同。
对象状态触控使用双状态模式:显示设备的触控表面要么处于触控状态(打开),要么处于非触控状态(关闭)。 没有可触发其他视觉反馈的悬停状态。

鼠标、笔/触笔和键盘均呈现三状态模式:向上(关闭)、向下(打开)和悬停(聚焦)。

用户可通过悬停浏览和了解与 UI 元素关联的工具提示。 悬停和焦点效果可以传达哪些对象是交互式对象,同时有助于定位。

与鼠标相同。
丰富交互支持多点触控:触控表面上有多个输入点(指尖)。支持单一输入点。与触控相同。
支持通过点击、拖移、滑动、收缩和旋转等手势直接操作对象。不支持直接操作,因为鼠标、笔/触笔和键盘是间接输入设备。与鼠标相同。

注意

间接输入的优势在于经过了超过 25 年的优化。 悬停触发的工具提示等功能旨在解决特定于触控板、鼠标、笔/触笔和键盘输入的 UI 探索问题。 此类 UI 功能已针对触控输入提供的丰富体验进行了重新设计,而不会损害其他此类设备的用户体验。

使用触控反馈

在与应用进行交互期间,适当的视觉反馈可帮助用户识别、了解和适应应用和 Windows 平台解释其交互的方式。 视觉反馈可以指示成功的交互、传达系统状态、改善控制感、减少错误、帮助用户了解系统和输入设备,并鼓励交互。

如果用户依靠触控输入来实现需要基于位置的准确性和精度的活动,视觉反馈至关重要。 每当检测到触控输入时都会在检测位置显示反馈,以帮助用户了解应用及其控件定义的所有自定义定位规则。

目标设定

定位通过各项进行优化:

  • 触控目标大小

    明确的大小准则可确保应用程序提供舒适的 UI,以包含可轻松、安全地定位的对象和控件。

  • 接触几何

    手指的整个接触区域确定最有可能的目标对象。

  • 清理

    可通过在组中的项目(例如单选按钮)之间拖移手指轻松对其重新定位。 当前项在释放触控后处于激活状态。

  • 摇摆

    要轻松重新定位密集打包的项目(例如超链接),可用手指按住项目(无需滑动)并前后摇摆。 由于闭塞,当前项通过工具提示或状态栏进行标识,并在释放触控时激活。

准确性

使用以下方法设计草率交互:

  • 吸附点,当用户与内容进行交互时可以更轻松地停在所需位置。
  • 定向“滑轨”,可以协助纵向或横向平移,即使手部沿轻微弧线移动。有关详细信息,请参阅平移准则

封闭

通过以下方法避免手指和手势闭塞:

  • UI 大小和定位

    将 UI 元素的大小调整为指尖接触区域无法完全覆盖的大小。

    尽可能将菜单和弹出窗口置于接触区域上方。

  • 工具提示

    当用户在对象上保持手指接触时显示工具提示。 这对于描述对象功能非常有用。 用户可以将指尖从对象上拖开,以避免调用工具提示。

    对于小对象,偏移工具提示,使其不会被指尖接触区域覆盖。 这有助于定位。

  • 精度控点

    如果需要精度(例如文本选择),提供偏移选择控点以提高准确性。 有关详细信息,请参阅文本和图像(Windows 运行时应用)选择准则

定时

避免定时模式更改以支持直接操作。 直接操作模拟对象的直接实时物理处理。 对象会在手指移动时做出响应。

另一方面,计时交互在触控交互后发生。 计时交互通常取决于时间、距离或速度等不可见阈值,以确定要执行的命令。 在系统执行操作之前,计时交互不会提供视觉反馈。

与定时交互相比,直接操作具有许多优势:

  • 交互过程中的即时视觉反馈可提高用户的参与度、信心和控制能力。
  • 直接操作可以更安全地探索系统,因为操作可逆 - 用户可以轻松地以逻辑和直观方式回退其操作。
  • 直接影响对象和模拟现实世界交互的交互更加直观、可发现和令人难忘。 它们不依赖于模糊或抽象的交互。
  • 定时交互可能难以执行,因为用户必须达到任意和不可见阈值。

此外,强烈建议遵循以下规则:

  • 不应按所用手指数来区分操作。
  • 交互应支持复合操作。 例如,在拖移手指进行平移时收缩以缩放。
  • 不应按时间区分交互。 无论执行交互所需的时间为何,相同的交互都应具有相同的结果。 基于时间的激活会为用户引入强制延迟,并降低直接操作的沉浸式特性和系统响应能力。

    注意

    其中的一个例外是使用特定的定时交互来帮助了解和探究(例如,长按)。

  • 适当的说明和视觉提示会极大地影响高级交互的使用。

应用视图

通过应用视图的平移/滚动和缩放设置调整用户交互体验。 应用视图决定了用户如何访问和操作应用及其内容。 视图还提供惯性、内容边界退回和吸附点等行为。

当视图的内容不适合视区时,ScrollViewer 控件的平移和滚动设置决定了用户在单一视图中的导航方式。 例如,单一视图可以是杂志或书籍的一页、计算机的文件夹结构、文档库或相册。

缩放设置适用于视觉缩放(适用于 ScrollViewer 控件)和语义式缩放控件。 语义式缩放是一种触控优化技术,用于在单一视图中呈现和导航相关的大型数据集或内容集。 它的工作原理是使用两种不同的分类模式或缩放级别。 这类似于在单一视图中进行平移和滚动。 平移和滚动可与语义式缩放结合使用。

使用应用视图和事件可修改平移/滚动和缩放行为。 这可以提供比通过指针和手势控点事件更流畅的交互体验。

有关应用视图的详细信息,请参阅控件、布局和文本

自定义触控交互

如果实施自己的交互支持,切记用户需要涉及与应用中的 UI 元素直接交互的直观体验。 建议在平台控件库中对自定义交互进行建模,使内容保持一致且可发现。 这些库中的控件可提供完整的用户交互体验,包括标准交互、动画演示的物理效果、视觉反馈和辅助功能。 仅当存在清晰且正确定义的要求且基本交互不支持你的应用场景时,才创建自定义交互。

重要

Windows 11 及更高版本

默认情况下,某些三指和四指触摸交互在 Windows 应用中不起作用

默认情况下,系统现在使用三指和四指触摸交互来执行窗口切换或最小化和虚拟桌面更改等操作。 由于这些交互现在在系统级别进行处理,因此应用的功能可能会受此更改的影响。

为支持应用程序中的三指或四指交互,引入了新的用户设置,用于指定系统是否要处理这些交互:

“蓝牙和设备 > 触摸 > 三指和四指触控姿势”

当设置为“开”(默认)时,系统将处理所有三指和四指交互(应用将不能支持它们)。

当设置为“关”时,应用可支持三指和四指交互(不会交由系统处理)。

如果应用程序必须支持这些交互,建议你告知用户此设置,并提供一个链接,将设置应用启动到相关页面(ms-settings:设备触摸)。 有关详细信息,请参阅启动 Windows 设置应用

要提供自定义触控支持,可以处理各种 UIElement 事件。 这些事件分为三个抽象级别。

  • 静态手势事件在完成交互后触发。 手势事件包括 TappedDoubleTappedRightTappedHolding

    要在特定元素上禁用手势事件,可通过将 IsTapEnabledIsDoubleTapEnabledIsRightTapEnabledIsHoldingEnabled 设置为 false

  • 指针事件(如 PointerPressedPointerMoved)为每个触控交互提供低级详细信息,包括指针运动以及区分按住和松开事件的能力。

    指针是具有统一事件机制的泛型输入类型。 它会在触摸屏、触控板、鼠标或笔等活动输入源上显示基本信息,例如屏幕位置。

  • 操作手势事件(如 ManipulationStarted)表示正在进行的交互。 当用户触控某个元素并继续操作时,将开始触发这些事件,直到用户抬起手指或操作取消。

    操作事件包括缩放、平移或旋转等多点触控交互,以及使用惯性和速度数据的交互,例如拖移。 操作事件提供的信息不会标识执行的交互形式,而是包括位置、平移增量和速度等数据。 可以使用此触控数据来确定需要执行的交互类型。

下面是 UWP 支持的基本触控手势集。

名称 类型 描述
点击 静态手势 一根手指触摸屏幕并抬起。
长按 静态手势 一根手指触摸屏幕并保持原位。
幻灯片 操作手势 一根或多根手指触摸屏幕并朝同一方向移动。
轻扫 操作手势 一根或多根手指触摸屏幕并朝同一方向移动一小段距离。
轮数 操作手势 两根或多根手指触摸屏幕,并沿顺时针或逆时针弧线移动。
捏放 操作手势 两根或多根手指触摸屏幕并缩小手指间距。
拉伸 操作手势 两根或多根手指触摸屏幕并增大手指间距。

手势事件

有关各个控件的详细信息,请参阅控件列表

指针事件

指针事件由各种活动输入源引发,包括触摸屏、触控板、笔和鼠标(它们取代了传统鼠标事件)。

指针事件基于单一输入点(手指、笔尖、鼠标光标),不支持基于速度的交互。

下面列出了指针事件及其相关事件参数。

事件或类 描述
指针按下 在单指触摸屏幕时发生。
指针释放 在抬起该同一触控交互时发生。
指针移动 在屏幕上拖移指针时发生。
指针进入 在指针进入元素的命中测试区域时发生。
指针退出 在指针退出元素的命中测试区域时发生。
指针已取消 在异常失去触控交互时发生。
光标捕获丢失 在另一个元素获得指针捕获时发生。
指针滚轮已更改 当鼠标滚轮的增量值改变以及收缩触摸板时出现。
PointerRoutedEventArgs 提供所有指针事件数据。

以下示例演示如何使用 PointerPressedPointerReleasedPointerExited 事件来处理 Rectangle 对象上的点击交互。

首先,采用 Extensible Application Markup Language (XAML) 创建名为

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Rectangle Name="touchRectangle"
           Height="100" Width="200" Fill="Blue" />
</Grid>

接下来,指定 PointerPressedPointerReleasedPointerExited 事件的侦听器。

MainPage::MainPage()
{
    InitializeComponent();

    // Pointer event listeners.
    touchRectangle->PointerPressed += ref new PointerEventHandler(this, &MainPage::touchRectangle_PointerPressed);
    touchRectangle->PointerReleased += ref new PointerEventHandler(this, &MainPage::touchRectangle_PointerReleased);
    touchRectangle->PointerExited += ref new PointerEventHandler(this, &MainPage::touchRectangle_PointerExited);
}
public MainPage()
{
    this.InitializeComponent();

    // Pointer event listeners.
    touchRectangle.PointerPressed += touchRectangle_PointerPressed;
    touchRectangle.PointerReleased += touchRectangle_PointerReleased;
    touchRectangle.PointerExited += touchRectangle_PointerExited;
}
Public Sub New()

    ' This call is required by the designer.
    InitializeComponent()

    ' Pointer event listeners.
    AddHandler touchRectangle.PointerPressed, AddressOf touchRectangle_PointerPressed
    AddHandler touchRectangle.PointerReleased, AddressOf Me.touchRectangle_PointerReleased
    AddHandler touchRectangle.PointerExited, AddressOf touchRectangle_PointerExited

End Sub

最后,PointerPressed 事件处理程序将增大 RectangleHeightWidth,而 PointerReleasedPointerExited 事件处理程序将 HeightWidth 重新设置为其起始值。

// Handler for pointer exited event.
void MainPage::touchRectangle_PointerExited(Object^ sender, PointerRoutedEventArgs^ e)
{
    Rectangle^ rect = (Rectangle^)sender;

    // Pointer moved outside Rectangle hit test area.
    // Reset the dimensions of the Rectangle.
    if (nullptr != rect)
    {
        rect->Width = 200;
        rect->Height = 100;
    }
}

// Handler for pointer released event.
void MainPage::touchRectangle_PointerReleased(Object^ sender, PointerRoutedEventArgs^ e)
{
    Rectangle^ rect = (Rectangle^)sender;

    // Reset the dimensions of the Rectangle.
    if (nullptr != rect)
    {
        rect->Width = 200;
        rect->Height = 100;
    }
}

// Handler for pointer pressed event.
void MainPage::touchRectangle_PointerPressed(Object^ sender, PointerRoutedEventArgs^ e)
{
    Rectangle^ rect = (Rectangle^)sender;

    // Change the dimensions of the Rectangle.
    if (nullptr != rect)
    {
        rect->Width = 250;
        rect->Height = 150;
    }
}
// Handler for pointer exited event.
private void touchRectangle_PointerExited(object sender, PointerRoutedEventArgs e)
{
    Rectangle rect = sender as Rectangle;

    // Pointer moved outside Rectangle hit test area.
    // Reset the dimensions of the Rectangle.
    if (null != rect)
    {
        rect.Width = 200;
        rect.Height = 100;
    }
}
// Handler for pointer released event.
private void touchRectangle_PointerReleased(object sender, PointerRoutedEventArgs e)
{
    Rectangle rect = sender as Rectangle;

    // Reset the dimensions of the Rectangle.
    if (null != rect)
    {
        rect.Width = 200;
        rect.Height = 100;
    }
}

// Handler for pointer pressed event.
private void touchRectangle_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    Rectangle rect = sender as Rectangle;

    // Change the dimensions of the Rectangle.
    if (null != rect)
    {
        rect.Width = 250;
        rect.Height = 150;
    }
}
' Handler for pointer exited event.
Private Sub touchRectangle_PointerExited(sender As Object, e As PointerRoutedEventArgs)
    Dim rect As Rectangle = CType(sender, Rectangle)

    ' Pointer moved outside Rectangle hit test area.
    ' Reset the dimensions of the Rectangle.
    If (rect IsNot Nothing) Then
        rect.Width = 200
        rect.Height = 100
    End If
End Sub

' Handler for pointer released event.
Private Sub touchRectangle_PointerReleased(sender As Object, e As PointerRoutedEventArgs)
    Dim rect As Rectangle = CType(sender, Rectangle)

    ' Reset the dimensions of the Rectangle.
    If (rect IsNot Nothing) Then
        rect.Width = 200
        rect.Height = 100
    End If
End Sub

' Handler for pointer pressed event.
Private Sub touchRectangle_PointerPressed(sender As Object, e As PointerRoutedEventArgs)
    Dim rect As Rectangle = CType(sender, Rectangle)

    ' Change the dimensions of the Rectangle.
    If (rect IsNot Nothing) Then
        rect.Width = 250
        rect.Height = 150
    End If
End Sub

操作事件

如果需要支持多个手指交互或要求使用高速数据的交互,请在应用中使用操作事件。

可以使用操作事件来检测拖移、缩放和法定保留等交互。

注意

触摸板不会引发操作事件。 但是会为触摸板输入引发指针事件。

下面列出了操作事件及相关事件参数。

事件或类 描述
ManipulationStarting 事件 首次创建操作处理器时发生。
ManipulationStarted 事件 在输入设备开始操作 UIElement 时发生。
ManipulationDelta 事件 输入设备在操作过程中更改位置时发生。
ManipulationInertiaStarting 事件 输入设备在操作和惯性开始期间失去与 UIElement 对象的交互时发生。
ManipulationCompleted 事件 UIElement 上的操作和惯性完成时发生。
操控启动路由事件参数 提供 ManipulationStarting 事件的数据。
操作启动路由事件参数 提供 ManipulationStarted 事件的数据。
ManipulationDeltaRoutedEventArgs 提供 ManipulationDelta 事件的数据。
ManipulationInertiaStartingRoutedEventArgs 提供 ManipulationInertiaStarting 事件的数据。
操控速度 描述操作发生时的速度。
ManipulationCompletedRoutedEventArgs 提供 ManipulationCompleted 事件的数据。

手势由一系列操作事件组成。 每个手势都以 ManipulationStarted 事件开始,例如当用户触摸屏幕时。

接下来,将触发一个或多个 ManipulationDelta 事件。 例如,如果触摸屏幕,然后在屏幕上拖移手指。 最后,交互完成时会引发 ManipulationCompleted 事件。

注意

如果你没有触摸屏监视器,则可以使用鼠标和鼠标滚轮界面在模拟器中测试你的操作事件代码。

以下示例演示如何使用 ManipulationDelta 事件处理 Rectangle 上的滑动交互,并在整个屏幕上进行移动。

首先,采用 XAML 创建名为 touchRectangleWidth 分别为 200。

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Rectangle Name="touchRectangle"
               Width="200" Height="200" Fill="Blue" 
               ManipulationMode="All"/>
</Grid>

接下来,将创建名为 的全局 ,用于平移 dragTranslation。 将在 Rectangle 上指定 ManipulationDelta 事件侦听器,并将 dragTranslation 添加到 RectangleRenderTransform

// Global translation transform used for changing the position of 
// the Rectangle based on input data from the touch contact.
Windows::UI::Xaml::Media::TranslateTransform^ dragTranslation;
// Global translation transform used for changing the position of 
// the Rectangle based on input data from the touch contact.
private TranslateTransform dragTranslation;
' Global translation transform used for changing the position of 
' the Rectangle based on input data from the touch contact.
Private dragTranslation As TranslateTransform
MainPage::MainPage()
{
    InitializeComponent();

    // Listener for the ManipulationDelta event.
    touchRectangle->ManipulationDelta += 
        ref new ManipulationDeltaEventHandler(
            this, 
            &MainPage::touchRectangle_ManipulationDelta);
    // New translation transform populated in 
    // the ManipulationDelta handler.
    dragTranslation = ref new TranslateTransform();
    // Apply the translation to the Rectangle.
    touchRectangle->RenderTransform = dragTranslation;
}
public MainPage()
{
    this.InitializeComponent();

    // Listener for the ManipulationDelta event.
    touchRectangle.ManipulationDelta += touchRectangle_ManipulationDelta;
    // New translation transform populated in 
    // the ManipulationDelta handler.
    dragTranslation = new TranslateTransform();
    // Apply the translation to the Rectangle.
    touchRectangle.RenderTransform = this.dragTranslation;
}
Public Sub New()

    ' This call is required by the designer.
    InitializeComponent()

    ' Listener for the ManipulationDelta event.
    AddHandler touchRectangle.ManipulationDelta,
        AddressOf testRectangle_ManipulationDelta
    ' New translation transform populated in 
    ' the ManipulationDelta handler.
    dragTranslation = New TranslateTransform()
    ' Apply the translation to the Rectangle.
    touchRectangle.RenderTransform = dragTranslation

End Sub

最后,在 ManipulationDelta 事件处理程序中,使用 Delta 属性上的 TranslateTransform 更新 Rectangle 的位置。

// Handler for the ManipulationDelta event.
// ManipulationDelta data is loaded into the
// translation transform and applied to the Rectangle.
void MainPage::touchRectangle_ManipulationDelta(Object^ sender,
    ManipulationDeltaRoutedEventArgs^ e)
{
    // Move the rectangle.
    dragTranslation->X += e->Delta.Translation.X;
    dragTranslation->Y += e->Delta.Translation.Y;
    
}
// Handler for the ManipulationDelta event.
// ManipulationDelta data is loaded into the
// translation transform and applied to the Rectangle.
void touchRectangle_ManipulationDelta(object sender,
    ManipulationDeltaRoutedEventArgs e)
{
    // Move the rectangle.
    dragTranslation.X += e.Delta.Translation.X;
    dragTranslation.Y += e.Delta.Translation.Y;
}
' Handler for the ManipulationDelta event.
' ManipulationDelta data Is loaded into the
' translation transform And applied to the Rectangle.
Private Sub testRectangle_ManipulationDelta(
    sender As Object,
    e As ManipulationDeltaRoutedEventArgs)

    ' Move the rectangle.
    dragTranslation.X = (dragTranslation.X + e.Delta.Translation.X)
    dragTranslation.Y = (dragTranslation.Y + e.Delta.Translation.Y)

End Sub

路由事件

此处提及的所有指针事件、手势事件和操作事件都作为路由事件实现。 这意味着事件可能由除了最初引发该事件的对象之外的对象进行处理。 对象树中的后续父级(例如 UIElement 的父容器或应用的根 Page)可以选择处理这些事件,即使原始元素无法处理。 相反,处理事件的任何对象都可以将事件标记为已处理,使其不再到达任何父元素。 有关路由事件概念及其如何影响为路由事件编写处理程序的详细信息,请参阅事件和路由事件概述

重要

如需在可滚动视图(例如 ScrollViewer 或 ListView)中处理 UIElement 的指针事件,必须调用 UIElement.CancelDirectmanipulation() 以在该视图中显式禁止支持对元素执行操作事件。 若要在该视图中重新启用操作事件,请调用 UIElement.TryStartDirectManipulation()

准则

  • 设计应用程序时将触控交互作为预期的主要输入方法。
  • 为所有类型的交互(触摸屏、笔、触笔、鼠标等)提供视觉反馈
  • 通过调整触摸目标大小、接触几何、推移和摇摆来优化定位。
  • 使用吸附点和定向“滑轨”优化准确性。
  • 提供工具提示和控点,以帮助提高紧密打包的 UI 项的触控准确性。
  • 尽量不要使用计时交互(适当用例:长按)。
  • 尽量不要使用用于区分操作的手指数。

示例

存档示例