安卓图片手势缩放、旋转、移动

两个ImageView都可以手势缩放、旋转、移动

自定义ImageView一

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewTreeObserver;
import androidx.appcompat.widget.AppCompatImageView;

public class MyImageView extends AppCompatImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener {
    /**
     * 控件宽度
     */
    private int mWidth;
    /**
     * 控件高度
     */
    private int mHeight;
    /**
     * 拿到src的图片
     */
    private Drawable mDrawable;
    /**
     * 图片宽度(使用前判断mDrawable是否null)
     */
    private int mDrawableWidth;
    /**
     * 图片高度(使用前判断mDrawable是否null)
     */
    private int mDrawableHeight;

    /**
     * 初始化缩放值
     */
    private float mScale;

    /**
     * 双击图片的缩放值
     */
    private float mDoubleClickScale;

    /**
     * 最大的缩放值
     */
    private float mMaxScale;

    /**
     * 最小的缩放值
     */
    private float mMinScale;

    private ScaleGestureDetector scaleGestureDetector;
    /**
     * 当前有着缩放值、平移值的矩阵。
     */
    private Matrix matrix;

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

    public MyImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnTouchListener(this);
        scaleGestureDetector = new ScaleGestureDetector(context, this);
        initListener();
    }


    /**
     * 初始化事件监听
     */
    private void initListener() {
        // 强制设置模式
        setScaleType(ScaleType.MATRIX);
        // 添加观察者
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                // 移除观察者
                getViewTreeObserver().removeGlobalOnLayoutListener(this);
                // 获取控件大小
                mWidth = getWidth();
                mHeight = getHeight();

                //通过getDrawable获得Src的图片
                mDrawable = getDrawable();
                if (mDrawable == null)
                    return;
                mDrawableWidth = mDrawable.getIntrinsicWidth();
                mDrawableHeight = mDrawable.getIntrinsicHeight();
                initImageViewSize();
                //moveToCenter();
            }
        });
    }

    /**
     * 初始化资源图片宽高
     */
    private void initImageViewSize() {
        if (mDrawable == null)
            return;

        // 缩放值
        float scale = 0.5f;
        /*// 图片宽度大于控件宽度,图片高度小于控件高度
        if (mDrawableWidth > mWidth && mDrawableHeight < mHeight)
            scale = mWidth * scale / mDrawableWidth;
            // 图片高度度大于控件宽高,图片宽度小于控件宽度
        else if (mDrawableHeight > mHeight && mDrawableWidth < mWidth)
            scale = mHeight * scale / mDrawableHeight;
            // 图片宽度大于控件宽度,图片高度大于控件高度
        else if (mDrawableHeight > mHeight && mDrawableWidth > mWidth)
            scale = Math.min(mHeight * scale / mDrawableHeight, mWidth * scale / mDrawableWidth);
            // 图片宽度小于控件宽度,图片高度小于控件高度
        else if (mDrawableHeight < mHeight && mDrawableWidth < mWidth)
            scale = Math.min(mHeight * scale / mDrawableHeight, mWidth * scale / mDrawableWidth);*/
        mScale = scale;
        mMaxScale = mScale * 8.0f;
        mMinScale = mScale * 0.1f;
    }

    /**
     * 移动控件中间位置
     */
    private void moveToCenter() {

        final float dx = mWidth / 2 ;
        final float dy = mHeight / 2;
        //final float dx = mWidth / 2 - mDrawableWidth / 2;
        //final float dy = mHeight / 2 - mDrawableHeight / 2;
        matrix = new Matrix();
        // 平移至中心
        matrix.postTranslate(dx, dy);
        // 以控件中心作为缩放
        matrix.postScale(mScale, mScale, mWidth / 2, mHeight / 2);
        setImageMatrix(matrix);
    }

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

    /**
     * @param matrix 矩阵
     * @return matrix的 l t b r 和width,height
     */
    private RectF getRectf(Matrix matrix) {
        RectF f = new RectF();
        if (mDrawable == null)
            return null;
        f.set(0, 0, mDrawableWidth, mDrawableHeight);
        matrix.mapRect(f);
        return f;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        if (mDrawable == 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;

        matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());

        RectF f = getRectf(matrix);
        float dX = 0.0f;
        float dY = 0.0f;
        // 图片高度大于控件高度
        if (f.height() >= mHeight) {
            // 图片顶部出现空白
            if (f.top > 0) {
                // 往上移动
                dY = -f.top;
            }
            // 图片底部出现空白
            if (f.bottom < mHeight) {
                // 往下移动
                dY = mHeight - f.bottom;
            }
        }
        // 图片宽度大于控件宽度
        if (f.width() >= mWidth) {
            // 图片左边出现空白
            if (f.left > 0) {
                // 往左边移动
                dX = -f.left;
            }
            // 图片右边出现空白
            if (f.right < mWidth) {
                // 往右边移动
                dX = mWidth - f.right;
            }
        }

        if (f.width() < mWidth) {
            dX = mWidth / 2 - f.right + f.width() / 2;
        }

        if (f.height() < mHeight) {
            dY = mHeight / 2 - f.bottom + f.height() / 2;
        }
        matrix.postTranslate(dX, dY);
        setImageMatrix(matrix);
        return true;
    }


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

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


    private float downX;
    private float downY;
    private float nowMovingX;
    private float nowMovingY;
    private float lastMovedX;
    private float lastMovedY;
    private boolean isFirstMoved = false;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                isFirstMoved = false;
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                isFirstMoved = false;
                break;
            case MotionEvent.ACTION_MOVE:
                nowMovingX = event.getX();
                nowMovingY = event.getY();
                if (!isFirstMoved) {
                    isFirstMoved = true;
                    lastMovedX = nowMovingX;
                    lastMovedY = nowMovingY;
                }
                float dX = 0.0f;
                float dY = 0.0f;
                RectF rectf = getRectf(matrix);
                // 判断滑动方向
                final float scrollX = nowMovingX - lastMovedX;
                // 判断滑动方向
                final float scrollY = nowMovingY - lastMovedY;
                // 图片高度大于控件高度
                if (rectf.height() > mHeight && canSmoothY()) {
                    dY = nowMovingY - lastMovedY;
                }

                // 图片宽度大于控件宽度
                if (rectf.width() > mWidth && canSmoothX()) {
                    dX = nowMovingX - lastMovedX;
                }
                matrix.postTranslate(dX, dY);

                remedyXAndY(dX,dY);

                lastMovedX = nowMovingX;
                lastMovedY = nowMovingY;
                break;
            case MotionEvent.ACTION_UP:
                break;
            case MotionEvent.ACTION_POINTER_UP:
                isFirstMoved = false;
                break;
        }
        return scaleGestureDetector.onTouchEvent(event);
    }

    /**
     * 判断x方向上能不能滑动
     * @return 可以滑动返回true
     */
    private boolean canSmoothX(){
        RectF rectf = getRectf(matrix);
        if (rectf.left >0 || rectf.right <getWidth())
            return false;
        return true;
    }

    /**
     * 判断y方向上可不可以滑动
     * @return 可以滑动返回true
     */
    private boolean canSmoothY(){
        RectF rectf = getRectf(matrix);
        if (rectf.top>0 || rectf.bottom < getHeight())
            return false;
        return true;
    }

    /**
     * 纠正出界的横和众线
     * @param dx 出界偏移的横线
     * @param dy 出街便宜的众线
     */
    private void remedyXAndY(float dx,float dy){
        if (!canSmoothX())
            matrix.postTranslate(-dx,0);
        if (!canSmoothY())
            matrix.postTranslate(0,-dy);
        setImageMatrix(matrix);
    }
}

自定义ImageView二

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;

import androidx.appcompat.widget.AppCompatImageView;

public class TouchImageView extends AppCompatImageView {

    private PointF startPoint = new PointF();
    private Matrix matrix = new Matrix();
    private Matrix currentMaritx = new Matrix();
    PointF mid = new PointF();
    private int mode = 0;//用于标记模式
    private static final int DRAG = 1;//拖动
    private static final int ZOOM = 2;//放大
    private float startDis = 0;
    private PointF midPoint;//中心点
    float oldRotation = 0;

    public TouchImageView(Context context){
        super(context);
    }

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

    public TouchImageView(Context context,AttributeSet paramAttributeSet){
        super(context,paramAttributeSet);
    }

    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mode = DRAG;
                currentMaritx.set(this.getImageMatrix());//记录ImageView当期的移动位置
                startPoint.set(event.getX(),event.getY());//开始点
                break;
            case MotionEvent.ACTION_MOVE://移动事件

                if (mode == DRAG) {//图片拖动事件
                    float dx = event.getX() - startPoint.x;//x轴移动距离
                    float dy = event.getY() - startPoint.y;
                    matrix.set(currentMaritx);//在当前的位置基础上移动

                    matrix.postTranslate(dx, dy);

                } else if(mode == ZOOM){//图片放大事件
                    float rotation = rotation(event) - oldRotation;

                    float endDis = distance(event);//结束距离
                    if(endDis > 10f){
                        float scale = endDis / startDis;//放大倍数
                        matrix.set(currentMaritx);
                        matrix.postScale(scale, scale, midPoint.x, midPoint.y);
                        matrix.postRotate(rotation, midPoint.x, midPoint.y);// 旋轉
                    }
                }
                break;

            case MotionEvent.ACTION_UP:
                mode = 0;
                break;
                //有手指离开屏幕,但屏幕还有触点(手指)
            case MotionEvent.ACTION_POINTER_UP:
                mode = 0;
                break;
            //当屏幕上已经有触点(手指),再有一个手指压下屏幕
            case MotionEvent.ACTION_POINTER_DOWN:
                oldRotation = rotation(event);

                mode = ZOOM;
                startDis = distance(event);

                if(startDis > 10f){
                    midPoint = mid(event);
                    currentMaritx.set(this.getImageMatrix());//记录当前的缩放倍数
                }

                break;
        }
        this.setImageMatrix(matrix);
        return true;
    }

    // 取旋转角度
    private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);
        return (float) Math.toDegrees(radians);
    }


    // 取手势中心点
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }
    /**
     * 两点之间的距离
     * @param event
     * @return
     */
    private static float distance(MotionEvent event){
        //两根线的距离
        float dx = event.getX(1) - event.getX(0);
        float dy = event.getY(1) - event.getY(0);
        return (float) Math.sqrt(dx*dx + dy*dy);
    }
    /**
     * 计算两点之间中心点的距离
     * @param event
     * @return
     */
    private static PointF mid(MotionEvent event){
        float midx = event.getX(1) + event.getX(0);
        float midy = event.getY(1) - event.getY(0);

        return new PointF(midx/2, midy/2);
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android中可以使用ImageView控件来显示图片,并且可以通过一些手势识别库,如GestureDetector来实现图片缩放移动旋转等操作。以下是一个简单的实现示例: 1. 首先在xml布局文件中添加一个ImageView控件,并设置其src属性为需要显示的图片资源文件: ```xml <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/my_image" /> ``` 2. 在Java代码中,获取ImageView控件的引用,并设置其可缩放、可移动和可旋转: ```java ImageView imageView = findViewById(R.id.imageView); imageView.setScaleType(ImageView.ScaleType.MATRIX); imageView.setOnTouchListener(new TouchListener()); ``` 其中,设置ImageView的ScaleType为MATRIX可以使其支持矩阵变换,从而实现图片缩放移动旋转等操作。设置一个TouchListener来监听手势操作。 3. 实现TouchListener中的事件处理方法,用来识别手势操作并执行相应的变换操作: ```java private class TouchListener implements View.OnTouchListener { private Matrix matrix = new Matrix(); private Matrix savedMatrix = new Matrix(); private PointF start = new PointF(); private PointF mid = new PointF(); private float oldDistance; private float oldRotation; @Override public boolean onTouch(View v, MotionEvent event) { ImageView imageView = (ImageView) v; switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: savedMatrix.set(matrix); start.set(event.getX(), event.getY()); break; case MotionEvent.ACTION_POINTER_DOWN: oldDistance = getDistance(event); oldRotation = getRotation(event); savedMatrix.set(matrix); getMidPoint(mid, event); break; case MotionEvent.ACTION_MOVE: if (event.getPointerCount() == 1) { matrix.set(savedMatrix); matrix.postTranslate(event.getX() - start.x, event.getY() - start.y); } else if (event.getPointerCount() >= 2) { float newDistance = getDistance(event); float newRotation = getRotation(event); matrix.set(savedMatrix); matrix.postScale(newDistance / oldDistance, newDistance / oldDistance, mid.x, mid.y); matrix.postRotate(newRotation - oldRotation, mid.x, mid.y); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: break; } imageView.setImageMatrix(matrix); return true; } private float getDistance(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); } private float getRotation(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.toDegrees(Math.atan2(y, x)); } private void getMidPoint(PointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); } } ``` 其中,onTouch方法中根据不同的手势操作执行相应的矩阵变换操作。用savedMatrix保存当前的矩阵,用matrix进行变换,并将变换后的矩阵设置回ImageView控件中。 这样就可以实现图片缩放移动旋转等操作了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值