当前位置: 首页 > 科技观察

VelocityDetector_0

时间:2023-03-18 13:44:48 科技观察

forHarmonyOS自定义控件更多信息请访问:https://harmonyos.51cto.com,与华为官方共同打造的鸿蒙技术社区。一般涉及到滚动场景,我们都会用于速度检测。比如列表滑动时,我们需要获取手指抬起做惯性滚动时的瞬时速度。再比如在滚动翻页的时候,我们需要根据手指的移动速度来判断是翻到下一页还是保持当前页面。接下来我们就来看看如何在HarmonyOS中使用VelocityDetector。使用方法VelocityDetector使用起来比较简单,主要分为以下几个步骤:获取VelocityDetector实例添加TouchEvent到VelocityDetector计算速度获取计算速度并清除添加的事件获取实例通过obtainInstance函数获取实例:VelocityDetectordetector=VelocityDetector.obtainInstance();添加TouchEvent在控件的TouchEventListener中调用addEvent函数:component.setTouchEventListener(newTouchEventListener(){@OverridepublicbooleanonTouchEvent(Componentcomponent,TouchEventev){detector.addEvent(ev);returntrue;}});计算速度一般情况下,我们需要计算手指抬起时的速度,因为我们需要的是手指抬起后的速度值。所以我们可以调用calculateCurrentVelocity函数计算TouchEvent.PRIMARY_POINT_UP时的速度:staticfinalintMAX_VELOCITY=10000;@OverridepublicbooleanonTouchEvent(Componentcomponent,TouchEventev){detector.addEvent(ev);if(ev.getAction()==TouchEvent.PRIMARY_POINT_UP){calculateCurrentVelocity(1000,MAX_VELOCITY,MAX_VELOCITY);}returntrue;}calculateCurrentVelocity函数有两个重载:voidcalculateCurrentVelocity(intunits);voidcalculateCurrentVelocity(intunits,floatmaxVxVelocity,floatmaxVyVelocity)其中:units代表单位,10pixels/ms,1代表秒,等等。一般我们传1000,得到的速度代表手指每秒移动了多少像素。maxVxVelocity是最大水平速度。比如在惯性滚动的时候,如果我们不想滚动的太快,我们可以设置一个最大速度maxVyVelocity作为垂直方向的最大速度。多少,比如惯性滚动,如果我们不想滚动太快,我们可以设置一个最大速度来获取速度,计算速度后直接获取速度值:floatvelocityY=detector.getVerticalVelocity();floatvelocityX=detector.getHorizo??ntalVelocity();//或者获取速度数组,下标0为水平速度,下标1为垂直速度float[]velocity=detector.getVelocity();得到的速度可能是正值也可能是负值,正值和负值代表速度的方向,大家可以自己用日志做实验。清除最后,我们需要清除之前添加的TouchEvents,为新一轮的事件做准备,防止旧的TouchEvents影响后续的速度计算。这里我们可以在获取速度后或在CANCEL事件中调用clear函数:if(ev.getAction()==TouchEvent.PRIMARY_POINT_UP){...float[]velocity=detector.getVelocity();...detector。clear();}if(ev.getAction()==TouchEvent.CANCEL){detector.clear();}总结VelocityDetector目前只能获取单指的速度,多指的情况暂时不能使用-touch获取其他手指的速度。至此,我们就得到了手指抬起时的速度。至于这个速度如何使用,会在惯性滚动相关的文章中介绍。接下来我们分析一下VelocityDetector到底出了什么问题。Question首先了解一下VelocityDetector的基本原理:我们通过addEvent将TouchEvent传递给VelocityDetector,然后通过calculateCurrentVelocity计算速度。在这个过程中,VelocityDetector基本上是通过TouchEvent获取到手指的坐标,然后移动距离和时间来计算速度。当然,内部算法远比这复杂,但我们只需要记住一个关键变量:移动距离。TouchEvent有两个获取手指坐标计算距离的函数:getPointerPosition和getPointerScreenPosition。VelocityDetector使用哪一个?我们可以用下面的代码进行实验:TAG+"getIndex");returnevent.getIndex();}@OverridepublicMmiPointgetPointerPosition(inti){System.out.println(TAG+"getPointerPosition");returnevent.getPointerPosition(i);}@OverridepublicMmiPointgetPointerScreenPosition(inti){System.out.println(TAG+"getPointerScreenPosition");returnevent.getPointerScreenPosition(i);}......};}手指移动过程中,log如下:08-0417:14:09.29624871-24871/com.ryan.ohos.parallaxlayoutISystem.out:ParallaxLayoutTouchEvent:getIndex08-0417:14:09.29624871-24871/com.ryan.ohos.parallaxlayoutISystem.out:ParallaxLayoutTouchEvent:getPointerPosition08-0417:14:09.29724871/com4ryan-com4parallaxlayoutISystem.out:ParallaxLayoutTouchEvent:getIndex08-0417:1个4:09.29724871-24871/com.ryan.ohos.parallaxlayoutISystem.out:ParallaxLayoutTouchEvent:getPointerPosition08-0417:14:09.46924871-24871/com.ryan.ohos.parallaxlayoutISystem.out:ParallaxLayoutTouchEvent:getVoretIndex....答案显而易见,使用getPointerPositiongetPointerPosition得到的坐标是相对于父控件的,不是屏幕的左上角。根据getPointerPosition的描述,我们有理由猜测:当被监控的控件在手指移动过程中不断改变位置时,那么通过getPointerPosition获取的手指坐标会加上控件的位移,导致滑动距离计算偏离预期.让我们试验一下。在父布局中,子控件监听触摸事件,通过getPointerPosition获取手指坐标并计算MOVE和DOWN中的坐标差值,通过setComponentPosition和坐标差值改变子控件的位置。然后我们打印getPointerPosition得到的y坐标,getPointerScreenPosition得到的y坐标,以及移动的距离。代码如下:开关(ev.getAction()){caseTouchEvent.PRIMARY_POINT_DOWN:downY=y;down??ScreenY=screenY;break;caseTouchEvent.POINT_MOVE:floatdeltaY=y-downY;floatdeltaScreenY=screenY-downScreenY;System.out.println(TAG+"y:"+y+"screenY:"+screenY+",deltaY:"+deltaY+"deltaScreenY"+deltaScreenY);moveChildren((int)deltaY);break;}returntrue;}privatevoidmoveChildren(intdeltaY){child.setComponentPosition(0,top+deltaY),child.getWidth(),top+deltaY+child.getHeight());}日志如下:y:1206.0348,screenY:1905.0348,deltaY:-13.782349,deltaScreenY:-13.782349y:1095.856,screenY:1781.856,deltaY:-123.96118,deltaScreenY:-136.96118y:1204.7794,screenY:1780.7794,deltaY:-15.03772,deltaScreenY:-138.03772y:1041.5786,screenY:1725.5786,deltaY:-178.23853,deltaScreenY:-193.23853y:1094.1056,screenY:1615.1056,deltaY:-125.71155,deltaScreenY:-303.71155y:972.12244,screenY:1546.1224,deltaY:-247.6947,deltaScreenY:-372.6947y:1066.4863,screenY:1518.4863,deltaY:-153.33081,deltaScreenY:-400.3308y:917.2855,screenY:1463.2855,deltaY:-302.53162,deltaScreenY:-455.53162y:1024.8671,screenY:1421.8671,deltaY:-194.95007,deltaScreenY:-496.95007y:875.4486,screenY:1380.4486,deltaY:-344.36853,deltaScreenY:-538.3685y:941.0178,screenY:1296.0178,deltaY:-278.79932,deltaScreenY:-622.7993y:821.4109,screenY:1242.4109,deltaY:-398.40625,deltaScreenY:-676.40625y:883.01855,screenY:1184.0186,deltaY:-336.79858,deltaScreenY:-734.7986y:817.2832,screenY:1180.2832,deltaY:-402.53394,deltaScreenY:-738.53394y:834.93787,screenY:1131.9379,deltaY:-384.87927,deltaScreenY:-786.8793可以发现getPointerPosition计算出来的deltaY不是线性增加而是突然变大变小了,对比一下通过getPointerScreenPosition计算deltaScreenY,可以发现deltaY等于deltaScreenY减去最后的deltaY也证明通过getPointerPosition得到的手指坐标会加上控件的位移。那么这对VelocityDetector有什么影响呢?VelocityDetector计算速度的一个重要因素是距离。在这种情况下,距离或大或小,都会导致速度的计算值低于正常速度,甚至出现正负值。不一样。综上所述:当一个控件在控件的touch事件内改变相对于父控件的位置时,VelocityDetector获取的速度就会有误差。能影响控件位置的函数有setTop(setTop在实验中未能改变控件的位置,不知道为什么)、setContentPosition、setComponentPosition,甚至还有setTranslationY和setTranslationX。而如果父控件在控件的触摸事件中改变了位置,也会出现这个问题。这样的话,触摸事件中计算距离的问题就很好解决了,不用getPointerPosition,直接用getPointerScreenPosition就可以了。但是如何解决VelocityDetector的问题呢?两种方式:代理方式和偏移方式。代理方法内部通过一个TouchEventProxy维护一个TouchEvent,并将其getPointerPosition实现转发给TouchEvent的getPointerScreenPosition。publicclassTouchEventProxyextendsTouchEvent{privateTouchEventevent;publicvoidsetEvent(TouchEventevent){this.event=event;}@OverridepublicintgetAction(){returnevent.getAction();}@OverridepublicintgetIndex(){returnevent.getIndex();}@OverridepubliclonggetStartTime(){returnevent.getStartTime();}@OverridepublicintgetPhase(){returnevent.getPhase();}@OverridepublicMmiPointgetPointerPosition(inti){//转发至getPointerScreenPositionreturnevent.getPointerScreenPosition(i);}@OverridepublicvoidsetScreenOffset(floatv,floatv1){event.setScreenOffset(v,v1);}@OverridepublicMmiPointgetPointerScreenPosition(inti){returnevent.getPointerScreenPosition(i);}@OverridepublicintgetPointerCount(){returnevent.getPointerCount();}@OverridepublicintgetPointerId(inti){returnevent.getPointerId(i);}@OverridepublicfloatgetForce(inti){returnevent.getForce(i);}@OverridepublicfloatgetRadius(inti){returnevent.getRadius(i);}@OverridepublicintgetSourceDevice(){returnevent.getSourceDevice();}@OverridepublicStringgetDeviceId(){returnevent.getDeviceId();}@OverridepublicintgetInputDeviceId(){returnevent.getInputDeviceId();}@OverridepubliclonggetOc??curredTime(){returnevent.getOc??curredTime();}}使用起来也很简单:TouchEventProxyvProxyonproxy=newTouchEvent(Componentcomponent,TouchEventev){proxy.setEvent(ev);detector.addEvent(proxy);...returntrue;}位移方法通过反射TouchEvent发现它包含一个可以设置偏移量的函数,该函数会影响getPointerPosition的值,那么我们可以比较一下触摸事件中getPointerPosition和getPointerScreenPosition的区别,通过函数设置偏移量,强制同步坐标。这里只提供位移法可行且经过验证的思路,大家可以自行尝试代码。比较既然有解决速度问题的方法,那么我们就可以比较一下修复前后的速度,看看有多少差异。我们定义两个VelocityDetector实例,一个添加代理,一个添加原始事件,然后同时获取速度看:VelocityDetectordetector1=VelocityDetector.obtainInstance();VelocityDetectordetector2=VelocityDetector.obtainInstance();TouchEventProxyproxy=newTouchEventProxy();@OverridepublicbooleanonTouchEventComponentcomponent,TouchEventev){proxy.setEvent(ev);detector1.addEvent(ev);detector2.addEvent(proxy);floaty=getY(ev);switch(ev.getAction()){caseTouchEvent.PRIMARY_POINT_DOWN:downY=y;中断;caseTouchEvent.POINT_MOVE:floatdeltaY=y-downY;moveChildren((int)deltaY);中断;caseTouchEvent.PRIMARY_POINT_UP:detector1.calculateCurrentVelocity(1000);detector2.calculateCurrentVelocity(1000);System.out.println(TAG+“检测”+detector1.getVerticalVelocity()+",detector2:"+detector2.getVerticalVelocity());detector1.clear();detector2.clear();break;}returntrue;}快速向上滑动:08-0509:36:29.0041846-1846/?ISystem.out:ParallaxLayoutTouchEvent:detector1:-5332.0,detector2:-9285。慢慢向上滑动:08-0509:35:39.0651846-1846/?ISystem.out:ParallaxLayoutTouchEvent:detector1:-1003.0,detector2:-3560.0先慢下来最后快上滑:08-0509:37:04.0661846-1846/?ISystem.out:ParallaxLayoutTouchEvent:detector1:-4176.0,detector2:-4491.0快速滑动:08-0509:39:44.7851846-1846/?ISystem.out:ParallaxLayoutTouchEvent:detector1:1955.0,detector2:6660.0慢速滑动:08-0509:40:32.8131846-1846/?ISystem.out:ParallaxLayoutTouchEvent:detector2:6660.0detector1:907.0,detector2:3835.0先减速后快滑:08-0509:39:15.7391846-1846/?ISystem.out:ParallaxLayoutTouchEvent:detector1:-784.0,detector2:1937.0综上所述,可以发现在向上滑动的过程中取的样本越少(速度慢的时候突然变快),两个速度比较接近,但是在滑动的过程中,如果速度慢一些,甚至会得到一个相反方向的速度https://harmonyos.51cto.com