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

Harmonyos开源第三方组件-自定义图片缩放组件PinchImageView-ohos

时间:2023-03-15 22:20:30 科技观察

更多信息请访问:与华为官方共建的Harmonyos技术社区https://harmonyos.51cto.com前言基于Android平台手势控制组件PinchImageView-ohos(https://github.com/boycy815/PinchImageView)实现了鸿蒙的功能迁移和重构。代码已经开源(https://gitee.com/isrc_ohos/pinch-image-view-ohos),欢迎广大开发者提出宝贵意见。背景PinchImageView-ohos是一个支持多点触控的ImageView手势控制组件。通过识别单指双击、双指捏合、单指滑动等手势指令,实现图片放大、缩小、滑动效果。该组件功能丰富,使用方便,广泛应用于各种图片预览应用。组件效果展示1、两指相对或相对捏合,实现图片的缩放变化。图1.双指捏合效果2.单指双击放大缩小图片。图2双击效果3.单指双击,再单指移动,可以放大和平移图片。图3单指双击后的单指移动效果Sample分析Sample部分主要负责整体显示布局的构建。首先为PinchImageView-ohos组件设置显示图片,然后将组件对象添加到显示布局中。下面将详细介绍各组件的使用。步骤1.创建整体显示布局。Step2.导入相关类并实例化PinchImageView-ohos组件对象。Step3.设置显示图片。Step4.将PinchImageView-ohos组件对象添加到整体显示布局中。//Step1创建整体显示布局DirectionalLayoutdirectionalLayout=newDirectionalLayout(this);//Step2导入相关类并实例化对象PinchImageViewnewpinchImageView=newPinchImageViewnew(this);//Step3设置显示图像pinchImageView.setPixelMap(this,ResourceTable.Media_1111);//Step4在整体显示布局中添加pinchImageViewdirectionalLayout.addComponent(pinchImageView);setUIContent(directionalLayout);库分析库主要为PinchImageView-ohos组件实现手势获取和图像操作功能。开发者设置显示器捕捉各种手势,并根据不同的手势执行不同的图像操作方法,从而显示不同的图像操作效果,如放大、缩小、移动等。1.手势获取方式手势获取对于实现PinchImageView-ohos组件的功能尤为重要。这里主要是通过onTouchEvent()方法来捕捉对应的手势。使用的主要手势包括PRIMARY_POINT_UP(最后一根手指离开屏幕)、PRIMARY_POINT_DOWN(第一根手指触摸屏幕)、OTHER_POINT_DOWN(当一个或多个手指触摸屏幕时,另一个手指触摸屏幕)、OTHER_POINT_UP(一些手指离开屏幕,而一些手指留在屏幕上),POINT_MOVE(手指在屏幕上移动)。通过监测各种手势的操作顺序和触摸时间等情况,实现捏合、滑动、单击、双击等复杂手势的识别效果。onTouchEvent()函数首先通过TouchEvent.getAction()方法获取当前手势。当手势为:(1)PRIMARY_POINT_UP需要判断图片之前是否处于缩放模式(此时图片处于缩放状态)。如果是缩放模式,则触发结束缩放动画,然后将手势状态置于自由模式。//最后一个点解除或取消,结束所有模式if(action==TouchEvent.PRIMARY_POINT_UP||action==TouchEvent.CANCEL){//如果之前使用了缩放模式,则需要结束缩放动画if(mPinchMode==PINCH_MODE_SCALE){scaleEnd();//缩放结束}//手势状态置于自由模式mPinchMode=PINCH_MODE_FREE;}(2)PRIMARY_POINT_DOWN需要判断图片是否处于缩放动画,如果不是,图片会切换到滚动模式(此时图片处于自由移动状态),并保存触发点的位置,用于(5)中的计算。elseif(action==TouchEvent.PRIMARY_POINT_DOWN){//缩放动画时不允许启动滚动模式if(!(mScaleAnimator!=null&&mScaleAnimator.isRunning())){//动画时不允许启动滚动模式,停止所有动画cancelAllAnimator();//切换到滚动模式mPinchMode=PINCH_MODE_SCROLL;//保存触发点的位置,用于(5)中的计算mLastMovePoint.modify(event.getPointerPosition(0).getX(),event.getPointerPosition(0).getY());}}(3)OTHER_POINT_DOWN需要将图片模式切换为缩放模式,并保存两个触发点的位置,用于(5)中的计算。elseif(action==TouchEvent.OTHER_POINT_DOWN){//不允许在动画过程中开启缩放模式,停止所有动画cancelAllAnimator();//切换到缩放模式mPinchMode=PINCH_MODE_SCALE;//保存两个触发点的位置zoom的计算,用于(5)saveScaleContext(event.getPointerPosition(0).getX(),event.getPointerPosition(0).getY(),event.getPointerPosition(1).getX(),event.getPointerPosition(1).getY());}(4)OTHER_POINT_UP需要判断手指抬起后图片是否处于缩放模式。如果处于缩放模式,则确定是否识别出两个以上的手指。当剩余手指多于两个时(缩放模式未结束),第一个触摸手指抬起,第二个触摸手指和第三个触摸手指所在的点作为缩放控制点。当剩余手指多于两个时(缩放模式未结束),第二个触摸的手指抬起,第一个触摸的手指和第三个触摸的手指所在的点作为缩放控制点。如果是缩放模式,则判断只识别到一根手指。此时不能允许切换到滚动模式,因为图像可能不在原来的位置。当手指抬起后图片不在缩放模式时(屏幕上只剩下一根手指),开启滚动模式,记录开始滚动的点。elseif(action==TouchEvent.OTHER_POINT_UP){//当有多个手指时抬起一个手指,需要处于缩放模式才能触发if(mPinchMode==PINCH_MODE_SCALE){//如果抬起点大于2,然后zoom模式仍然有效,但是有可能初始点已经改变,重新测量初始点if(event.getPointerCount()>2){//如果缩放模式还没有结束,但是第一个点提出,则让第二个点和第三个点作为缩放控制点if(event.getAction()>>8==0){event.getPointerPosition(1).getX();saveScaleContext(event.getPointerPosition(1).getX(),event.getPointerPosition(1).getY(),event.getPointerPosition(2).getX(),event.getPointerPosition(2).getY());//如果缩放模式还没有结束,但第二个点是凸起的,那么让第一个点和第三个点为缩放控制点}elseif(event.getAction()>>8==1){saveScaleContext(event.getPointerPosition(0).getX(),事件.getPointerPosition(0).getY(),事件.getPointerPosition(2).getX(),event.getPointerPosition(2).getY());}}//如果凸起的点等于2,那么就只剩一个点了,同样不允许进入单指模式,因为此时图片可能不在正确的位置}}(5)POINT_MOVE需要判断当前图片的模式。当处于滚动模式时,执行scrollBy()方法,实现图片的移动效果;缩放模式下,计算两个缩放点与缩放点中心的距离,执行scale()方法,实现图片缩放效果。scrollBy()方法和scale()方法的具体逻辑在图像操作方法中有详细介绍,这里不再赘述。elseif(action==TouchEvent.POINT_MOVE){if(!(mScaleAnimator!=null&&mScaleAnimator.isRunning())){//滚动模式移动if(mPinchMode==PINCH_MODE_SCROLL){//每次移动产生差异累加scrollBy(event.getPointerPosition(0).getX()-mLastMovePoint.position[0],event.getPointerPosition(0).getY()-mLastMovePoint.position[1]);//记录新的移动点mLastMovePoint.modify(event.getPointerPosition(0).getX(),event.getPointerPosition(0).getY());//在缩放模式下移动}elseif(mPinchMode==PINCH_MODE_SCALE&&event.getPointerCount()>1){//两个缩放点之间的距离floatdistance=MathUtils.getDistance(event.getPointerPosition(0).getX(),event.getPointerPosition(0).getY(),event.getPointerPosition(1).getX(),event.getPointerPosition(1).getY());//保存缩放点中心float[]lineCenter=MathUtils.getCenterPoint(event.getPointerPosition(0).getX(),event.getPointerPosition(0).getY(),event.getPointerPosition(1).getX(),event.getPointerPosition(1).getY());mLastMovePoint.modify(lineCenter[0],lineCenter[1]);//过程缩放scale(mScaleCenter,mScaleBase,distance,mLastMovePoint);}}}2.图像操作方法图像缩放(1)双指捏合双指捏合,顾名思义,就是两个手指向相反方向移动的操作。这个操作可以实现放大和缩小图像的效果。完成图像缩放的功能是通过scale()方法实现的。scale()方法体中需要设置各种缩放参数:scaleBase为缩放因子,scaleCenter代表图像缩放的中点,distance为两指间的距离,lineCenter为两指的中点。scaleBase和distance相乘就得到了缩放比例,图像还是会根据缩放比例变化。在缩放过程中,图片缩放中点的scaleCenter会随着两指lineCenter的中点一起移动,实现以两指中点为中心放大和缩小图片的效果。缩放效果如图4所示。图4捏合缩放privatevoidscale(PointscaleCenter,floatscaleBase,floatdistance,PointlineCenter){if(!isReady()){return;}//计算图像从fitcenter状态到的缩放比例目标状态floatscale=scaleBase*distance;Matrixmatrix=MathUtils.matrixTake();//根据图片的缩放中心进行缩放,让缩放中心在缩放点的中点matrix.postScale(scale,scale,scaleCenter.position[0],scaleCenter.position[1]);//使图片缩放中点跟随手指缩放中点matrix.postTranslate(lineCenter.position[0]-scaleCenter.position[0],lineCenter.position[1]-scaleCenter.position[1]);//应用变换mOuterMatrix.setMatrix(matrix);MathUtils.matrixGiven(matrix);dispatchOuterMatrixChanged();//重绘invalidate();}(2)单指双击单指双击表示用单指双击屏幕的操作,可以实现放大和图片的缩小因此,单指双击完成图片缩放的功能是通过doubleTap()方法实现的。在doubleTap()方法体中,我们初始化了一个缩放动画对象mScaleAnimator(),它有两个参数:mOuterMatrix(开始矩阵)和animEnd(结束矩阵)。起始矩阵表示图片的原始位置和大小;结束矩阵表示图片缩放后的位置和大小,根据缩放比例和双击点的位置确定。确定图片的起始和结束矩阵后,启动缩放动画,实现缩放效果,如图5所示。图5单触双击缩放privatevoiddoubleTap(floatx,floaty){...//开始进行计算缩放动画的结果矩阵MatrixanimEnd=MathUtils.matrixTake(mOuterMatrix);//计算需要缩放的倍数animEnd.postScale(nextScale/currentScale,nextScale/currentScale,x,y);//移动缩放点到控制中心animEnd.postTranslate(displayWidth/2f-x,displayHeight/2f-y);RectFloattestBound=MathUtils.rectFTake(0,0,mp.getImageInfo().size.width,mp.getImageInfo().size.height);...//清理当前可能正在执行的动画cancelAllAnimator();//启动矩阵动画mScaleAnimator=newScaleAnimator(mOuterMatrix,animEnd);mScaleAnimator.start();...}图片移动缩放状态下。单指滑动意味着手指在屏幕上完成矢量平移,这是移动图片的唯一方式。该功能是通过scrollBy()方法实现的。以图像左右移动为例。在scrollBy()方法中,需要判断图片在缩放状态下的最大位移距离。有几种不同的情况:图像移动后,左边缘超出了控件的左边缘,图像无法移动。移动;图片移动后右边缘超出控件右边缘,图片无法移动;图片移动后,如果两边都没有超出控件的边缘,则手指触摸点作为控制点水平移动图片。图片的上下平移与左右平移类似,这里不再赘述。图片移动效果如图6所示。图6图片移动的最大距离publicbooleanscrollBy(floatxDiff,floatyDiff){...if(bound.right-bound.left0){//如果移动前没有超出,计算应该移动的距离if(bound.left<0){xDiff=-bound.left;//否则不能移动}else{xDiff=0;}//如果移动后图片右侧超出控件右侧}elseif(bound.right+xDiffdisplayWidth){xDiff=displayWidth-bound.right;//否则不能移动}else{xDiff=0;}}...}更多信息,请访问:与华为官方共建的鸿蒙科技社区https://harmonyos.51cto.com