小工具知识网

当前位置: 首页 > 行业知识 > Android手势识别GestureDetector和ScaleGestureDetector介绍与使用,以自定义一个可拖拽拉伸的ImageView为例

行业知识

Android手势识别GestureDetector和ScaleGestureDetector介绍与使用,以自定义一个可拖拽拉伸的ImageView为例

2026-11-05 15:46:26 来源:互联网转载

一、GestureDetector

1. 简介

GestureDetector主要用于检测单指手势,例如单击、长按、滑动等,不支持多指手势。

2. SimpleOnGestureListener 内部类

GestureDetector.SimpleOnGestureListener 是用于处理手势事件的辅助类,它包含了一系列回调方法,用于处理不同类型的单指手势事件。下面是对每个回调方法的简要介绍:

  • onDown(MotionEvent e): 当用户按下(Down)手指时触发。这个方法返回 true 表示事件被消费了,false 表示未被消费。

  • onShowPress(MotionEvent e): 当用户按下并保持按压一段时间时触发。它表示按下动作已被识别,但尚未发生其它任何行为。

  • onSingleTapUp(MotionEvent e): 当用户轻击屏幕时触发。这个方法返回 true 表示事件被消费了,false 表示未被消费。

  • onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY): 当用户在屏幕上滚动时触发。它提供了滚动开始和结束时的事件信息,以及在X和Y方向上的距离差。

  • onLongPress(MotionEvent e): 当用户长按屏幕时触发。

  • onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY): 当用户迅速滑动手指并松开时触发。它提供了滑动开始和结束时的事件信息,以及在X和Y方向上的速度。

  • onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY): 当用户在屏幕上滚动时触发。与第四个回调方法不同,这个方法在滚动过程中持续触发,而不仅仅是在滚动结束时触发。

  • onDoubleTap(MotionEvent e): 当用户双击屏幕时触发。

  • onDoubleTapEvent(MotionEvent e): 当双击事件包含按下、移动和抬起动作时触发。通常与 onDoubleTap() 结合使用,以处理更复杂的双击手势。

  • onSingleTapConfirmed(MotionEvent e): 当确认发生了单击事件时触发。与 onSingleTapUp() 不同的是,这个方法确保了事件是单击事件而不是双击事件。

    这些回调方法提供了处理各种类型手势事件的灵活性,可以根据需求选择实现相应的方法来处理手势事件。

    3. 示例

    import android.content.Context;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;public class MyView extends View {    private GestureDetector gestureDetector;    public MyView(Context context, AttributeSet attrs) {        super(context, attrs);        // 实例化 GestureDetector,并传入 SimpleOnGestureListener 对象        gestureDetector = new GestureDetector(context, new MyGestureListener());    }    @Override    public boolean onTouchEvent(MotionEvent event) {        // 将触摸事件传递给 GestureDetector        return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event);    }    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {        @Override        public boolean onDown(MotionEvent e) {            // 用户按下屏幕时触发            return true; // 返回 true 表示事件被消费        }        @Override        public boolean onSingleTapConfirmed(MotionEvent e) {            // 单击事件确认时触发            return true;        }        @Override        public boolean onDoubleTap(MotionEvent e) {            // 双击事件时触发            return true;        }        @Override        public void onLongPress(MotionEvent e) {            // 长按事件时触发        }        @Override        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {            // 滚动事件时触发            return true;        }        // 其他手势事件处理方法...    }}

    二、ScaleGestureDetector

    1. 简介

    用于检测缩放手势,即双指捏合或者扩张的手势。它提供了 onScale() 和 onScaleBegin() 等回调方法来处理缩放手势的开始、进行中和结束时的事件。

    2. SimpleOnGestureListener 内部类

    ScaleGestureDetector.SimpleOnGestureListener 用于处理手势事件的辅助类,它包含了一系列回调方法,用于处理不同类型的双指手势事件。下面是对每个回调方法的简要介绍:

    • onScale(ScaleGestureDetector detector): 当缩放手势进行中时调用。这个方法会在缩放手势进行过程中持续调用,每次缩放都会触发。参数 detector 提供了有关缩放手势的信息,如当前的缩放因子等。

    • onScaleBegin(ScaleGestureDetector detector): 当缩放手势开始时调用。这个方法在缩放手势的第一次触发时调用,可以用来初始化缩放相关的状态。参数 detector 提供了有关缩放手势的信息。

    • onScaleEnd(ScaleGestureDetector detector): 当缩放手势结束时调用。这个方法在缩放手势结束后调用,可以用来清理缩放相关的状态。参数 detector 提供了有关缩放手势的信息。

      3. 示例

      import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.View;public class MyScaleView extends View {    private ScaleGestureDetector scaleGestureDetector;    private float scaleFactor = 1.0f;    public MyScaleView(Context context, AttributeSet attrs) {        super(context, attrs);        scaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener());    }    @Override    public boolean onTouchEvent(MotionEvent event) {        // 将触摸事件传递给 ScaleGestureDetector        scaleGestureDetector.onTouchEvent(event);        return true;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // 在画布上绘制内容,并根据 scaleFactor 进行缩放        canvas.scale(scaleFactor, scaleFactor, getWidth() / 2f, getHeight() / 2f);        // 绘制内容...    }    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {        @Override        public boolean onScale(ScaleGestureDetector detector) {            // 缩放因子的变化            scaleFactor *= detector.getScaleFactor();            // 限制缩放因子的范围(可选)            scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 5.0f));            // 重绘 View            invalidate();            return true;        }    }}

      三、自定义一个可拖拽和拉伸的ImageView

      1. 思路整理

      • 首先,我们可以直接继承 ImageView,并通过 Matrix 来控制图片的移动和拉伸。
      • 其次,使用 GestureDetector 监听移动的相关事件,使用 ScaleGestureDetector 监听拉伸的相关事件。
      • 最后,我们可能需要控制图片最大和最小缩放的比例。实际应用中还会考虑一些图片边界、双击放大、动画等,可根据需求自行添加。

        2. 示例

        import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.ColorDrawable;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import androidx.annotation.NonNull;import androidx.annotation.Nullable;import androidx.appcompat.widget.AppCompatImageView;public class DragZoomImageView extends AppCompatImageView {    private static final int NONE = 0;    private static final int DRAG = 1;    private static final int ZOOM = 2;    private int mMode = NONE;    private Matrix mFinalMatrix = new Matrix();    private Matrix mSavedMatrix = new Matrix();    // 图像以FitXY显示时使用的Scale大小    private float mOriginScale = 1.0f;    // 图像的最小、最大缩放比例    private float mMinScale = 0.5f;    private float mMaxScale = 5.0f;    private float mCurrentScale = 1.0f;    private Bitmap mBitmap;    private GestureDetector mGestureDetector;    private ScaleGestureDetector mScaleGestureDetector;    public DragZoomImageView(@NonNull Context context) {        super(context);        init();    }    public DragZoomImageView(@NonNull Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    public DragZoomImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        setScaleType(ScaleType.MATRIX);        mGestureDetector = new GestureDetector(getContext(), new GestureListener());        mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleListener());    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        Drawable drawable = getDrawable();        Bitmap bitmap = drawableToBitmap(drawable);        if (bitmap != null) {            Matrix matrix = getFitCenterMatrix(bitmap.getWidth(), bitmap.getHeight(), w, h);            setImageMatrix(matrix);            mOriginScale = getMatrixScaleX(matrix);            mBitmap = bitmap;        }    }    @Override    public void setImageMatrix(Matrix matrix) {        super.setImageMatrix(matrix);        mFinalMatrix.set(matrix);    }    @Override    public void setImageBitmap(Bitmap bm) {        if (bm == null) {            return;        }        super.setImageBitmap(bm);        Matrix matrix = getFitCenterMatrix(bm.getWidth(), bm.getHeight(), getWidth(), getHeight());        setImageMatrix(matrix);        mOriginScale = getMatrixScaleX(matrix);        mBitmap = bm;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        if (mBitmap == null) {            return false;        }        switch (event.getAction() & MotionEvent.ACTION_MASK) {            case MotionEvent.ACTION_DOWN:  // 单指                mMode = DRAG;                break;            case MotionEvent.ACTION_POINTER_DOWN:  // 多指                mMode = ZOOM;                break;        }        if (mMode == DRAG) {            mGestureDetector.onTouchEvent(event);        }        if (mMode == ZOOM) {            mScaleGestureDetector.onTouchEvent(event);        }        return true;    }    public void handleScale(float scale) {        handleScale(scale, getWidth() / 2, getHeight() / 2);    }    public void handleScale(float scale, float px, float py) {        if (scale  mMaxScale) {            scale = mMaxScale;        }        if (mCurrentScale == scale) {            return;        }        mCurrentScale = scale;  // record scale        float newScale = scale * mOriginScale;        float oldScale = getMatrixScaleX(mFinalMatrix);        float postScale = newScale / oldScale;        mFinalMatrix.postScale(postScale, postScale, px, py);        super.setImageMatrix(mFinalMatrix);    }    private float getMatrixScaleX(Matrix matrix) {        float[] values = new float[9];        matrix.getValues(values);        return values[Matrix.MSCALE_X];    }    private Matrix getFitCenterMatrix(int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight) {        Matrix matrix = new Matrix();        matrix.reset();        float scale;        float dx;        float dy;        scale = Math.min((float) viewWidth / (float) bitmapWidth, (float) viewHeight / (float) bitmapHeight);        dx = Math.round((viewWidth - bitmapWidth * scale) * 0.5f);        dy = Math.round((viewHeight - bitmapHeight * scale) * 0.5f);        matrix.setScale(scale, scale);        matrix.postTranslate(dx, dy);        return matrix;    }    private Bitmap drawableToBitmap(Drawable drawable) {        if (drawable == null) {            return null;        }        if (drawable instanceof BitmapDrawable) {            return ((BitmapDrawable) drawable).getBitmap();        } else if (drawable instanceof ColorDrawable) {            // 如果 Drawable 是 ColorDrawable,则创建一个相同大小的 Bitmap            ColorDrawable colorDrawable = (ColorDrawable) drawable;            int width = colorDrawable.getIntrinsicWidth();            int height = colorDrawable.getIntrinsicHeight();            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);            Canvas canvas = new Canvas(bitmap);            colorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());            colorDrawable.draw(canvas);            return bitmap;        } else {            // 如果 Drawable 不是 BitmapDrawable 或 ColorDrawable,则返回空            return null;        }    }    private class GestureListener extends GestureDetector.SimpleOnGestureListener {        @Override        public boolean onDown(MotionEvent e) {            mSavedMatrix.set(mFinalMatrix);            return super.onDown(e);        }        @Override        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {            Matrix matrix = new Matrix(mSavedMatrix);            float dx = e2.getX() - e1.getX();            float dy = e2.getY() - e1.getY();            matrix.postTranslate(dx, dy);            setImageMatrix(matrix);            return true;        }    }    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {        float px;        float py;        @Override        public boolean onScaleBegin(ScaleGestureDetector detector) {            px = detector.getFocusX();            py = detector.getFocusY();            return true;        }        @Override        public boolean onScale(ScaleGestureDetector detector) {            float scale = detector.getScaleFactor() * mCurrentScale;            handleScale(scale, px, py);            return true;        }    }}

上一篇:织梦管理员设置全攻略,一步步成为超级管理员!

下一篇:ASP网站源码入门教程,轻松搭建个人网站