教程17:触摸和操纵
触摸和操纵
NoesisGUI使用户界面可以同时接收多个触摸。甲触摸是一种由触摸屏上的手指把生成的用户输入的。您可以在NoesisGUI中以以下三个不同级别来管理触摸事件,以下各节对此进行了解释:本机控件,触摸事件和操纵事件。
本机控件
ScrollViewer定义了PanningMode附加属性,该属性使您可以指定水平,垂直,两者或两者都不启用触摸平移。该PanningDeceleration属性指定滚动的速度减慢,当用户拿起从触摸屏的手指。所述PanningRatio附加属性指定的滚动偏移至平移操纵偏移的比率。
PanningMode可以直接在ScrollViewer上设置,也可以用作附加属性。当控制包含的ScrollViewer在其ControlTemplate中,PanningMode被用作附加属性来指定的行为的ScrollViewer在ControlTemplate中。当您在ControlTemplate之外使用ScrollViewer时,将在ScrollViewer上直接设置PanningMode。
以下示例创建一个ScrollViewer并向其中添加几个按钮。该示例将PanningMode设置为Both,以便用户可以使用手指水平和垂直滚动ScrollViewer。
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ScrollViewer HorizontalScrollBarVisibility="Auto" Height="250" Width="125" PanningMode="Both">
<StackPanel>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
<Button Width="150" Height="80">Push me if you dare</Button>
</StackPanel>
</ScrollViewer>
</Grid>
下一个示例创建一个TextBox并将PanningMode用作附加属性。它将PanningMode设置为VerticalOnly。这样,当用户在垂直移动手指之前水平移动手指时,文本将突出显示。
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox Height="300" Width="300" Name="textBox1" TextWrapping="Wrap" IsReadOnly="True"
ScrollViewer.PanningMode="VerticalOnly" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
Hello world 1 Hello world 2 Hello world 3 Hello world 4 Hello world 5
Hello world 6 Hello world 7 Hello world 8 Hello world 9 Hello world 10
Hello world 11 Hello world 12 Hello world 13 Hello world 14 Hello world 15
Hello world 16 Hello world 17 Hello world 18 Hello world 19 Hello world 20
Hello world 21 Hello world 22 Hello world 23 Hello world 24 Hello world 25
Hello world 26 Hello world 27 Hello world 28 Hello world 29 Hello world 30
Hello world 31 Hello world 32 Hello world 33 Hello world 34 Hello world 35
Hello world 36 Hello world 37 Hello world 38 Hello world 39 Hello world 40
Hello world 41 Hello world 42 Hello world 43 Hello world 44 Hello world 45
Hello world 46 Hello world 47 Hello world 48 Hello world 49 Hello world 50
Hello world 51 Hello world 52 Hello world 53 Hello world 54 Hello world 55
Hello world 56 Hello world 57 Hello world 58 Hello world 59 Hello world 60
Hello world 61 Hello world 62 Hello world 63 Hello world 64 Hello world 65
Hello world 66 Hello world 67 Hello world 68 Hello world 69 Hello world 70
Hello world 71 Hello world 72 Hello world 73 Hello world 74 Hello world 75
Hello world 76 Hello world 77 Hello world 78 Hello world 79 Hello world 80
Hello world 81 Hello world 82 Hello world 83 Hello world 84 Hello world 85
Hello world 86 Hello world 87 Hello world 88 Hello world 89 Hello world 90
Hello world 91 Hello world 92 Hello world 93 Hello world 94 Hello world 95
Hello world 96 Hello world 97 Hello world 98 Hello world 99 Hello world 100
Hello world 101 Hello world 102 Hello world 103 Hello world 104 Hello world 105
Hello world 106 Hello world 107 Hello world 108 Hello world 109 Hello world 110
Hello world 111 Hello world 112 Hello world 113 Hello world 114 Hello world 115
Hello world 116 Hello world 117 Hello world 118 Hello world 119 Hello world 120
Hello world 121 Hello world 122 Hello world 123 Hello world 124 Hello world 125
Hello world 126 Hello world 127 Hello world 128 Hello world 129 Hello world 130
Hello world 131 Hello world 132 Hello world 133 Hello world 134 Hello world 135
Hello world 136 Hello world 137 Hello world 138 Hello world 139 Hello world 140
Hello world 141 Hello world 142 Hello world 143 Hello world 144 Hello world 145
Hello world 146 Hello world 147 Hello world 148 Hello world 149 Hello world 150
Hello world 151 Hello world 152 Hello world 153 Hello world 154 Hello world 155
Hello world 156 Hello world 157 Hello world 158 Hello world 159 Hello world 160
Hello world 161 Hello world 162 Hello world 163 Hello world 164 Hello world 165
Hello world 166 Hello world 167 Hello world 168 Hello world 169 Hello world 170
Hello world 171 Hello world 172 Hello world 173 Hello world 174 Hello world 175
Hello world 176 Hello world 177 Hello world 178 Hello world 179 Hello world 180
Hello world 181 Hello world 182 Hello world 183 Hello world 184 Hello world 185
Hello world 186 Hello world 187 Hello world 188 Hello world 189 Hello world 190
Hello world 191 Hello world 192 Hello world 193 Hello world 194 Hello world 195
Hello world 196 Hello world 197 Hello world 198 Hello world 199 Hello world 200
</TextBox>
</Grid>
触摸事件
UIElement定义了您可以订阅的以下事件,以便您的应用程序可以响应触摸:
- TouchDown:当手指悬停在元素上方时,手指触摸屏幕时发生。
- TouchMove:当手指在某个元素上并在屏幕上移动时发生。
- TouchUp:当手指悬停在某个元素上方时,将手指从屏幕上移开时,会发生TouchUp。
- TouchEnter:当触摸从元素边界的外部移到内部时发生。
- TouchLeave:当触摸从元素边界的内部移动到外部时发生。
- PreviewTouchDown:TouchDown的预览事件。
- PreviewTouchMove:TouchMove的预览事件。
- PreviewTouchUp:TouchUp的预览事件。
- GotTouchCapture:当触摸被捕获到元素时发生。
- LostTouchCapture:当此元素失去触摸捕获时发生。
请注意,每次触摸的坐标均在屏幕空间中给出。必须使用Visual :: PointFromScreen方法将它们转换为本地坐标。
与键盘和鼠标事件一样,触摸事件是路由事件。名称以Preview开头的事件是隧道事件,而以Touch开头的事件是冒泡事件。顺序如下:
- 所述TouchEnter在事件发生时,当用户将手指的元件上的一个时间。
- 在TouchDown的事件发生一次。
- 当用户在元素内移动手指时,TouchMove事件会发生多次。
- 的触上事件发生时,当用户从元件提起手指一次。
- 该TouchLeave事件发生一次。
当使用两个以上的手指时,每个手指都会发生事件。
注意
在NoeisGUI中,如果未处理触摸事件,则会将其提升为鼠标事件。
操作事件
对于应用程序使用户能够操纵对象的情况,UIElement类定义操纵事件。与仅报告触摸位置的触摸事件不同,操纵事件报告如何解释输入。共有三种类型的操纵:平移,扩展和旋转。下表描述了如何调用三种类型的操作:
- 将手指放在一个对象上,然后在触摸屏上移动手指以调用翻译操作。这将移动对象。
- 将两个手指放在一个对象上,然后将手指移近或移开以调整对象的大小。
- 将两个手指放在一个对象上,然后使手指彼此围绕旋转以进行旋转操作。这将旋转对象。
UIElement定义了以下操作路由事件:
- ManipulationStarting:第一次创建操纵处理器时发生。
- ManipulationStarted:当输入设备开始对UIElement对象进行操纵时发生。
- ManipulationDelta:在操作期间输入设备更改位置时发生。
- ManipulationInertiaStarting:当输入设备在操作过程中与UIElement对象失去联系时发生。
- ManipulationCompleted:当对UIElement对象的操纵和惯性完成时,发生。
默认情况下,不会生成这些操作事件。要在UIElement上接收操纵事件,请在XAML中或通过代码将依赖项属性IsManipulationEnabled设置为true。
注意
如果使用了TouchDown事件(操作的成员设置为True),则操作逻辑不会生成操作事件。
操作开始
该ManipulationStarting当用户将手指在对象上发生的事件。除其他事项外,此事件使您可以设置ManipulationContainer属性。在随后的事件中,操纵的位置将相对于此ManipulationContainer。您还可以设置Mode属性,以指示可能的操作类型(平移,旋转或缩放)。
操作开始
将在ManipulationStarted发生旁事件ManipulationStarting。此事件报告操纵的来源。
操作三角
当用户的手指在触摸屏上移动时,会发生多次ManipulationDelta事件。ManipulationDeltaEventArgs类的DeltaManipulation属性报告该操作是解释为运动,扩展还是平移。在这里,您可以执行大部分操作对象的工作。请注意,ManipulationDelta发生在ManipulationInertiaStarting事件之前和之后。ManipulationDeltaEventArgs.isInertial属性报告在惯性期间是否发生ManipulationDelta事件,因此您可以检查该属性并根据其值执行不同的操作。
操纵惯性开始
将在ManipulationInertiaStarting当用户的手指失去与物体发生接触的事件。此事件使您可以指定惯性期间操纵的减速度。这样,如果您选择,您的对象可以模拟不同的物理空间或属性。例如,假设您的应用程序有两个对象,它们代表物理世界中的项目,而一个对象比另一个重。您可以使较重的对象比较轻的对象减速更快。
操作完成
将在ManipulationCompleted发生事件时的操作和任何惯性结束。也就是说,在所有ManipulationDelta事件发生之后,此事件表示操作已完成。
注意
您可以通过在任何操作事件中在事件参数上设置cancel属性来取消操作。设置为true时,不再引发操纵事件,并且针对已取消的操纵发生鼠标事件。
样品
C ++
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyTouch">
<Canvas x:Name="root">
<Rectangle Fill="Red" Width="200" Height="200" RenderTransform="1 0 0 1 50 50"
IsManipulationEnabled="True"/>
<Rectangle Fill="Blue" Width="200" Height="200" RenderTransform="1 0 0 1 200 300"
IsManipulationEnabled="True"/>
</Canvas>
</Grid>
class MyTouch: public Grid
{
public:
void OnManipulationStarting(const ManipulationStartingEventArgs& e)
{
e.mode = ManipulationModes_All;
e.manipulationContainer = (Visual*)FindName("root");
e.handled = true;
}
void OnManipulationInertiaStarting(const ManipulationInertiaStartingEventArgs& e)
{
e.translationBehavior.desiredDeceleration = 100.0f / (1000.0f * 1000.0f);
e.rotationBehavior.desiredDeceleration = 360.0f / (1000.0f * 1000.0f);
e.expansionBehavior.desiredDeceleration = 300.0f / (1000.0f * 1000.0f);
e.handled = true;
}
void OnManipulationDelta(const ManipulationDeltaEventArgs& e)
{
UIElement* rectangle = (UIElement*)e.source;
MatrixTransform* tr = (MatrixTransform*)rectangle->GetRenderTransform();
Transform2f mtx = tr->GetMatrix();
mtx.RotateAt(e.deltaManipulation.rotation * DegToRad_f, e.manipulationOrigin.x,
e.manipulationOrigin.y);
mtx.ScaleAt(e.deltaManipulation.scale, e.deltaManipulation.scale,
e.manipulationOrigin.x, e.manipulationOrigin.y);
mtx.Translate(e.deltaManipulation.translation.x, e.deltaManipulation.translation.y);
tr->SetMatrix(mtx);
e.handled = true;
}
private:
NS_IMPLEMENT_INLINE_REFLECTION(MyTouch, Grid)
{
NsMeta<TypeId>("MyTouch");
}
};
Unity
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas x:Name="root">
<Rectangle Fill="Red" Width="200" Height="200" RenderTransform="1 0 0 1 50 50"
IsManipulationEnabled="True"/>
<Rectangle Fill="Blue" Width="200" Height="200" RenderTransform="1 0 0 1 200 300"
IsManipulationEnabled="True"/>
</Canvas>
</Grid>
public class Touch: MonoBehaviour
{
Grid _root;
void Start()
{
_root = (Grid)GetComponent<NoesisView>().Content;
_root.ManipulationStarting += this.ManipulationStarting;
_root.ManipulationInertiaStarting += this.ManipulationInertiaStarting;
_root.ManipulationDelta += this.ManipulationDelta;
}
void ManipulationStarting(object sender, ManipulationStartingEventArgs e)
{
e.Mode = Noesis.ManipulationModes.All;
e.ManipulationContainer = (UIElement)_root.FindName("root");
e.Handled = true;
}
void ManipulationInertiaStarting(object sender, ManipulationInertiaStartingEventArgs e)
{
e.TranslationBehavior.DesiredDeceleration = 100.0f / (1000.0f * 1000.0f);
e.RotationBehavior.DesiredDeceleration = 360.0f / (1000.0f * 1000.0f);
e.ExpansionBehavior.DesiredDeceleration = 300.0f / (1000.0f * 1000.0f);
e.Handled = true;
}
void ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
var rectangle = (Rectangle)e.Source;
var transform = (MatrixTransform)rectangle.RenderTransform;
var matrix = transform.Matrix;
float rotation = e.DeltaManipulation.Rotation * Mathf.Deg2Rad;
float originX = e.ManipulationOrigin.X;
float originY = e.ManipulationOrigin.Y;
float scale = e.DeltaManipulation.Scale;
float translationX = e.DeltaManipulation.Translation.X;
float translationY = e.DeltaManipulation.Translation.Y;
matrix.RotateAt(rotation, originX, originY);
matrix.ScaleAt(scale, scale, originX, originY);
matrix.Translate(translationX, translationY);
transform.Matrix = matrix;
e.Handled = true;
}
}