您当前的位置:首页 > 电脑百科 > 程序开发 > 移动端 > Android

Android缩放手势的检测

时间:2019-09-10 11:31:15  来源:  作者:

 

缩放手势对于大部分 Android 工程师来说,需要用到的机会比较少,它最常见于以下的一些应用场景中,例如:图片浏览,图片编辑(贴图效果)、网页缩放、地图、文本阅读(通过缩放手势调整文字大小)等。应用场景相对比较狭窄,不过肯定也会有一些用武之地,它可以实现如下的效果:

缩放手势的检测

 

2.缩放手势检测(ScaleGestureDetector)

缩放手势检测同样是官方提供的手势检测工具,它的使用方式的 GentureDetector 类似,也是通过 Listener 进行监听用户的操作手势,它是对缩放手势进行了一次封装, 可以方便用户快速的完成缩放相关功能的开发。缩放手势相对比较简单,网络上也能查到不少非官方实现的缩放手势计算方案,但部分非官方的方案确实有所局限,例如只支持两个手指的计算,在出现超过两个手指时,只计算了前两个手指的移动,这样显然是不合理的。而官方的这种实现方案轻松的应对了多个手指的情况,下面我们就来看看它是如何实现的吧。

2.1 构造方法

它有两个构造方法,和 GestureDetector 类似,如下所示:

ScaleGestureDetector(Contextcontext,ScaleGestureDetector.OnScaleGestureListenerlistener)
ScaleGestureDetector(Contextcontext,ScaleGestureDetector.OnScaleGestureListenerlistener,Handlerhandler) 

2.2 手势监听器

它只有两个监听器,但严格来说,这两个监听器是同一个,只不过一个是接口,另一个是空实现而已。

监听器 简介

OnScaleGestureListener 缩放手势检测器

SimpleOnScaleGestureListene r缩放手势检测器的空实现。

2.3 简单示例

这是使用 ScaleGestureDetector 的一个极简用例,当然,它没有实现任何功能,只是用日志的方式输出了几个我们比较关心的参数而已。

public class ScaleGestureDemoView extends View { 
 private static final String TAG = "ScaleGestureDemoView";
 private ScaleGestureDetector mScaleGestureDetector;
 public ScaleGestureDemoView(Context context) { 
 super(context);
 }
 public ScaleGestureDemoView(Context context, @Nullable AttributeSet attrs) {
 super(context, attrs);
 initScaleGestureDetector();
 } 
 private void initScaleGestureDetector() { 
 mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {
 @Override
 public boolean onScaleBegin(ScaleGestureDetector detector) { 
 return true;
 } 
 @Override
 public boolean onScale(ScaleGestureDetector detector) {
 Log.i(TAG, "focusX = " + detector.getFocusX()); // 缩放中心,x坐标
 Log.i(TAG, "focusY = " + detector.getFocusY()); // 缩放中心y坐标
 Log.i(TAG, "scale = " + detector.getScaleFactor()); // 缩放因子
 return true;
 }
 @Override 
 public void onScaleEnd(ScaleGestureDetector detector) { 
 }
 });
 } 
 @Override
 public boolean onTouchEvent(MotionEvent event) {
 mScaleGestureDetector.onTouchEvent(event);
 return true; 
 }
 }

3. 基本原理

由于缩放手势检测使用起来非常简单,没有什么复杂的内容,不仅如此,它的实现也非常简单,下面我就带大家简单分析一下它的基本原理。在缩放手势中我们其实主要关心的只有两个参数而已,一个是缩放的中心点,另一个就是缩放比例了。 下面我们就看看这两个参数是如何计算出来的.

3.1 计算缩放的中心点(焦点)

如果只有两个手指的话,缩放的中心点自然是非常容易计算的,那就是两个手指坐标的中点,但是如果有多个手指该如何计算缩放的中心点呢?

计算中心点的原理其实也非常简单,那就是将所有的坐标都加起来,然后除以数量即可。

这是一个简单的数学原理,并不复杂,如果有不理解的,自己尝试计算一下也就能明白了。不过在实际运用中还是需要注意一下的, 用户的手指数量可能并不是固定的,用户可能随时抬起来或者按下手指,ScaleGestureDetector 中是这样实现的:

 finalbooleananchoredScaleCancelled=
 mAnchoredScaleMode == ANCHORED_SCALE_MODE_STYLUS && !isStylusButtonDown;
 final boolean streamComplete = action == MotionEvent.ACTION_UP ||
 finalbooleanstreamComplete=action==MotionEvent.ACTION_UP||
 action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled;
 // 注意这里
 if(action==MotionEvent.ACTION_DOWN||streamComplete){
 //重置侦听器正在进行的任何缩放。
 //如果是ACTION_DOWN,我们正在开始一个新的事件流。
 //这意味着应用程序可能没有给我们所有的事件(事件被上层直接拦截了)。
 if(mInProgress){
 mListener.onScaleEnd(this);
 mInProgress=false;
 mInitialSpan=0;
 mAnchoredScaleMode=ANCHORED_SCALE_MODE_NONE;
 }elseif(inAnchoredScaleMode()&&streamComplete){
 mInProgress=false;
 mInitialSpan=0;
 mAnchoredScaleMode=ANCHORED_SCALE_MODE_NONE;
 }
 if(streamComplete){
 returntrue;
 }
} 

可以看到,当触发 down 或者触发 up,cancel 时,如果之前处于缩放计算的状态,会将其状态重置, 并调用 onScaleEnd 方法。

计算中心点:

 final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
 action == MotionEvent.ACTION_POINTER_UP ||
 action == MotionEvent.ACTION_POINTER_DOWN || anchoredScaleCancelled; 
 // 注意这里
 final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; 
 final int skipIndex = pointerUp ? event.getActionIndex() : -1; 
 // 确定焦点
 float sumX = 0, sumY = 0;
 final int div = pointerUp ? count - 1 : count; 
 final float focusX; 
 final float focusY;
 if (inAnchoredScaleMode()) { 
 // 在锚定比例模式下,焦点始终是双击或按钮按下时手势开始的位置
 focusX = mAnchoredScaleStartX; 
 focusY = mAnchoredScaleStartY; 
 if (event.getY() < focusY) {
 mEventBeforeOrAboveStartingGestureEvent = true;
 } else { 
 mEventBeforeOrAboveStartingGestureEvent = false; 
 } } else {
 // 注意这里, 最终计算得到焦点 
 for (int i = 0; i < count; i++) {
 if (skipIndex == i) continue;
 sumX += event.getX(i);
 sumY += event.getY(i);
 }
 focusX = sumX / div; 
 focusY = sumY / div;
 } 

3.2 计算缩放比例

计算缩放比例也很简单,就是计算各个手指到焦点的平均距离,在用户手指移动后用新的平均距离除以旧的平均距离,并以此计算得出缩放比例。

 // 计算到焦点的平均距离
 floatdevSumX=0,devSumY=0;
 for(inti=0;i<count;i++){
 if (skipIndex == i) continue;
 devSumX+=Math.abs(event.getX(i)-focusX);
 devSumY+=Math.abs(event.getY(i)-focusY);
 }
 finalfloatdevX=devSumX/div;
 finalfloatdevY=devSumY/div;
 // 注意这里
 finalfloatspanX=devX*2;
 finalfloatspanY=devY*2;
 finalfloatspan;
 if(inAnchoredScaleMode()){
 span=spanY;
 }else{
 // 相当于 sqrt(x*x + y*y)
 span=(float)Math.hypot(spanX,spanY);
 }

当用户移动的距离超过一定数值(数值大小由系统定义)后,会触发 onScaleBegin 方法,如果用户在 onScaleBegin 方法里面返回了 true,表示接受事件后,就会重置缩放相关数值,并且开始积累缩放因子。

 // mSpanSlop 和 mMinSpan 都是从系统里面取得的预定义数值,该数值实际上影响的是缩放的灵敏度。
 // 不过该参数并没有提供设置的方法,如果对灵敏度不满意的话,和通过直接之际复制一个 ScaleGestureDetector 到项目中, 并且修改其中的数值。
 finalintminSpan=inAnchoredScaleMode()?mSpanSlop:mMinSpan;
 if(!mInProgress&&span>=minSpan&&
 (wasInProgress||Math.abs(span-mInitialSpan)>mSpanSlop)){
 mPrevSpanX=mCurrSpanX=spanX;
 mPrevSpanY=mCurrSpanY=spanY;
 mPrevSpan=mCurrSpan=span;
 mPrevTime=mCurrTime;
 mInProgress=mListener.onScaleBegin(this);
 }

通知用户缩放:

 if(action==MotionEvent.ACTION_MOVE{
 mCurrSpanX=spanX;
 mCurrSpanY=spanY;
 mCurrSpan=span;
 booleanupdatePrev=true;
 if(mInProgress){
 // 注意这里,用户的返回值决定了是否重新计算缩放因子
 updatePrev=mListener.onScale(this);
 }
 // 如果用户返回了 true ,就会重新计算缩放因子
 if(updatePrev){
 mPrevSpanX=mCurrSpanX;
 mPrevSpanY=mCurrSpanY;
 mPrevSpan=mCurrSpan;
 mPrevTime=mCurrTime;
 }
 }

由于缩放手势检测确实比较简单,也大概就这么多了,感兴趣的话,可以私信我



Tags:Android 缩放手势   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
缩放手势对于大部分 Android 工程师来说,需要用到的机会比较少,它最常见于以下的一些应用场景中,例如:图片浏览,图片编辑(贴图效果)、网页缩放、地图、文本阅读(通过缩放手势调...【详细内容】
2019-09-10  Tags: Android 缩放手势  点击:(201)  评论:(0)  加入收藏
▌简易百科推荐
今天面试遇到同学说做过内存优化,于是我一般都会问那 Bitmap 的像素内存存在哪?大多数同学都回答在 java heap 里面,就比较尴尬,理论上你做内存优化,如果连图片这个内存大户内存...【详细内容】
2021-12-23  像程序那样思考    Tags:Android开发   点击:(8)  评论:(0)  加入收藏
Android logcat日志封装logcat痛点在Android开发中使用logcat非常频繁,logcat能帮我们定位问题,但是在日常使用中发现每次使用都需要传递tag,并且会遇到输出频率很高的log,在多...【详细内容】
2021-12-22  YuCoding    Tags:Android   点击:(8)  评论:(0)  加入收藏
对项目的基本介绍 1.整个框架主要是给MVVM框架使用的,自己写完interface接口后,通过自定义的注解就能自动生成接口方法 2.用Kotlin的Flow去代替Rxjava,因为我发现RxJava功能很...【详细内容】
2021-12-08  网易Leo    Tags:Android开发   点击:(17)  评论:(0)  加入收藏
前言在Android开发过程中,有些时候会根据需要引用别的项目到当前项目里面,而且以Module形式引用。所以本篇博文就来分享一下怎么以Module形式引用别的项目到当前项目中,方便开...【详细内容】
2021-12-07  网易Leo    Tags:Android开发   点击:(22)  评论:(0)  加入收藏
作者:fundroid这篇文章偏阅读一些,大家可以了解下 Android 的一些最新动向。每年9/10月份 Google 都会举行约为期2天的 Android Dev Summit,在活动上 Google 的技术专家们会分...【详细内容】
2021-11-30  像程序那样思考    Tags:Android开发   点击:(15)  评论:(0)  加入收藏
一、 准备工作1、安装JDK,下载地址(可能需要一个oracle账号,大家百度一下或者自行注册一个就行。尽可能选择8或者11,这两个是长期版本)Java SE | Oracle Technology Network | Or...【详细内容】
2021-11-23  永沧    Tags:Android   点击:(28)  评论:(0)  加入收藏
使用Maven Publish Plugin插件。(官方支持)一、在Library的build.gradle中配置plugins { id &#39;com.android.library&#39; id &#39;kotlin-android&#39; id &#39;k...【详细内容】
2021-11-05  羊城小阳    Tags:Android   点击:(37)  评论:(0)  加入收藏
谷歌离推出Play Store应用程序的新数据隐私部分又近了一步。应用程序开发人员现在可以通过谷歌在Play控制台的新 "数据安全表 "填写相关细节。该公司表示,所需信息将从2022年...【详细内容】
2021-10-20    中关村在线  Tags:安卓   点击:(58)  评论:(0)  加入收藏
架构究竟是什么?如何更好的理解架构?我们知道一个APP通常是由class组成,而这些class之间如何组合,相互之间又如何产生作用,就是影响这个APP的关键点。细分的话我们可以将其分为类...【详细内容】
2021-09-17  像程序那样思考    Tags:Android架构   点击:(52)  评论:(0)  加入收藏
概述当Android应用程序需要访问设备上的敏感资源时,应用程序开发人员会使用权限模型。虽然该模型使用起来非常简单,但开发人员在使用权限时容易出错,从而导致安全漏洞。本文中,...【详细内容】
2021-09-07  SecTr安全团队    Tags:Android开发   点击:(66)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条