Android手勢(shì)ImageView三部曲 第二部
廢話不多說了,還記得上一節(jié)Android手勢(shì)ImageView三部曲(一)最后我們提及的那個(gè)框架么?這一節(jié)我們重點(diǎn)了掌握一下GestureDetector這個(gè)類相關(guān)的屬性方法。
一、那么GestureDetector是干嘛的呢?
顧名思義,字面意思就是“手勢(shì)檢測(cè)器“的意思,還記得我們上一節(jié)中實(shí)現(xiàn)的GestureImageView么?我們?cè)趏nTouchEvent中檢測(cè)到了各種個(gè)樣的手勢(shì)(手指按下、抬起、什么時(shí)候?qū)儆谕献?、什么時(shí)候?qū)儆诳s放)都是通過我們的計(jì)算得到的,但是有了GestureDetector這個(gè)類后,我們不需要自己做判斷現(xiàn)在是什么手勢(shì)了,GestureDetector會(huì)幫我們做好判斷,完了后通過回調(diào)函數(shù)告訴你,就像官網(wǎng)所說的(This class should only be used with MotionEvents reported via touch (don't use for trackball events).)這個(gè)類僅僅是通過觸碰檢查事件的,而不是用于跟蹤事件的,我檢測(cè)到了事件,然后告訴你,至于你需要怎么處理這個(gè)事件,那就是你自己的事了。
GestureDetector的一些具體的api大家可以去查看谷歌官方文檔或啟艦大神的博客:
https://developer.android.google.cn/reference/android/view/GestureDetector.html
Android手勢(shì)識(shí)別器GestureDetector使用詳解
說了這么多估計(jì)你都有點(diǎn)累了,下面讓我們看看具體怎么使用:
偷一下懶,我就直接用 Android手勢(shì)ImageView三部曲(一)
中的MatrixImageView類改改代碼了:
public class MatrixImageView extends ImageView {
private static final int MODE_NONE = 190;
private static final int MODE_DRAG = 468;
private static final int MODE_ZOOM = 685;
private int mode;
private float startX, startY;
private float midX, midY;
private Matrix currMatrix, savedMatrix;
private float preRotate, rotate;
private float preSpacing;
private GestureDetector detector;
public MatrixImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
detector=new GestureDetector(context,onGestureListener);
}
private void initView() {
mode = MODE_NONE;
currMatrix = new Matrix();
savedMatrix = new Matrix();
DisplayMetrics dm = getResources().getDisplayMetrics();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
bitmap = Bitmap.createScaledBitmap(bitmap, dm.widthPixels, dm.heightPixels, true);
setImageBitmap(bitmap);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return detector.onTouchEvent(event);
}
private GestureDetector.SimpleOnGestureListener onGestureListener=new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.e("TAG", "====onSingleTapUp=====");
return super.onSingleTapUp(e);
}
@Override
public void onLongPress(MotionEvent e) {
Log.e("TAG", "====onLongPress=====");
super.onLongPress(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.e("TAG", "====onScroll=====");
Log.e("TAG", "distanceX===>"+distanceX);
Log.e("TAG", "distanceY===>"+distanceY);
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public void onShowPress(MotionEvent e) {
Log.e("TAG", "====onShowPress=====");
super.onShowPress(e);
}
@Override
public boolean onDown(MotionEvent e) {
Log.e("TAG", "====onDown=====");
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
Log.e("TAG", "====onDoubleTap=====");
return super.onDoubleTap(e);
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
Log.e("TAG", "====onDoubleTapEvent=====");
return super.onDoubleTapEvent(e);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.e("TAG", "====onSingleTapConfirmed=====");
return super.onSingleTapConfirmed(e);
}
@Override
public boolean onContextClick(MotionEvent e) {
Log.e("TAG", "====onContextClick=====");
return super.onContextClick(e);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.e("TAG", "====onFling=====");
Log.e("TAG", "velocityX===>"+velocityX);
Log.e("TAG", "velocityY===>"+velocityY);
return super.onFling(e1, e2, velocityX, velocityY);
}
};
}
首先我們?cè)跇?gòu)造方法中創(chuàng)建一個(gè)手勢(shì)監(jiān)測(cè)器的對(duì)象GestureDetector:
public MatrixImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
detector=new GestureDetector(context,onGestureListener);
}
GestureDetector既然是監(jiān)聽我們的手勢(shì)的工具類,那我們是不是得把我們得手勢(shì)交給它呢? 是的?。?于是我們?cè)趏nTouchEvent中把事件交給GestureDetector:
@Override
public boolean onTouchEvent(MotionEvent event) {
return detector.onTouchEvent(event);
}
那我們把事件交給了GestureDetector,GestureDetector處理完畢后我們?cè)趺粗滥兀?還記得我們創(chuàng)建GestureDetector對(duì)象的時(shí)候傳遞的參數(shù)嗎?
detector=new GestureDetector(context,onGestureListener);
我們傳遞給了GestureDetector一個(gè)onGestureListener對(duì)象,GestureDetector檢查完畢手勢(shì)后,會(huì)調(diào)用onGestureListener中的方法進(jìn)行回調(diào),我們只需要在onGestureListener對(duì)象的相應(yīng)方法中作出處理就可以了:
private GestureDetector.SimpleOnGestureListener onGestureListener=new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.e(“TAG”, “====onSingleTapUp=====”);
return super.onSingleTapUp(e);
}
@Override
public void onLongPress(MotionEvent e) {
Log.e("TAG", "====onLongPress=====");
super.onLongPress(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.e("TAG", "====onScroll=====");
Log.e("TAG", "distanceX===>"+distanceX);
Log.e("TAG", "distanceY===>"+distanceY);
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public void onShowPress(MotionEvent e) {
Log.e("TAG", "====onShowPress=====");
super.onShowPress(e);
}
@Override
public boolean onDown(MotionEvent e) {
Log.e("TAG", "====onDown=====");
return super.onDown(e);
}
@Override
public boolean onDoubleTap(MotionEvent e) {
Log.e("TAG", "====onDoubleTap=====");
return super.onDoubleTap(e);
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
Log.e("TAG", "====onDoubleTapEvent=====");
return super.onDoubleTapEvent(e);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.e("TAG", "====onSingleTapConfirmed=====");
return super.onSingleTapConfirmed(e);
}
@Override
public boolean onContextClick(MotionEvent e) {
Log.e("TAG", "====onContextClick=====");
return super.onContextClick(e);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.e("TAG", "====onFling=====");
Log.e("TAG", "velocityX===>"+velocityX);
Log.e("TAG", "velocityY===>"+velocityY);
return super.onFling(e1, e2, velocityX, velocityY);
}
};
我們先不管其中方法啥時(shí)候調(diào)用,我們先重寫它的所有方法,然后打上log,看看我們手指操作后相應(yīng)的回調(diào),于是我們運(yùn)行代碼:

是的,沒錯(cuò)!就只是一張圖片,因?yàn)槲覀円仓皇秋@示了一張圖片:
我們輕輕的點(diǎn)擊一下屏幕:
03-02 20:47:41.367 1798-1798/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 20:47:41.466 1798-1798/com.leo.gestureimageview E/TAG: ====onShowPress=====
03-02 20:47:41.967 1798-1798/com.leo.gestureimageview E/TAG: ====onLongPress=====
輕輕的點(diǎn)擊一下屏幕:
我們可以看到log執(zhí)行順序:onDown->onShowPress->onLongPress
我們點(diǎn)擊屏幕按下,然后過一會(huì)再放開:
03-02 21:51:27.121 17138-17138/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 21:51:27.222 17138-17138/com.leo.gestureimageview E/TAG: ====onShowPress=====
03-02 21:51:27.722 17138-17138/com.leo.gestureimageview E/TAG: ====onLongPress=====
我們滑動(dòng)一下手指:
03-02 21:51:27.121 17138-17138/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 21:51:27.222 17138-17138/com.leo.gestureimageview E/TAG: ====onShowPress=====
03-02 21:51:27.722 17138-17138/com.leo.gestureimageview E/TAG: ====onLongPress=====
不管我們?cè)趺礃硬僮鳎蛴〉膌og總是這三個(gè)方法? 這是咋回事呢? 如果看到這里你有疑問的話,那我告訴你,你Android事件傳遞機(jī)制掌握的還不是很好,為什么這么說呢?? 下面我們帶著疑問看看源碼:
猜都可以猜到GestureDetector處理手勢(shì)的代碼肯定在onTouchEvent方法中,那么我們看一下onTouchEvent方法:
public boolean onTouchEvent(MotionEvent ev) {
if (mDoubleTapListener != null) {
boolean hadTapMessage = mHandler.hasMessages(TAP);
if (hadTapMessage) mHandler.removeMessages(TAP);
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
// This is a second tap
mIsDoubleTapping = true;
// Give a callback with the first tap of the double-tap
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
// Give a callback with down event of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else {
// This is a first tap
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
}
}
mDownFocusX = mLastFocusX = focusX;
mDownFocusY = mLastFocusY = focusY;
if (mCurrentDownEvent != null) {
mCurrentDownEvent.recycle();
}
mCurrentDownEvent = MotionEvent.obtain(ev);
mAlwaysInTapRegion = true;
mAlwaysInBiggerTapRegion = true;
mStillDown = true;
mInLongPress = false;
mDeferConfirmSingleTap = false;
if (mIsLongpressEnabled) {
mHandler.removeMessages(LONG_PRESS);
mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+ TAP_TIMEOUT + LONGPRESS_TIMEOUT);
}
mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
handled |= mListener.onDown(ev);
break;
}
代碼太多了,那為什么我們只收到了onDown、onShowPress、onLongPress這三個(gè)方法的回調(diào)呢?
我們知道,當(dāng)我們手指剛按下屏幕的時(shí)候,ACTION_DOWN會(huì)執(zhí)行,然后我們看到這么一行代碼:
handled |= mListener.onDown(ev);
mListener是我們傳遞的SimpleOnGestureListener,于是就看到了控制臺(tái)的第一個(gè)log:
03-02 21:51:29.706 17138-17138/com.leo.gestureimageview E/TAG: ====onDown=====
我們的onDown是打印了,然后handled |= mListener.onDown(ev);看一下我們返回的是什么值:
@Override
public boolean onDown(MotionEvent e) {
Log.e("TAG", "====onDown=====");
return super.onDown(e);
}
我們直接返回了super.onDown(e),接著我們看一下父類返回的是什么:
public boolean onDown(MotionEvent e) {
return false;
}
可以看到,父類直接返回了false,所以handled此時(shí)為false,然后當(dāng)ACTION_DOWN執(zhí)行完畢后,就回到了我們的自定義view中的onTouchEvent方法中了:
@Override
public boolean onTouchEvent(MotionEvent event) {
return detector.onTouchEvent(event);
}
此時(shí)我們的view中的onTouchEvent 方法返回的是false,到了這里懂事件傳遞機(jī)制的小伙伴都懂,當(dāng)我們的onTouchEvent返回了false的話,后面的事件都將接收不到了,也就是說只能執(zhí)行ACTION_DOWN,那么有些小伙伴可能又要說了,那我把view的clickable或者longclickable設(shè)置成true,事件不就可以傳遞了么?
好的~! 我們?cè)囈辉嚕?/p>
<com.leo.gestureimageview.MatrixImageView android:clickable="true" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="matrix" />
運(yùn)行代碼,還是只打印了那三個(gè)方法,那這又是怎么回事呢? 還記得我們view的onTouchEvent方法么?我們是這么寫的:
@Override
public boolean onTouchEvent(MotionEvent event) {
return detector.onTouchEvent(event);
}
如果改成這樣再試試:
@Override
public boolean onTouchEvent(MotionEvent event) {
detector.onTouchEvent(event);
return super.onTouchEvent(event);
}
拖動(dòng)手指返回結(jié)果:

好啦~??! 終于看到我們久違的結(jié)果了,如果我們還是想用以前的寫法,把onTouchEvent的返回結(jié)果交給GestureDetector處理該怎么做呢?
@Override
public boolean onTouchEvent(MotionEvent event) {
return detector.onTouchEvent(event);
}
我們只需要在回調(diào)方法的onDown中返回true即可:
@Override
public boolean onDown(MotionEvent e) {
Log.e("TAG", "====onDown=====");
return true;
}
我們?cè)俅芜\(yùn)行代碼并拖動(dòng)手指:

好啦~! 說了那么多不知道小伙伴們理解了沒?還是不理解的小伙伴可以去看看我前幾篇事件傳遞的博客,嘻嘻~我們還是快點(diǎn)往下走吧….
我們長(zhǎng)按一下屏幕然后提起手指:
03-02 22:29:37.361 22104-22104/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:29:37.367 22104-22104/com.leo.gestureimageview E/TAG: ====onSingleTapUp=====
03-02 22:29:37.663 22104-22104/com.leo.gestureimageview E/TAG: ====onSingleTapConfirmed=====
執(zhí)行了onDown=>onSingleTapUp=>onSingleTapConfirmed.
我們快速點(diǎn)擊一下屏幕:
03-02 22:31:48.603 22104-22104/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:31:48.610 22104-22104/com.leo.gestureimageview E/TAG: ====onSingleTapUp=====
03-02 22:31:48.903 22104-22104/com.leo.gestureimageview E/TAG: ====onSingleTapConfirmed=====
執(zhí)行了onDown=>onSingleTapUp=>onSingleTapConfirmed.
然后我們?cè)俅位瑒?dòng)手指:
03-02 22:34:41.820 22104-22104/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:34:41.920 22104-22104/com.leo.gestureimageview E/TAG: ====onShowPress=====
03-02 22:34:42.018 22104-22104/com.leo.gestureimageview E/TAG: ====onScroll=====
03-02 22:34:42.018 22104-22104/com.leo.gestureimageview E/TAG: distanceX===>-117.13138
03-02 22:34:42.018 22104-22104/com.leo.gestureimageview E/TAG: distanceY===>75.100464
03-02 22:34:42.036 22104-22104/com.leo.gestureimageview E/TAG: ====onScroll=====
03-02 22:34:42.036 22104-22104/com.leo.gestureimageview E/TAG: distanceX===>-75.859314
執(zhí)行順序:onDown=》onShowPress=》onScroll(很多次)
最后我們手指拖動(dòng)距離長(zhǎng)一點(diǎn)再快一點(diǎn):
03-02 22:47:42.453 5103-5103/com.leo.gestureimageview E/TAG: distanceX===>-274.69336
03-02 22:47:42.453 5103-5103/com.leo.gestureimageview E/TAG: distanceY===>-0.34838867
03-02 22:47:42.460 5103-5103/com.leo.gestureimageview E/TAG: ====onFling=====
03-02 22:47:42.460 5103-5103/com.leo.gestureimageview E/TAG: velocityX===>27284.943
03-02 22:47:42.460 5103-5103/com.leo.gestureimageview E/TAG: velocityY===>-95.6131
前 面還有一段log沒給出了,調(diào)用方法順序?yàn)椋簅nDown=》onShowPress=》onScroll(很多次)=》最后松開手指的時(shí)候onFling();
好了~! 到這里,我們的回調(diào)方法中還有幾個(gè)沒有被調(diào)用,就是監(jiān)聽雙擊事件的時(shí)候,于是我們雙擊屏幕:
03-02 22:50:34.786 5103-5103/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:50:34.793 5103-5103/com.leo.gestureimageview E/TAG: ====onSingleTapUp=====
03-02 22:50:34.924 5103-5103/com.leo.gestureimageview E/TAG: ====onDoubleTap=====
03-02 22:50:34.924 5103-5103/com.leo.gestureimageview E/TAG: ====onDoubleTapEvent=====
03-02 22:50:34.924 5103-5103/com.leo.gestureimageview E/TAG: ====onDown=====
03-02 22:50:34.932 5103-5103/com.leo.gestureimageview E/TAG: ====onDoubleTapEvent=====
我們雙擊屏幕執(zhí)行的方法為:
onDown=>onSingleTapUp=>onDoubleTap=>onDoubleTapEvent=>onDoubleTapEvent
可見,執(zhí)行了一次onDoubleTap,兩次onDoubleTapEvent
好啦~! 看完了SimpleOnGestureListener中所有方法的回調(diào),我們反過來再看一遍這些回調(diào)方法:

好啦~!說了那么api內(nèi)容,下面寫個(gè)小例子用一下GestureDetector:
package com.leo.gestureimageview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ImageView;
public class MatrixImageView extends ImageView {
private Matrix currMatrix;
private GestureDetector detector;
public MatrixImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
detector=new GestureDetector(context,onGestureListener);
}
private void initView() {
currMatrix = new Matrix();
DisplayMetrics dm = getResources().getDisplayMetrics();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
bitmap = Bitmap.createScaledBitmap(bitmap, dm.widthPixels, dm.heightPixels, true);
setImageBitmap(bitmap);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return detector.onTouchEvent(event);
}
private float currX;
private float currY;
private GestureDetector.SimpleOnGestureListener onGestureListener=new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.e("TAG", "====onScroll=====");
Log.e("TAG", "distanceX===>"+distanceX);
Log.e("TAG", "distanceY===>"+distanceY);
currX-=distanceX;
currY-=distanceY;
currMatrix.reset();
currMatrix.postTranslate(currX,currY);
setImageMatrix(currMatrix);
return super.onScroll(e1, e2, distanceX, distanceY);
}
};
}
代碼很短,想必大家都看得懂,就是一個(gè)隨著手指移動(dòng)而移動(dòng)的圖片:

好啦~~ 這篇有點(diǎn)長(zhǎng),為什么花這么久去研究GestureDetector,這也是為了給下一節(jié)的MoveGestureDetector、RotateGestureDetector、ShoveGestureDetector以及PhotoView這些大牛寫的框架做鋪墊。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android中ImageView.src設(shè)置圖片拉伸、填滿控件的方法
- Android自定義圓角ImageView控件
- Android ImageView 不顯示JPEG圖片的問題解決
- Android 自定義imageview實(shí)現(xiàn)圖片縮放實(shí)例詳解
- Android中ImageView實(shí)現(xiàn)選擇本地圖片并顯示功能
- Android自定義控件之圓形、圓角ImageView
- Android ImageView實(shí)現(xiàn)圖片裁剪和顯示功能
- Android實(shí)現(xiàn)ImageView陰影和圖層效果
- Android ImageView的selector效果實(shí)例詳解
相關(guān)文章
Android仿新浪微博oauth2.0授權(quán)界面實(shí)現(xiàn)代碼(2)
這篇文章主要為大家詳細(xì)介紹了Android仿新浪微博oauth2.0授權(quán)界面實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
Android自定義View onDraw()方法會(huì)調(diào)用兩次的問題解決
這篇文章主要介紹了Android自定義View onDraw()方法會(huì)調(diào)用兩次的問題解決,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-01-01
Android 實(shí)現(xiàn)帶進(jìn)度條的WebView的實(shí)例
這篇文章主要介紹了Android 實(shí)現(xiàn)帶進(jìn)度條的WebView的實(shí)例的相關(guān)資料,這里介紹了Webview加載網(wǎng)頁(yè)的方法及帶進(jìn)度的Drawable文件view_progress_webview的實(shí)現(xiàn),需要的朋友可以參考下2017-07-07
Android實(shí)現(xiàn)快速滾動(dòng)FastScrollView效果
這篇文章主要介紹了Android實(shí)現(xiàn)快速滾動(dòng)FastScrollView效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
Android中pendingIntent與Intent的深入分析
這篇文章主要介紹了Android中pendingIntent的深入分析的相關(guān)資料,需要的朋友可以參考下2017-04-04
Android內(nèi)存優(yōu)化操作方法梳理總結(jié)
這篇文章主要介紹了Android 內(nèi)存優(yōu)化知識(shí)點(diǎn)梳理總結(jié),Android 操作系統(tǒng)給每個(gè)進(jìn)程都會(huì)分配指定額度的內(nèi)存空間,App 使用內(nèi)存來進(jìn)行快速的文件訪問交互,長(zhǎng)時(shí)間如此便需要優(yōu)化策略,文章分享優(yōu)化知識(shí)點(diǎn)總結(jié),需要的朋友可以參考一下2022-11-11

