在使用手机时,少不了使用手势了,像滑动,缩放,长按等,
那么如何在Delphi开发的APP中使用手势呢?
Delphi自带有示例,在下面这个目录中,
可以看到ImageRotation图片旋转,ImageZoom图片缩放,TapAndHold长按这三个示例
我们打开图片缩放的示例ImageZoom来学习一下如何使用手势
这个示例的窗口中间放了一个Image控件
要缩放,就得让窗体响应缩放的事件,
这个设置不在Image控件上,而是在窗体Touch属性中,
展开窗体的Touch属性,
可以看到Delphi的窗体直接支持如下手势:
Zoom:缩放
Pan:平移
Rotate:旋转
TwoFingerTap:两指点按
PressAndTap:按下并点击
LongTap:长按
DoubleTap:连接点两下
如果你想让窗体响应哪些手势,勾上就行,
这个示例窗体勾上了Zoom手势,
表示它只需要处理缩放手势
那么在哪个事件里进行处理呢?
窗体有个OnGesture事件:
事件的定义:
TGestureEvent = procedure(Sender: TObject; const EventInfo: TGestureEventInfo;
var Handled: Boolean) of object;
参数EventInfo里面包含手势的相关信息,
TGestureEventInfo = record
//手势ID,代表什么手势
GestureID: TGestureID;
//触摸的位置
Location: TPointF;
//手势的状态
Flags: TInteractiveGestureFlags;
//旋转的角度
Angle: Double;
//惯性的矢量,在手势操作的时候,
//比如平移手势Pan,需要根据这个矢量来计算页面能在水平和垂直方向惯性滚动多长的距离
InertiaVector: TPointF;
//缩放的距离
Distance: Integer;
//点按的位置
TapLocation: TPointF;
end;
TGestureID这个类型定义在System.UITypes单元,
表示手势的类型,很奇怪为什么没有用枚举类型:
const
// Interactive gesture id's (maps to Windows 7's WM_GESTURE)
igiBegin = 1 + igiFirst;
igiEnd = 2 + igiFirst;
igiZoom = 3 + igiFirst;
igiPan = 4 + igiFirst;
igiRotate = 5 + igiFirst;
igiTwoFingerTap = 6 + igiFirst;
igiPressAndTap = 7 + igiFirst;
// Extra interactive gestures
igiLongTap = 8 + igiFirst;
igiDoubleTap = 9 + igiFirst;
TInteractiveGestureFlags 则定义在FMX.Types单元,
标志手势的状态,是手势开始、进行中还是结束了:
TInteractiveGestureFlag = (gfBegin, gfInertia, gfEnd);
TInteractiveGestureFlags = set of TInteractiveGestureFlag;
看下官方的示例中实现图片缩放的代码:
运行看下效果:
官方示例只是简单的对图片进行居中缩放,
但是我们在实际使用中,我们通常都是需要对任意一点进行缩放,
比如下面这张图片,
我想对她头上的花进行放大,看看到底是什么花,
我将两指对准那朵花进行放大,但是花的位置却上移,
我用官方示例查看的效果:
并且图片控件放大了之后,超出了屏幕的显示区域,超出的区域不能移动,都看不到了。
那么我们来试着完善下这个示例,
需要完善以下两点:
- 图片超出了屏幕显示区域,要能滑动查看被挡住的部分。
- 缩放的时候以两根手指的中心点为基点,这个点上不能偏移,比如我想放大美女头上那朵花,将手指放在花的两边进行缩放,缩放的时候,这朵花得一直在屏幕原来的位置上放大,我想要的效果如下,这是我用QQ查看这张图片时缩放的效果:
先来解决第一个问题,
很简单,将图片控件放到一个ScrollBox里面就行了
放大之后可以滑动了
再来解决第二个问题
先要知道我们两指缩放时的那两个点,
窗体有一个OnTouch事件,
TTouchEvent = procedure(Sender: TObject; const Touches: TTouches; const Action: TTouchAction) of object;
它是一个多点触摸事件,通过它我们能知道当前有几个手指摸在窗体上,
事件中的Touches参数是一个点的数组,会返回当前的触摸点,
如果Touches的长度为2,就表示有两根手指触摸在屏幕上,
在缩放的时候这两个点就是两根手指所触摸的位置,
我们定义两个全局变量,将这两个点保存起来,
var
gFormTouch1:TPointF;
gFormTouch2:TPointF;
我们在对图片控件中心点进行缩放的时候,
比如放大20个像素,
如果仅给图片控件的宽度和高度都加上20,那么中心点就会偏移了,
如下图所示:
那么怎么保持中心点不变呢?
只需要左边和右边各延伸一半,即图片.Left要减10,右边的Right要加10,
如下图:
如果缩放点不是控件的中心点,而是图片控件的1/3处,
那么只需要图片.Left减去20个像素的1/3,图片控件的Right向右再延伸20个像素的2/3,
那么缩放中心点也不会偏移,
如下图所示:
所以缩放时中心点不偏移的公式:
控件位置=控件位置-缩放尺寸*缩放中心点位置的比例
比如控件位置Left为10,缩放了20个像素,缩放中心点在图片控件的1/3处,
那么首先控件宽度要加上20,
然后控件的Left要减去20*1/3(约7),就变成了3
在手势缩放开始的时候,通过触摸的两个点,计算出缩放中心点,
这两点目前是基于窗体的坐标,要先转换成相对于图片控件的坐标,
所以还需要一些变量:
//图片控件上的两个触摸点
gImageControlTouch1:TPointF;
gImageControlTouch2:TPointF;
//图片控件上的两个触摸点的中心点
gImageControlTouchCenter:TPointF;
//中心点占图片宽度的百分比
gImageControlTouchCenterWidthRate:Double;
//中心点占图片高度的百分比
gImageControlTouchCenterHeightRate:Double;
先做开始缩放的准备,
代码如下:
在缩放的时候,保持缩放中心点的物体不偏移:
代码如下:
因为每次生成到Android手机再调试有点慢,
所以我先在Windows平台测试,
放一个按钮,直接调用FormGesture事件来模拟手势缩放,
先找到花相对于图片控件的坐标,是(120,45)
先模拟缩放开始,让它计算出中心点:
并且为了直观的展示缩放中心点的物体不产生偏移,我将它这个点的位置画出来:
再放个按钮,点击它,模拟缩放的过程:
先在Windows下运行试试:
先点"模拟缩放开始"按钮,可以看到缩放中心在那朵花上,
点击"模拟缩放进行"按钮:
可以看到,图片放大了之后,这红点的位置没有发生偏移,
并且红点所在的还是在这朵花的位置,
然后在手机上模拟一下:
手势的处理大致就是这么一个流程了,
大家有兴趣可以自己体验一下。