016-Android自定义View(11):手势监听处理实践之双指缩放双击复原ImageView

1,519 阅读2分钟

1.效果

1.拖拽

2.双击复原、双指缩放

3.点击回调、关闭activity

2.监听手势实现功能

  1. 双指缩放
  2. 拖拽移动
  3. 点击事件回调(查看大图的Activity中可以finish)
  4. 双击事件回调,图片复原

3.用法及代码详解

3.1属性添加

/res/value/attrs.xml添加属性节点

 <declare-styleable name="ScaleImageView">
        <attr name="img_src" format="reference" />
    </declare-styleable>

3.2xml布局文件中使用

留意使用自定义属性

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
        <com.cupster.base_super_resource.gesturedetector.ScaleImageView
            android:id="@+id/tv_words"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:minHeight="120dp"
            android:minWidth="120dp"
            app:img_src="@mipmap/pic_drinking"
            android:layout_centerInParent="true"
            />
</RelativeLayout>

3.3Activity使用

public class ScaleGesTureActivity extends AppCompatActivity implements ScaleImageView.OperateListener{
     ScaleImageView mTvGesture;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gesture_detector);
        mTvGesture = findViewById(R.id.tv_words);
        mTvGesture.setOperateListener( this);
    }

    @Override
    public void onClicked() {
        finish();
    }

    @Override
    public void onDoubleClicked() {

    }
}

3.4ScaleImageView详解

package com.cupster.base_super_resource.gesturedetector;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;

import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;

import android.widget.ImageView;

import com.cupster.base_super_resource.R;


public class ScaleImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,  GestureDetector.OnDoubleTapListener,GestureDetector.OnGestureListener {

    private ScaleGestureDetector scaleGestureDetector;
    private GestureDetector gestureDetector;

    Bitmap mBitmap;         // 图片
    RectF mBitmapRectF;     // 图片所在区域
    Matrix mBitmapMatrix;   // 控制图片的 matrix
    /**
     * 图片宽度
     */
    private int mBitmapWidth;
    /**
     * 图片高度
     */
    private int mBitmapHeight;
    /**
     * 控件宽度
     */
    private int mWidth;
    /**
     * 控件高度
     */
    private int mHeight;
    boolean canDrag = false;
    PointF lastPoint = new PointF(0, 0);

    public ScaleImageView(Context context) {
        this(context, null);
    }

    public ScaleImageView(Context context, AttributeSet attrs) {
        super(context, attrs);

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.outWidth = 960/2;
        options.outHeight = 800/2;

        mDeafultPaint = new Paint();
        TypedArray typedArray = context.obtainStyledAttributes(attrs ,R.styleable.ScaleImageView);
        int resId = typedArray.getResourceId(R.styleable.ScaleImageView_img_src , R.mipmap.ic_launcher);
        typedArray.recycle();
        mBitmap = BitmapFactory.decodeResource(this.getResources(), resId, options);
        mBitmapWidth = mBitmap.getWidth();
        mBitmapHeight = mBitmap.getHeight();
        mBitmapRectF = new RectF(0,0,mBitmap.getWidth(), mBitmap.getHeight());
        mBitmapMatrix = new Matrix();

        scaleGestureDetector = new ScaleGestureDetector(context, this);
        gestureDetector = new GestureDetector(context ,this);
        gestureDetector.setIsLongpressEnabled(true);
        gestureDetector.setOnDoubleTapListener(this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        mWidth = getWidth();
        mHeight = getHeight();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        gestureDetector.onTouchEvent(event);
        scaleGestureDetector.onTouchEvent(event);
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                // 是否第一个手指 && 点是否在图片内
                if (event.getPointerId(event.getActionIndex()) == 0 && mBitmapRectF.contains((int)event.getX(), (int)event.getY())){
                    canDrag = true;
                    lastPoint.set(event.getX(), event.getY());
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                // 是否第一个手指
                if (event.getPointerId(event.getActionIndex()) == 0){
                    canDrag = false;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                // 如果存在第一个手指,且这个手指点在图片内,则拖动绘制
                if (canDrag) {
                    int index = event.findPointerIndex(0);
                    mBitmapMatrix.postTranslate(event.getX(index)-lastPoint.x, event.getY(index)-lastPoint.y);
                    lastPoint.set(event.getX(index), event.getY(index));
                    mBitmapRectF = new RectF(0,0,mBitmap.getWidth(), mBitmap.getHeight());
                    mBitmapMatrix.mapRect(mBitmapRectF);
                    invalidate();
                }
                break;
        }

        return true;
    }

    Paint mDeafultPaint;
    boolean isFirstDraw = true;
    @Override
    protected void onDraw(Canvas canvas) {
        if (isFirstDraw){//初始位置居中
            isFirstDraw = false;
            mBitmapMatrix.setScale(1.0f,1.0f , mBitmapWidth /2 , mBitmapHeight /2);
            mBitmapMatrix.setTranslate(mWidth/2 - mBitmapWidth /2,mHeight/2 - mBitmapHeight /2);
            mBitmapRectF = new RectF(0,0,mBitmap.getWidth(), mBitmap.getHeight());
            mBitmapMatrix.mapRect(mBitmapRectF);
        }
        canvas.drawBitmap(mBitmap, mBitmapMatrix, mDeafultPaint);
    }

    float mScale = 1.0f;
    float mMaxScale = mScale * 8.0f;
    float mMinScale = mScale * 0.5f;
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        if (mBitmap == null) {
            return true;
        }
        // 缩放因子
        float scaleFactor = detector.getScaleFactor();
        // 已经缩放值
        float scale = getmScale();
        float scaleResult = scale * scaleFactor;
        if (scaleResult >= mMaxScale && scaleFactor > 1.0f)
            scaleFactor = mMaxScale / scale;
        if (scaleResult <= mMinScale && scaleFactor < 1.0f)
            scaleFactor = mMinScale / scale;
        mBitmapMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
        setImageMatrix(mBitmapMatrix);
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
        float scale = getmScale();
        if (scale < mScale) {
            mBitmapMatrix.postScale(mScale / scale, mScale / scale, mWidth / 2, mHeight / 2);
            setImageMatrix(mBitmapMatrix);
        }
    }

    /**
     * 当前缩放的值
     */
    private float getmScale() {
        float[] floats = new float[9];
        mBitmapMatrix.getValues(floats);
        return floats[Matrix.MSCALE_X];
    }

    public interface OperateListener {
        void onClicked();
        void onDoubleClicked();
    }

    OperateListener operateListener;

    public void setOperateListener(OperateListener operateListener) {
        this.operateListener = operateListener;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent motionEvent) {//单击事件回调
        if (operateListener != null){
            operateListener.onClicked();
        }
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent motionEvent) {
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent motionEvent) {//双击事件回调,回调2次
        if (operateListener != null){
            operateListener.onDoubleClicked();
        }
        isFirstDraw =true;
        invalidate();
        return true;
    }

    @Override
    public boolean onDown(MotionEvent motionEvent) {//按下那一刻
        return true;
    }

    @Override
    public void onShowPress(MotionEvent motionEvent) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent motionEvent) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent motionEvent) {

    }

    @Override
    public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
        return false;
    }
}