UI 前沿技术

文本触控

Charles Petzold

下载代码示例

Charles Petzold
我第一台购买计算机时 Osborne 1,它被设计为足够小,能够存放平面的座位下面的第一个商业计算机。它的主要特点是能够显示仅 24 行 52 个字符的微小 5 英寸显示器部分实现这一功能。

我最新的计算机购买了基于 Windows Phone 7 的设备,并且当然有更小的屏幕。纵横比是不同 (3: 5 而不是 4: 3) 和 Windows Phone 7 字体具有成比例间距,但文本密度是几乎完全相同: 编写 Windows Phone 7 设备的 Silverlight 程序使用将大约相同数量的行具有相同数量的字符显示为 Osborne 1 显示默认字体!

我不会烦扰您无数的这些创建的 30 年分离,除非要请注意没有人曾经考虑阅读一本书 Osborne 1 上的两台计算机之间的差异。(的编写一本书中,但阅读一本书中,肯定不)。与此相反,阅读书籍已成为我Windows Phone 7 设备上我最喜欢的活动之一。我喜欢阅读我的电话这么我所写入我自己的电子书阅读软件,因此我可以定制体验所需的方式上的书籍。

触摸单词

当然大部分使用电话,因为电子书阅读器是其理想的大小的吸引力: 如果任何较小的屏幕,它将是难以阅读和处理。任何更大,它不适合放在我的口袋,和这是重要的标准。

我认为在电话上的触摸界面是同样的吸引力的一个重要方面。易用性翻转页面与只需点击一下或手指的卡似乎满足某本书的两个要求: 边学和 cerebral。当然,电话感觉不像真正的简介册,但响应对于它们来说如此轻松设备渐隐到背景中,以不会干扰更 cerebral 的阅读体验。

触摸界面是弊和电子书籍阅读器 curse。它是含糊的笔势可以与 leniency,如向上或向下拖动滚动的页面或匆忙向前或向后分页文档的页处理的最佳选择。触摸界面变得笨拙的要求更高的精度,如文本选定内容的活动。尺寸手指与屏幕上的文字之间的巨大差异使文本选定内容而无需从该程序的某些特殊协助几乎不可能。然而,此协助必须自身感觉自然。如果所选文本变为一个复杂的过程,从用户的角度来看,它可能也不是程序功能,根本。

虽然很容易设想电子书籍阅读器不选定文本的情况下,该功能提供了忽略太多好处。电子书籍阅读器可以让用户查找所选的单词或短语字典、 维基百科或搜索引擎中。所选的句子可能与读者的注意,如"很好的见解 !"或",并不遵循根本。"一起保存或者,您可能希望将简介册中的选区粘贴到电子邮件。

尽管我将讲述文本选定内容的上下文中的电子书籍阅读器,概念可应用于任何 Windows Phone 7 程序的屏幕显示文本,并能够使读者能够与该文本进行交互。

文本选定内容门限

所选文本中的第一个问题是命中测试。当用户接触屏幕,什么单词位于该手指?

Windows Phone 7 有三个编程接口,用于触摸: 低级别的 Touch.FrameReported 事件、 更高级别的处理事件和 (我的最爱) 可用于 Silverlight 的 Windows Phone Toolkit,这是从 CodePlex 可下载的笔势接口。每个这些接口使您可以确定手指与屏幕相接触的点处的元素。对于文本,此元素可能是 textblock)。

不能 ,但是,是很容易地确定用户接触 TextBlock 所显示的文本的哪一部分。如果 TextBlock 显示多个单词,您不能在执行计算使用字体规格信息的情况下在这些词之间区别开来。如果您具有必要的字体规格信息 — 例如,基于我在本专栏的最后一个月的一部分所述的技术 — 您最好使用该信息将文本分隔成多个单词每个 TextBlock 专门用于显示单个单词的位置。

如果每个单词是 textblock) 很容易地确定哪些单词位于对应于用户的手指在屏幕上的点,来跟踪用户的手指然后在选择多个连续出现的单词。但是,用户可能会比程序得不太确定!手指不透明,并掌握通常获改善大屏幕上的微小文字。

若要准确地选择屏幕上的单个单词,用户需要选择该文本之前,在屏幕上放大。与多点触摸接口,以展开元素在屏幕上的标准方法是两个手指常客操作。但是,常客是只有一部分。一旦屏幕会被放大,用户应该能够使用单个手指移动周围的页,相对于屏幕的视区。这有时称为"平移"操作,从使用运动图片 camerawork 中的单词。

简单地说,实现所选文本意味着解释的触摸事件处理不同的操作模式不同。A 拖动操作 — 触摸屏幕和移动手指 — 通常具有移位页向前或向后的效果。但是还需要拖动操作扩展所选文本超出了一个单词,这意味着东西才可将该程序放入文本选择模式。

要表示一个选区开始时,极好的选择是笔势名为"待定"。当用户按下一个手指放到屏幕并保存它仍为约一秒钟,将发生这种情况。后面的封存笔势任何拖动操作将被解释为扩展选定范围。

但是,如果用户具有用来展开的常客操作才能使所选内容,然后常规屏幕拖动 — 即前面没有保留通过拖动 — — 必须被解释为一个移动操作,而不是页面过渡效果。

在摘要,拖动操作可以解释具体情况取决于当前的模式的三种不同方式。

实施笔势模式

如果已经被遵循本专栏的过去的几个分期付款,您知道我一直在逐渐积聚 Windows Phone 7 电子书阅读器隔离各种功能并研究它们。这篇文章的可下载程序称为 BleakHouseReader。像其他程序,它仅限于一本书,此时间 Charles Dickens 的 1853年小说"Bleak 房屋,"这不是标题将似乎表明总 downer。简介册是从项目谷网站下载一个公共域纯文本格式文件。

当您切换为使用一个单独的 TextBlock 元素在页面上的每个单词使用 textblock) 对整个段落 (或尽可能多的段落为可以容纳在一页上) 时上, 一个程序 (PhineasReader) 中分页展示的性能改进。除了提供的切换更快的布局,也是实现所选文本的必要的第一步。

BleakHouseReader 中的 BookViewer 控件的所有安装处理程序,但触摸之一,那手势核心部分中的 BookViewer.xaml 文件中所示图 1。将忽略该控件的唯一笔势是 DoubleTap。像往常一样,三个嵌套的边框元素用于主机当前页、 上一页和下一页。添加此程序的 BookViewer.xaml 的内容转换的两个块用于常客和平移操作。转换的第二块使程序可以执行一些转换计算而不显式执行矩阵相乘。

图 1 BookViewer.xaml 的内容区域

<Grid x:Name="LayoutRoot">
  <toolkit:GestureService.GestureListener>
    <toolkit:GestureListener GestureBegin="OnGestureListenerGestureBegin"
                             GestureCompleted="OnGestureListenerGestureCompleted"
                             Tap="OnGestureListenerTap"
                             Hold="OnGestureListenerHold"
                             Flick="OnGestureListenerFlick"
                             DragStarted="OnGestureListenerDragStarted"
                             DragDelta="OnGestureListenerDragDelta"
                             DragCompleted="OnGestureListenerDragCompleted"
                             PinchStarted="OnGestureListenerPinchStarted"
                             PinchDelta="OnGestureListenerPinchDelta"
                             PinchCompleted="OnGestureListenerPinchCompleted" />
  </toolkit:GestureService.GestureListener>
     
  <Grid Name="manipulationGrid" CacheMode="BitmapCache">
    <Border Name="pageContainer0" Style="{StaticResource pageContainerStyle}">
      <Border Name="pageHost0" Style="{StaticResource pageHostStyle}" />
    </Border>
     
    <Border Name="pageContainer1" Style="{StaticResource pageContainerStyle}">
      <Border Name="pageHost1" Style="{StaticResource pageHostStyle}" />
    </Border>
     
    <Border Name="pageContainer2" Style="{StaticResource pageContainerStyle}">
      <Border Name="pageHost2" Style="{StaticResource pageHostStyle}" />
    </Border>
 
    <Grid.RenderTransform>
      <TransformGroup x:Name="transformGroup">
        <MatrixTransform x:Name="matrixTransform" />
        <ScaleTransform x:Name="scaleTransform" />
        <TranslateTransform x:Name="translateTransform" />
      </TransformGroup>
    </Grid.RenderTransform>
  </Grid>
 
<!-- Scratch pad transforms -->
<Grid Width="0" Height="0">
  <Grid.RenderTransform>
    <TransformGroup x:Name="calcTransformGroup">
      <MatrixTransform x:Name="calcMatrixTransform" />
      <ScaleTransform x:Name="calcScaleTransform" />
      <TranslateTransform x:Name="calcTranslateTransform" />
    </TransformGroup>
  </Grid.RenderTransform>
</Grid>
</Grid>

BookViewer.xaml 的代码隐藏文件 BookViewer.xaml.cs,但要防止文件变得太臃肿不堪,我创建了专门为 11 触摸笔势的 11 处理程序名为 BookViewer.Gestures.cs 的第二个代码隐藏文件。

以前我几个布尔字段用来帮助解释复杂组合的笔势。 此程序切换到枚举。 图 2 显示 ViewerTouchMode 和 ViewerDisplayMode 的枚举值。 这些帮助笔势事件处理程序保留什么直接发生的情况和避免冲突。

图 2 ViewerTouchMode 和 ViewerDisplayMode 枚举

public enum ViewerTouchMode
{
  Reading = 0,
  Dragging,
  Selecting,
  Pinching,
  Panning,
  Animating,
}
 
public enum ViewerDisplayMode
{
  Normal = 0,
  Zoomed
}

例如,页面过渡效果开头分路器、 拖动或轻击笔势的用户,但随动画的结束。 如果任何笔势开始时的当前触摸模式是 ViewerTouchMode.Animating,则将忽略整个笔势。 保持笔势的处理程序检查 textblock) 是否下手指。 如果是这样,当前的触摸模式将成为 ViewerTouchMode.Selecting,直到手指将从屏幕。 当触摸模式选择,拖动笔势扩展所选内容。

如果没有手指触摸屏幕,当前的触摸模式是 ViewerTouchMode.Reading 或 ViewerTouchMode.Animating,并在动画结束时,触摸模式将切换到 ViewerTouchMode.Reading。 除了从 Animating 值时,触摸模式只属于当前正在进行的笔势。

但是,用户可以捏变得大些,并从屏幕上,删除所有手指在屏幕和屏幕应予放大。 然后用户可以执行另一个常客操作仍放大或缩小,使屏幕,再加上拖动操作来移动它。 这是为什么 ViewerDisplayMode 枚举是必需的。 例如,如果用户在屏幕上拖动手指,该按钮将变为页面过渡效果如果当前显示模式是 ViewerDisplayMode.Normal,但将成为 ViewerDisplayMode.Zoomed 的平移操作。

夹紧常客和平移

如果用户接触两个手指与屏幕,然后拉伸他相隔使显示器更大的手指,当前显示模式将从 ViewerDisplayMode.Normal 切换到 ViewerDisplayMode.Zoomed 中。 显示模式保持 ViewerDisplayMode.Zoomed,直到用户点击屏幕。 通过设置动画到其正常大小的屏幕并切换到 ViewerDisplayMode.Normal BookViewer 控件响应分路器。 或者,如果用户 pinches 屏幕并因此的缩放系数低于 1.05 合同它时,屏幕会自动还原到正常大小。

时的显示模式为准进行放大,任何其他挤压和平移操作与从先前的操作而加剧。 这是在三个分组转换的原因图 1。 在单个平移操作中,TranslateTransform 属性会被更改移动到网格相对于屏幕。 在单个常客操作中,获取涉及的 ScaleTransform 和 TranslateTransform。 笔势运行完毕之后,TransformGroup 表示的总的转换。 一个名为 ConsolidateTransform BookViewer.xaml.cs 中的短方法将此总转换传输到 MatrixTransform,并重新设置为默认值,以准备进行下一步的笔势操作设置的 TranslateTransform 和 ScaleTransform。 这种方式,MatrixTransform 积累所有常客和平移操作的效果。

我还发现限制常客的范围和平移操作所必需。 例如,它仅意义常客操作导致页面缩放系数大于 1。 没有理由让收缩为小于号以其正常大小的页面。 同样,它成为这样我们就可以看到"下面"页面为平移页面没有意义。 在页面的左边的缘不应出现屏幕和等效的其它三条边的左边缘的右侧。

在名为 ClampPinchAndPanTransforms,所示的 BookViewer.xaml.cs 方法中应用这些限制是图 3。 此方法无疑是该程序的最困难的部分之一。 它可以使用 BookViewer.xaml 中的其他三种转换为执行"便笺簿"矩阵变换计算。

图 3 ClampPinchAndPanTransforms 方法

void ClampPinchAndPanTransforms(
  double scale, double translateX, double translateY)
{
  // This is the matrix transform from previous operations.
calcMatrixTransform.Matrix = matrixTransform.Matrix;
 
  // Calculate scaling factor so it's always 1 or greater.
double totalScale = scale * matrixTransform.Matrix.M11;
  totalScale = Math.Max(1, totalScale);
  scale = totalScale / matrixTransform.Matrix.M11;
 
  // Set up properties for new scale matrix.
calcScaleTransform.CenterX = scaleTransform.CenterX;
  calcScaleTransform.CenterY = scaleTransform.CenterY;
  calcScaleTransform.ScaleX = scale;
  calcScaleTransform.ScaleY = scale;
 
  // Set up properties for new translation matrix.
calcTranslateTransform.X = translateX;
  calcTranslateTransform.Y = translateY;
 
  // Obtain the total matrix from the transform group.
Matrix totalMatrix = calcTransformGroup.Value;
 
  // Restict translation to original area of transformed element.
double totalTranslationX = totalMatrix.OffsetX;
  double totalTranslationY = totalMatrix.OffsetY;
 
  double clampedTranslationX =
    Math.Min(0,
      Math.Max((1 - totalMatrix.M11) * manipulationGrid.ActualWidth,
        totalTranslationX));
 
  double clampedTranslationY =
    Math.Min(0,
      Math.Max((1 - totalMatrix.M22) * manipulationGrid.ActualHeight,
        totalTranslationY));
 
  // Adjust translation factors.
translateX += clampedTranslationX - totalTranslationX;
  translateY += clampedTranslationY - totalTranslationY;
 
  // Set transforms.
scaleTransform.ScaleX = scale;
  scaleTransform.ScaleY = scale;
  translateTransform.X = translateX;
  translateTransform.Y = translateY;
}

左和右光标笔势正常启动页面过渡效果,并转到下一页或前一动画的实现。 对于此版本,我决定将向上或向下的笔势应插入一个书签在该页面上,但这些书签尚未尚未实现。

但光标笔势时的当前显示模式是 ViewerDisplayMode.Zoomed 的呢? 它看上去像光标笔势应移动相对于视区的页面。 我决定只是将缩放后的页移动到极端的左侧、 顶部、 右侧或底部的视区,具体情况取决于光标笔势的角度。 这是实现的一个属于我并不完全满意有关。 真正进行运动看起来如同页是发展的步伐移动,然后会放慢动画处理。

当用户按下一个手指放到屏幕单词上一秒,将移动该手指来扩展所选内容,然后将手指时,BookViewer 控件将引发一个 TextSelected 方法。 MainPage 通过显示页面顶部的一个小菜单处理此事件,如中所示图 4

A Zoomed Page with a Text-Selection Menu
图 4 缩放文本选择菜单页

尚未实现的第一个项目。 此功能将允许用户键入一点需要注意一下有关选定的段中。 这些注释将保存与文档设置。 最后一项只是消除菜单。 "Bing"项目使用的 Windows Phone 7 SearchTask 类来调用了电话的标准 Web 搜索应用程序。 其他两项可用于调用带有包含与选定文本的 
a 查询字符串的 URL 的 Internet Explorer WebBrowserTask。

为该词典,我最初希望使用 Bing 字典中,但在实践中似乎相当不稳定。 有时它似乎不像字典根本。 某些进一步探索后结束了与 Google 字典,提供了广泛似乎更适合我的需要的结果。

第二个想法

我曾提到过文本选择菜单上的"备注"选项未实现,以及要插入书签与光标笔势的功能。 不是所有尚未实现 ! 在图像中图4,在应用程序栏上的第一个按钮调出的章节列表。 一起工作的。 其他三个按钮最终将得到一个所有书签的列表,列表中的所有说明和一个对话框搜索书籍中的文本 — — 但尚未完全正确。

我已阅读"Bleak 房屋",因为我已经喜欢查找的单词或短语 Bing、 维基百科和 Google 字典中。 它是相当简单,以扩展页面,选择的单词或短语,然后点击该菜单项。

但是,我可以告诉已这是的选定文本的注释特征的最佳方法。 : 记下几乎始终是选定文本的长度至少为一个句子。 但是,一旦展开页面时,该句子的一部分可能将熄灭屏幕,并且当前没有要平移屏幕时进行选择的方法。

我想知道现在如果注释功能为所选文本应略有不同。 我想知道是否一个不同的文本选择方案无法自动启动是否不放大页面。 我想知道是否 unzoomed 页上的封存笔势应该选择整个句子,然后拖动应范围扩展到其他整个句子。

这就是软件最棒的一点: 没有以往任何时候都全力以赴。 始终是增强和改进的机会。

Charles Petzold  MSDN 杂志 *的长期特约编辑。*他的新书《Programming Windows Phone 7》(Microsoft Press,2010 年)可从 bit.ly/cpebookpdf 免费下载获得。

这要归功于以下的技术专家审阅这篇文章: Chipalo 街道