电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

Android點擊事件之多點觸摸與手勢識別的實現

瀏覽:30日期:2022-09-18 18:51:17
前言

最近遇到想要實現三指滑動監聽的需求,實現代碼不方便貼出來,但是思路還是可以記錄一下。

Muilti-touch 雙指縮放探索

首先要實現OnTouchListener接口,然后重寫方法:

public boolean onTouch(View v, MotionEvent event);

從這個方法中我們就可以獲取實現兩指縮放功能的全部信息。

View v是觸發事件的源,MotionEvent event即一個觸摸事件。對屏幕的幾乎所有操作都會觸發事件,如點擊、放開、滑動等。

不同的事件在MotionEvent中有不同的id,我們可以根據event.getAction() & MotionEvent.ACTION_MASK的結果來判斷是何種事件。

有如下事件使我們要用到的:

MotionEvent.ACTION_DOWN:在第一個點被按下時觸發 MotionEvent.ACTION_UP:當屏幕上唯一的點被放開時觸發 MotionEvent.ACTION_POINTER_DOWN:當屏幕上已經有一個點被按住,此時再按下其他點時觸發。 MotionEvent.ACTION_POINTER_UP:當屏幕上有多個點被按住,松開其中一個點時觸發(即非最后一個點被放開時)。 MotionEvent.ACTION_MOVE:當有點在屏幕上移動時觸發。值得注意的是,由于它的靈敏度很高,而我們的手指又不可能完全靜止(即使我們感覺不到移動,但其實我們的手指也在不停地抖動),所以實際的情況是,基本上只要有點在屏幕上,此事件就會一直不停地被觸發。

舉例子來說:當我們放一個食指到屏幕上時,觸發ACTION_DOWN事件;再放一個中指到屏幕上,觸發ACTION_POINTER_DOWN事件;此時再把食指或中指放開,都會觸發ACTION_POINTER_UP事件;再放開最后一個手指,觸發ACTION_UP事件;而同時在整個過程中,ACTION_MOVE事件會一直不停地被觸發。

event.getX(index)和event.getY(index)可以獲取到指定index點的坐標,所以當屏幕上有兩個點的時候,我們用如下方法來獲取兩點間的距離:

private float spacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return FloatMath.sqrt(x * x + y * y); }

由以上事件觸發的原理,就可以根據被觸發的不同事件來判斷當前屏幕上的點的個數:

switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mode = 1; break; case MotionEvent.ACTION_UP: mode = 0; break; case MotionEvent.ACTION_POINTER_UP: mode -= 1; break; case MotionEvent.ACTION_POINTER_DOWN: mode += 1; break; }

然后在MotionEvent.ACTION_MOVE事件中,判斷點的個數,如果大于等于2,就計算兩點間的距離,如果距離增大就把圖片放大,距離減少就把圖片縮小。

于是代碼就成了:

switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mode = 1; break; case MotionEvent.ACTION_UP: mode = 0; break; case MotionEvent.ACTION_POINTER_UP: mode -= 1; break; case MotionEvent.ACTION_POINTER_DOWN: oldDist = spacing(event);//兩點按下時的距離 mode += 1; break;case MotionEvent.ACTION_MOVE: if (mode >= 2) { float newDist = spacing(event); if (newDist > oldDist) { zoomOut(); } if (newDist < oldDist) { zoomIn(); } break; }

經過檢驗,這種方法是能夠實現縮放效果的。

但是有了另外一個問題:就是由于ACTION_MOVE會因顫抖一直被觸發,而每次觸發的時候兩點間的距離也總會有細小的變化,所以運行之后只要有兩點在屏幕上,就總會在放大或縮小字體。

經過一番思考,我想出了一個控制其靈敏度的方法,即在case MotionEvent.ACTION_MOVE時判斷只有當距離變化大于一定程度時才會更改字體大小:

if (newDist > oldDist + 1) {//原為:if (newDist > oldDist) zoomOut();//放大 }

另外縮放的方法也改成了按比例縮放,完整的ZoomListenter代碼:

import android.util.FloatMath;import android.view.MotionEvent;import android.view.View;import android.view.View.OnTouchListener;import android.widget.TextView; public class ZoomListenter implements OnTouchListener { private int mode = 0; float oldDist; float textSize = 0; TextView textView = null; @Override public boolean onTouch(View v, MotionEvent event) {textView = (TextView) v;if (textSize == 0) { textSize = textView.getTextSize();}switch (event.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN: mode = 1; break;case MotionEvent.ACTION_UP: mode = 0; break;case MotionEvent.ACTION_POINTER_UP: mode -= 1; break;case MotionEvent.ACTION_POINTER_DOWN: oldDist = spacing(event); mode += 1; break; case MotionEvent.ACTION_MOVE: if (mode >= 2) {float newDist = spacing(event);if (newDist > oldDist + 1) { zoom(newDist / oldDist); oldDist = newDist;}if (newDist < oldDist - 1) { zoom(newDist / oldDist); oldDist = newDist;} } break;}return true; } private void zoom(float f) {textView.setTextSize(textSize *= f); } private float spacing(MotionEvent event) {float x = event.getX(0) - event.getX(1);float y = event.getY(0) - event.getY(1);return FloatMath.sqrt(x * x + y * y); } }

這樣,基本算是能達到預期的效果了。

Android原生帶的手勢監聽GestureDetector 使用

GestureDetector 是 Android 中,專門用來進行手勢監聽的一個對象,在他的監聽器中,我們通過傳入 MotionEvents 對象,就可以在各種事件的回調方法中各種手勢進行監測。舉個例子: GestureDetector 的 OnGestureListener 就是一種回調方法,就是說在獲得了傳入的這個 MotionEvents 對象之后,進行了處理,我們通過重寫了其中的各種方法(單擊事件、雙擊事件等等),就可以監聽到單擊,雙擊,滑動等事件,然后直接在這些方法內部進行處理。

Android點擊事件之多點觸摸與手勢識別的實現

使用方法

首先,創建一個 SimpleOnGestureListener 回調方法對象,并對其中各個方法進行重寫根據這個 listener 對象,實例化出 GestureDetector 對象對目標控件重寫 setOnTouchListener 方法,并在其中調用 detector 對象的 onTouchEvent 方法即可簡單易懂,一分鐘搞定。

@Override protected void onResume() {button.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) {return detector.onTouchEvent(event); }});super.onResume(); } private void iniGestureListener(){GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){ @Override public boolean onDoubleTap(MotionEvent e) {MyToast.makeToast(GestureDetectorActivity.this, 'double click up!');return super.onDoubleTap(e); } detector = new GestureDetector(GestureDetectorActivity.this, listener); }

GestureDetecotr 還有哪些厲害的回調方法呢?

OnDoubleTapListener :也就是雙擊事件,雙擊事件除了 onDoubleTapEvent 這個回調方法之外,還有 SingleTapConfirmed 和 DoubleTap 這兩個回調方法 OnGestureListener :這里集合了眾多手勢的監聽器:主要有:按下(Down)、 扔(Fling)、長按(LongPress)、滾動(Scroll)、觸摸反饋(ShowPress) 和 單擊抬起(SingleTapUp) SimpleOnGestureListener :上述接口的空實現,用的頻率比較多

OnDoubleTapListener

我們先來講講 OnDoubleTapListener,大家可能要問:剛剛不是已經講過雙擊事件監聽了嗎,這里又來不是浪費時間?廢話不說,讓我詳細介紹下這類的方法:

單擊回調 SingleTapConfirmed

有人就會很好奇,對于單擊事件的回調,直接去用 onClickListener 不就好了么,干嘛要用 SingleTapConfirmed 呢?

首先,這兩個方法是沖突的,這里就涉及到了事件分發機制,具體的后面有空再總結下,這里就不詳解了。

其二,更具備 onClickListener 的機制,我們不難發現,如果是用 onClickListener 的話,當我們雙擊時,我們也會調用單擊事件,也就是單擊了兩次,這明顯是不符合我們意圖的。那么該如何調用呢?如下:

final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){ @Override public boolean onSingleTapConfirmed(MotionEvent e) {MyToast.makeToast(GestureDetectorActivity.this, 'single click!');return super.onSingleTapConfirmed(e); } ...};

DoubleTap 與 onDoubleTapEvent

打算把這兩個方法放在一起將,一則他兩都屬于雙擊的范疇,二則他兩有著極高相似和細微卻重要的區別。

大家可以嘗試著在 onDoubleTapEvent和 DoubleTap 中,對點擊的 Down move 和 up 進行打印,你就會發現,對于 DoubleTap 而言,它是在第二次點擊按下時,發生的回調,而對于 onDoubleTapEvent 而言,則是在第二次點擊后,手指抬起離開了屏幕時,發生的回調。這就是他兩最重要的區別。

final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){ @Overridepublic boolean onDoubleTap(MotionEvent e) { MyToast.makeToast(GestureDetectorActivity.this, 'double click down!'); return super.onDoubleTap(e);} @Overridepublic boolean onDoubleTapEvent(MotionEvent e) { switch (e.getActionMasked()){case MotionEvent.ACTION_UP: MyToast.makeToast(GestureDetectorActivity.this, 'double click up!'); break; } return super.onDoubleTapEvent(e);} };

所以,有了這兩個方法,我們就可以更具目的性的滿足兩種需求。 到這里,單擊雙擊事件就告一段落了,下面我們進入OnGestureListener的學習。

OnGestureListener

這可以說是整個手勢監測中,最核心的部分了,前面都是引入,現在才是正題,這里我主要向大家介紹一下手勢:

按下(Down) 一扔(Fling) 長按(LongPress) 滾動(Scroll) 觸摸反饋(ShowPress) 單擊抬起(SingleTapUp)

onDown

onDown 事件很好理解,他在一個 View 被按下時執行。也正是如此,要想能執行 onDown ,首先要保證這個 View 是可以點擊的,也就是 onClickable 的值為 true 。

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){ @Overridepublic boolean onDown(MotionEvent e) { MyToast.makeToast(GestureDetectorActivity.this, 'onDown'); // 后續事件 return super.onDown(e);} };

onFling

對于 onFling 我個人感覺這是個最常用的方法,就像它的名字,翻譯過來是拖、拽、扔的意思。舉個例子 RecyclerView 或者 ListView 我們都有用過,當我們快速上拉后會滾動一定距離停止,我們可愛的 onFling 就是用于檢測這種手勢的。

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { mSpeedX = velocityX; mSpeedY = velocityY; handler.postDelayed(runnable, 30); return super.onFling(e1, e2, velocityX, velocityY);} };

從代碼中,我們不難發現:該方法有四個參數

參數 意義 e1 手指按下時的 Event。 e2 手指抬起時的 Event。 velocityX 在 X 軸上的運動速度(像素/秒)。 velocityY 在 Y 軸上的運動速度(像素/秒)。

通過前兩個 MotionEvent 參數,我們可以獲得點擊發生的位置等,通過后兩個 float 參數,我們可以獲得手指滑動的速度。

具體使用其實還是蠻多的,比如我們可以想象下臺球游戲,球桿擊球后,就有這樣一個初速度遞減的效果。

onLongPress

onLongPress 很簡單,就是長按事件的回調,比如說長按復制,長按彈窗等等,它不但應用廣泛,同時使用也非常簡單,這里就不嘮叨了

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){ @Overridepublic void onLongPress(MotionEvent e) { MyToast.makeToast(GestureDetectorActivity.this, 'onLongPress'); // 后續工作 super.onLongPress(e);} };

onScroll

onScroll 方法和 onFling 很像,唯一的區別在于,onFling 的參數是滑動的速度,而 onScroll 的后兩個參數則是滑動的距離:

參數 意義 e1 手指按下時的 MotionEvent e2 手指抬起時的 MotionEvent distanceX 在 X 軸上劃過的距離 distanceY 在 Y 軸上劃過的距離

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { MyToast.makeToast(GestureDetectorActivity.this, 'onScroll X = ' + distanceX + ' Y = ' + distanceY); return super.onScroll(e1, e2, distanceX, distanceY);} };

onShowPress

這個方法我其實覺得作用不是很大,因為它是在 View 被點擊(按下)是調用,其作用是給用戶一個視覺反饋,讓用戶知道我這個控件被點擊了,這樣的效果我們完全可以用 Material design 的 ripple 實現,或者直接 drawable 寫個背景也行。

如果說它有什么特別指出的話,它是一種延時回調,延遲時間是 180 ms。也就是說用戶手指按下后,如果立即抬起或者事件立即被攔截,時間沒有超過 180 ms的話,這條消息會被 remove 掉,也就不會觸發這個回調。

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){@Overridepublic void onShowPress(MotionEvent e) { MyToast.makeToast(GestureDetectorActivity.this, 'onShowPress');// >150ms 時調用 super.onShowPress(e);} };

onSingleTapUp

對于 onSingleTapUp 網上有很多分析,但我覺得過于復雜了,其實這東西很簡單。舉個例子你就懂了:

之前我們講過雙擊事件,那好 onSingleTapUp 就是在 雙擊事件的第一次點擊時回調。也就是說但你點擊了一個控件時(雙擊第一下),這個回調馬上會被調用,然后迅速點第二下(雙擊事件的第二下),則其不會被調用。

類型 觸發次數 摘要 onSingleTapUp 1 在雙擊的第一次抬起時觸發 onSingleTapConfirmed 0 雙擊發生時不會觸發。 onClick 2 在雙擊事件時觸發兩次。

它和 onSingleTapConfirmed 的區別也就很明顯了,onSingleTapConfirmed 在發生雙擊時,不會回調,而 onSingleTapUp 只會在雙擊的的第一次回調。

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){@Overridepublic boolean onSingleTapUp(MotionEvent e) {// 雙擊第一次抬起觸發,第二次不觸發 Log.d('onSingleTapUp', 'onSingleTapUp');// >150ms 時調用 return super.onSingleTapUp(e);} };

SimpleOnGestureListener

SimpleOnGestureListener 中包含了以上所有方法的空實現,之所以在文末再一次提及他,主要是想講下它的方便之處。

我們以監聽 OnDoubleTapListener 為例,如果想要使用 OnDoubleTapListener 接口則需要這樣進行設置:

GestureDetector detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener());detector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() { @Override public boolean onSingleTapConfirmed(MotionEvent e) {Toast.makeText(MainActivity.this, 'onSingleTapConfirmed', Toast.LENGTH_SHORT).show();return false; } @Override public boolean onDoubleTap(MotionEvent e) {Toast.makeText(MainActivity.this, 'onDoubleTap', Toast.LENGTH_SHORT).show();return false; } @Override public boolean onDoubleTapEvent(MotionEvent e) {Toast.makeText(MainActivity.this,'onDoubleTapEvent',Toast.LENGTH_SHORT).show();return false; }});

我們不難發現一個問題,既然在 GestureDetector 實例化時,已經實例化了一個 SimpleOnGestureListener 了,那么在舍近求遠的去使用 OnGestureListener 的話,會多出幾個無用的空實現,顯然很浪費,所以在一般情況下,乖乖的使用 SimpleOnGestureListener 就好了。

其它

Android 除了提供了一個 GestureDetector 來幫助我們識別一些基本的觸摸手勢外,還有 ScaleGestureDetector 可以識別縮放手勢,讓我們很方便地實現手勢控制功能。

//-----------------------implement OnScaleGestureListener’s method----------------------// @Override public boolean onScale(ScaleGestureDetector detector) {Toast.makeText(MainActivity.this, 'onScale', Toast.LENGTH_SHORT).show();return true; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) {Toast.makeText(MainActivity.this, 'onScaleBegin', Toast.LENGTH_SHORT).show();return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) {Toast.makeText(MainActivity.this, 'onScaleEnd', Toast.LENGTH_SHORT).show(); }

到此這篇關于Android點擊事件之多點觸摸與手勢識別的實現的文章就介紹到這了,更多相關Android 多點觸摸與手勢識別內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Android
相關文章:
主站蜘蛛池模板: 飞扬动力官网-广告公司管理软件,广告公司管理系统,喷绘写真条幅制作管理软件,广告公司ERP系统 | 吸音板,隔音板,吸音材料,吸音板价格,声学材料 - 佛山诺声吸音板厂家 | 青岛代理记账_青岛李沧代理记账公司_青岛崂山代理记账一个月多少钱_青岛德辉财税事务所官网 | 盘扣式脚手架-附着式升降脚手架-移动脚手架,专ye承包服务商 - 苏州安踏脚手架工程有限公司 | 粉末包装机-给袋式包装机-全自动包装机-颗粒-液体-食品-酱腌菜包装机生产线【润立机械】 | 复合肥,化肥厂,复合肥批发,化肥代理,复合肥品牌-红四方 | 除甲醛公司-甲醛检测-广西雅居环境科技有限公司 | 不锈钢螺丝,不锈钢螺栓,不锈钢标准件-江苏百德特种合金有限公司 交变/复合盐雾试验箱-高低温冲击试验箱_安奈设备产品供应杭州/江苏南京/安徽马鞍山合肥等全国各地 | 苏州防水公司_厂房屋面外墙防水_地下室卫生间防水堵漏-苏州伊诺尔防水工程有限公司 | 澳门精准正版免费大全,2025新澳门全年免费,新澳天天开奖免费资料大全最新,新澳2025今晚开奖资料,新澳马今天最快最新图库 | 定硫仪,量热仪,工业分析仪,马弗炉,煤炭化验设备厂家,煤质化验仪器,焦炭化验设备鹤壁大德煤质工业分析仪,氟氯测定仪 | 智慧养老_居家养老_社区养老_杰佳通| 光谱仪_积分球_分布光度计_灯具检测生产厂家_杭州松朗光电【官网】 | 深圳湾1号房价_深圳湾1号二手房源 | 二手注塑机回收_旧注塑机回收_二手注塑机买卖 - 大鑫二手注塑机 二手光谱仪维修-德国OBLF光谱仪|进口斯派克光谱仪-热电ARL光谱仪-意大利GNR光谱仪-永晖检测 | 特材真空腔体_哈氏合金/镍基合金/纯镍腔体-无锡国德机械制造有限公司 | 深圳市八百通智能技术有限公司官方网站| 水厂自动化|污水处理中控系统|水利信息化|智慧水务|智慧农业-山东德艾自动化科技有限公司 | 焊接烟尘净化器__焊烟除尘设备_打磨工作台_喷漆废气治理设备 -催化燃烧设备 _天津路博蓝天环保科技有限公司 | 超高频感应加热设备_高频感应电源厂家_CCD视觉检测设备_振动盘视觉检测设备_深圳雨滴科技-深圳市雨滴科技有限公司 | 伺服电机维修、驱动器维修「安川|三菱|松下」伺服维修公司-深圳华创益 | 华禹护栏|锌钢护栏_阳台护栏_护栏厂家-华禹专注阳台护栏、楼梯栏杆、百叶窗、空调架、基坑护栏、道路护栏等锌钢护栏产品的生产销售。 | 沈阳缠绕膜价格_沈阳拉伸膜厂家_沈阳缠绕膜厂家直销 | 超声波破碎仪-均质乳化机(供应杭州,上海,北京,广州,深圳,成都等地)-上海沪析实业有限公司 | 不锈钢钢格栅板_热浸锌钢格板_镀锌钢格栅板_钢格栅盖板-格美瑞 | 专注提供国外机电设备及配件-工业控制领域一站式服务商-深圳市华联欧国际贸易有限公司 | 岸电电源-60HZ变频电源-大功率变频电源-济南诚雅电子科技有限公司 | 交联度测试仪-湿漏电流测试仪-双85恒温恒湿试验箱-常州市科迈实验仪器有限公司 | 电加热导热油炉-空气加热器-导热油加热器-翅片电加热管-科安达机械 | 深圳工程师职称评定条件及流程_深圳职称评审_职称评审-职称网 | 烟台金蝶财务软件,烟台网站建设,烟台网络推广 | 液氮罐_液氮容器_自增压液氮罐-北京君方科仪科技发展有限公司 | 中式装修设计_全屋定制家具_实木仿古门窗花格厂家-喜迎门 | 电动卫生级调节阀,电动防爆球阀,电动软密封蝶阀,气动高压球阀,气动对夹蝶阀,气动V型调节球阀-上海川沪阀门有限公司 | 数码管_LED贴片灯_LED数码管厂家-无锡市冠卓电子科技有限公司 | 天一线缆邯郸有限公司_煤矿用电缆厂家_矿用光缆厂家_矿用控制电缆_矿用通信电缆-天一线缆邯郸有限公司 | 淬火设备-钎焊机-熔炼炉-中频炉-锻造炉-感应加热电源-退火机-热处理设备-优造节能 | 废旧物资回收公司_广州废旧设备回收_报废设备物资回收-益美工厂设备回收公司 | 乐之康护 - 专业护工服务平台,提供医院陪护-居家照护-居家康复 | 胶原检测试剂盒,弹性蛋白检测试剂盒,类克ELISA试剂盒,阿达木单抗ELISA试剂盒-北京群晓科苑生物技术有限公司 | 金蝶帐无忧|云代账软件|智能财税软件|会计代账公司专用软件 |