Android自定义可缩放的ImageView,长按可弹出菜单

参考了一些网上案例,按照自己的逻辑写的,自认为逻辑还算清晰,代码量也不大。

使用方式和ImageView相似,但其缩放标签失效

 

public class ZoomImageView extends android.support.v7.widget.AppCompatImageView implements Runnable {

    private boolean isLoadImg;
    private int mode;
    private int firstPointerId;
    private int secondPointerId;
    private float[] values = new float[9];//存储matrix数据
    private float minScale, maxScale;
    private long lastClickTime;
    private final long TIME_DELAY = 200;
    private static final int DRAG = 1, ZOOM = 2;
    private Paint paint;
    private Matrix matrix;
    private Bitmap bitmap;
    private Point p1 = new Point();
    private Point p2 = new Point();

    public ZoomImageView(Context context) {
        this(context, null, 0);
    }

    public ZoomImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ZoomImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        isLoadImg = true;
        paint = new Paint();
        BitmapDrawable drawable = (BitmapDrawable) getDrawable();
        if (drawable != null) {
            bitmap = drawable.getBitmap();
        }
    }

    //reset the image to the initial state
    public void reset() {
        isLoadImg = true;
        invalidate();
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        bitmap = bm;
        isLoadImg = true;
        invalidate();
    }

    @Override
    public void setImageDrawable(@Nullable Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
            setImageBitmap(bitmap);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (isLoadImg) {
            matrix = new Matrix();
            float sx = getWidth() * 1.0f / bitmap.getWidth();
            float sy = getHeight() * 1.0f / bitmap.getHeight();
            if (sx > sy) {
                minScale = sy;
                matrix.postScale(sy, sy);
                matrix.postTranslate((getWidth() - sy * bitmap.getWidth()) / 2, 0);
            } else {
                minScale = sx;
                matrix.postScale(sx, sx);
                matrix.postTranslate(0, (getHeight() - sx * bitmap.getHeight()) / 2);
            }
            minScale = minScale > 1 ? 1 : minScale;
            maxScale = minScale * 10;
        }
        canvas.drawBitmap(bitmap, matrix, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        isLoadImg = false;
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                postDelayed(this, 1500);//longClick
                firstPointerId = event.getPointerId(event.getActionIndex());
                mode = DRAG;
                p1.setXY(event.getX(), event.getY());
                onDoubleClick();
                lastClickTime = System.currentTimeMillis();
                break;
            case MotionEvent.ACTION_MOVE:
                if (distance(event.getX(), event.getY(), p1.x, p1.y) > 5) {//判断手指移动距离,超过则取消长按
                    removeCallbacks(this);
                }
                matrix.getValues(values);
                if (mode == DRAG) {//平移模式
                    float dx = event.getX() - p1.x;
                    float dy = event.getY() - p1.y;
                    dx = checkXboundary(dx);
                    dy = checkYboundary(dy);
                    matrix.postTranslate(dx, dy);
                    p1.setXY(event.getX(), event.getY());
                } else if (mode == ZOOM) {//缩放模式
                    int index1 = event.findPointerIndex(firstPointerId);
                    int index2 = event.findPointerIndex(secondPointerId);
                    float x1 = event.getX(index1);
                    float y1 = event.getY(index1);
                    float x2 = event.getX(index2);
                    float y2 = event.getY(index2);
//                    float scale = (x2 - x1) / (p2.x - p1.x);
                    float scale = (float) (distance(x1, y1, x2, y2) / distance(p1.x, p1.y, p2.x, p2.y));
                    if (scale * values[Matrix.MSCALE_Y] < minScale)
                        scale = minScale / values[Matrix.MSCALE_Y];
                    else if (scale * values[Matrix.MSCALE_Y] > maxScale)
                        scale = maxScale / values[Matrix.MSCALE_Y];
                    //长宽均小于控件,则居中显示
                    if (values[Matrix.MSCALE_X] * bitmap.getWidth() * scale < getWidth() && values[Matrix.MSCALE_X] * bitmap.getHeight() * scale < getHeight()) {
                        matrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);
                    } else
                        matrix.postScale(scale, scale, (x1 + x2) / 2, (y1 + y2) / 2);
                    p1.setXY(x1, y1);
                    p2.setXY(x2, y2);
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                removeCallbacks(this);
                mode = 0;
                break;
            case MotionEvent.ACTION_POINTER_DOWN://第二个手指或更多手指按下
                if (event.getPointerCount() == 2) {//第二根手指
                    int index = event.getActionIndex();
                    secondPointerId = event.getPointerId(index);
                    p2.setXY(event.getX(index), event.getY(index));
                }
                mode = ZOOM;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                mode = 0;
                break;
        }

        return true;
    }

    //distance between (x1,y1) and (x2,y2)
    private double distance(float x1, float y1, float x2, float y2) {
        return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    }

    //双击进行图片缩放
    private void onDoubleClick() {
        if (System.currentTimeMillis() - lastClickTime < TIME_DELAY) {
            matrix.getValues(values);
            float dx = (getWidth() - bitmap.getWidth() * values[Matrix.MSCALE_X]) / 2 - values[Matrix.MTRANS_X];
            float dy = (getHeight() - bitmap.getHeight() * values[Matrix.MSCALE_Y]) / 2 - values[Matrix.MTRANS_Y];
            matrix.postTranslate(dx, dy);//移到控件中心
            float scale = 1;
            if (values[Matrix.MSCALE_Y] < maxScale)
                scale = maxScale / values[Matrix.MSCALE_Y];
            else
                scale = minScale / values[Matrix.MSCALE_Y];
            matrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);
            invalidate();
        }
    }

    private float checkYboundary(float dy) {
        if (bitmap.getHeight() * values[Matrix.MSCALE_Y] < getHeight()) {//纵向向未填满,则纵向居中
            dy = (getHeight() - bitmap.getHeight() * values[Matrix.MSCALE_Y]) / 2 - values[Matrix.MTRANS_Y];
        } else {
            if (values[Matrix.MTRANS_Y] + dy >= 0)
                dy = -values[Matrix.MTRANS_Y];
            else if (values[Matrix.MTRANS_Y] + bitmap.getHeight() * values[Matrix.MSCALE_Y] + dy < getHeight())
                dy = getHeight() - values[Matrix.MTRANS_Y] - bitmap.getHeight() * values[Matrix.MSCALE_Y];
        }
        return dy;
    }

    private float checkXboundary(float dx) {
        if (bitmap.getWidth() * values[Matrix.MSCALE_X] < getWidth()) {//横向向未填满,则横向居中
            dx = (getWidth() - bitmap.getWidth() * values[Matrix.MSCALE_X]) / 2 - values[Matrix.MTRANS_X];
        } else {//不允许拉出空白区域
            if (values[Matrix.MTRANS_X] + dx >= 0)
                dx = -values[Matrix.MTRANS_X];
            else if (values[Matrix.MTRANS_X] + bitmap.getWidth() * values[Matrix.MSCALE_X] + dx < getWidth())
                dx = getWidth() - values[Matrix.MTRANS_X] - bitmap.getWidth() * values[Matrix.MSCALE_X];
        }
        return dx;
    }

    @Override
    public void run() {//显示上下文菜单
        super.showContextMenu();
    }

    public class Point {
        public float x;
        public float y;

        public void setXY(float x, float y) {
            this.x = x;
            this.y = y;
        }
    }
}

进行了升级,可支持gif图的缩放及横竖屏切换,Glide加载gif测试通过

public class ZoomImageView extends android.support.v7.widget.AppCompatImageView implements Runnable {

    private static final int DEFAULT_LONG_PRESS_TIMEOUT = 500;
    private boolean isLoadImg;
    private boolean blockClick;//是否截留单击事件
    private int mode;
    private float[] values = new float[9];//存储matrix数据
    private float minScale, maxScale;
    private long lastClickTime;
    private static final long TIME_DELAY = 200;
    private static final int DRAG = 1, ZOOM = 2;
    private Matrix matrix;
    private Drawable drawable;
    private Point p0, p1, p2;

    public ZoomImageView(Context context) {
        this(context, null, 0);
    }

    public ZoomImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ZoomImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        isLoadImg = true;
        p0 = new Point();
        p1 = new Point();
        p2 = new Point();
        matrix = new Matrix();
        drawable = getDrawable();
    }


    //reset the image to the initial state
    public void reset() {
        isLoadImg = true;
        invalidate();
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        matrix.reset();
        isLoadImg = true;
        invalidate();
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        setImageDrawable(new BitmapDrawable(bm));
    }

    @Override
    public void setImageDrawable(@Nullable Drawable drawable) {
        super.setImageDrawable(drawable);
        this.drawable = drawable;
        isLoadImg = true;
        matrix.reset();//重置matrix
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (isLoadImg && drawable != null) {

            int iWidth = drawable.getIntrinsicWidth();
            int iHeight = drawable.getIntrinsicHeight();
            float sx = getWidth() * 1.0f / iWidth;
            float sy = getHeight() * 1.0f / iHeight;
            if (sx > sy) {
                minScale = sy;
                matrix.postScale(sy, sy);
                matrix.postTranslate((getWidth() - sy * iWidth) / 2, 0);
            } else {
                minScale = sx;
                matrix.postScale(sx, sx);
                matrix.postTranslate(0, (getHeight() - sx * iHeight) / 2);
            }
            minScale = minScale > 1 ? 1 : minScale;
            maxScale = minScale * 10;
            isLoadImg = false;
        }
        canvas.concat(matrix);

        if (drawable != null) {
            drawable.draw(canvas);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                postDelayed(this, DEFAULT_LONG_PRESS_TIMEOUT);//longClick
                mode = DRAG;
                p0.setXY(event.getX(), event.getY());
                p1.setXY(event.getX(), event.getY());
                if (drawable != null) {
                    onDoubleClick(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
                }
                lastClickTime = System.currentTimeMillis();
                break;
            case MotionEvent.ACTION_MOVE:
                if (drawable == null)
                    return true;
                if (distance(event.getX(), event.getY(), p1.x, p1.y) > 8) {//判断手指移动距离,超过则取消长按
                    removeCallbacks(this);
                }
                matrix.getValues(values);
                if (mode == DRAG) {//平移模式
                    float dx = event.getX() - p1.x;
                    float dy = event.getY() - p1.y;
                    dx = checkXboundary(dx, drawable.getIntrinsicWidth());
                    dy = checkYboundary(dy, drawable.getIntrinsicHeight());
                    matrix.postTranslate(dx, dy);
                    p1.setXY(event.getX(), event.getY());
                } else if (mode == ZOOM) {//缩放模式
                    float x1 = event.getX(0);
                    float y1 = event.getY(0);
                    float x2 = event.getX(1);
                    float y2 = event.getY(1);
                    float scale = (float) (distance(x1, y1, x2, y2) / distance(p1.x, p1.y, p2.x, p2.y));
                    if (scale * values[Matrix.MSCALE_Y] < minScale)
                        scale = minScale / values[Matrix.MSCALE_Y];
                    else if (scale * values[Matrix.MSCALE_Y] > maxScale)
                        scale = maxScale / values[Matrix.MSCALE_Y];
                    //长宽均小于控件,则居中显示
                    if (values[Matrix.MSCALE_X] * drawable.getIntrinsicWidth() * scale < getWidth() && values[Matrix.MSCALE_X] * drawable.getIntrinsicHeight() * scale < getHeight()) {
                        matrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);
                    } else
                        matrix.postScale(scale, scale, (x1 + x2) / 2, (y1 + y2) / 2);
                    p1.setXY(x1, y1);
                    p2.setXY(x2, y2);
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                removeCallbacks(this);
                if (blockClick) {
                    blockClick = false;
                } else if (distance(event.getX(), event.getY(), p0.x, p0.y) < 8) {
                    performClick();//调用onClickListener
                }
                matrix.getValues(values);
                if (drawable == null) {
                    mode = 0;
                    return true;
                }
                float dx = checkXboundary(0, drawable.getIntrinsicWidth());
                float dy = checkYboundary(0, drawable.getIntrinsicHeight());
                matrix.postTranslate(dx, dy);
                invalidate();
                mode = 0;
                break;
            case MotionEvent.ACTION_POINTER_DOWN://第二个手指或更多手指按下
                removeCallbacks(this);
                if (event.getPointerCount() == 2) {//第二根手指
                    p2.setXY(event.getX(1), event.getY(1));
                }
                mode = ZOOM;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                mode = 0;//不用DRAG,避免两根手指先后移开导致的图片移动
                break;
        }

        return true;
    }

    //distance between (x1,y1) and (x2,y2)
    private double distance(float x1, float y1, float x2, float y2) {
        return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    }

    //双击进行图片缩放
    private void onDoubleClick(int width, int height) {
        if (System.currentTimeMillis() - lastClickTime < TIME_DELAY) {
            blockClick = true;
            matrix.getValues(values);
            float dx = (getWidth() - width * values[Matrix.MSCALE_X]) / 2 - values[Matrix.MTRANS_X];
            float dy = (getHeight() - height * values[Matrix.MSCALE_Y]) / 2 - values[Matrix.MTRANS_Y];
            matrix.postTranslate(dx, dy);//移到控件中心
            float scale = 1;
            if (values[Matrix.MSCALE_Y] < maxScale)
                scale = maxScale / values[Matrix.MSCALE_Y];
            else
                scale = minScale / values[Matrix.MSCALE_Y];
            matrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);
            invalidate();
        }
    }

    private float checkYboundary(float dy, int height) {
        if (height * values[Matrix.MSCALE_Y] < getHeight()) {//纵向向未填满,则纵向居中
            dy = (getHeight() - height * values[Matrix.MSCALE_Y]) / 2 - values[Matrix.MTRANS_Y];
        } else {
            if (values[Matrix.MTRANS_Y] + dy >= 0)
                dy = -values[Matrix.MTRANS_Y];
            else if (values[Matrix.MTRANS_Y] + height * values[Matrix.MSCALE_Y] + dy < getHeight())
                dy = getHeight() - values[Matrix.MTRANS_Y] - height * values[Matrix.MSCALE_Y];
        }
        return dy;
    }

    private float checkXboundary(float dx, int width) {
        if (width * values[Matrix.MSCALE_X] < getWidth()) {//横向向未填满,则横向居中
            dx = (getWidth() - width * values[Matrix.MSCALE_X]) / 2 - values[Matrix.MTRANS_X];
        } else {//不允许拉出空白区域
            if (values[Matrix.MTRANS_X] + dx >= 0)
                dx = -values[Matrix.MTRANS_X];
            else if (values[Matrix.MTRANS_X] + width * values[Matrix.MSCALE_X] + dx < getWidth())
                dx = getWidth() - values[Matrix.MTRANS_X] - width * values[Matrix.MSCALE_X];
        }
        return dx;
    }

    @Override
    public void run() {//显示上下文菜单
        blockClick = super.showContextMenu();
    }

    public class Point {
        float x;
        float y;

        void setXY(float x, float y) {
            this.x = x;
            this.y = y;
        }
    }
}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要实现这个要求,你可以自定义一个继承自 ImageView 的类,并在其中重写 onMeasure 方法来控制 ImageView 的最大度和高度。同时,在加载图片时,你可以根据图片的宽高比例来计算缩放比例,然后使用 Matrix 对图片进行缩放操作。 下面是一个示例代码: ```java public class CustomImageView extends ImageView { private int mMaxWidth; // 最大宽度 private int mMaxHeight; // 最大高度 public CustomImageView(Context context) { super(context); init(); } public CustomImageView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { // 设置默认的最大宽度和高度 mMaxWidth = Integer.MAX_VALUE; mMaxHeight = Integer.MAX_VALUE; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 获取 ImageView 的测量模式和尺寸 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); // 计算根据最大宽度和高度得到的宽度和高度 int maxWidth = Math.min(widthSize, mMaxWidth); int maxHeight = Math.min(heightSize, mMaxHeight); int scaledWidth = widthSize; int scaledHeight = heightSize; // 根据图片的宽高比例计算缩放比例 Drawable drawable = getDrawable(); if (drawable != null) { int imageWidth = drawable.getIntrinsicWidth(); int imageHeight = drawable.getIntrinsicHeight(); float scale = Math.min((float) maxWidth / imageWidth, (float) maxHeight / imageHeight); scaledWidth = (int) (imageWidth * scale); scaledHeight = (int) (imageHeight * scale); } // 根据测量模式设置最终的宽度和高度 int finalWidth = (widthMode == MeasureSpec.EXACTLY) ? widthSize : scaledWidth; int finalHeight = (heightMode == MeasureSpec.EXACTLY) ? heightSize : scaledHeight; // 设置最终的宽度和高度 setMeasuredDimension(finalWidth, finalHeight); } public void setMaxSize(int maxWidth, int maxHeight) { mMaxWidth = maxWidth; mMaxHeight = maxHeight; } } ``` 你可以在布局文件中使用这个自定义ImageView,然后通过调用 `setMaxSize()` 方法来设置最大宽度和高度。当加载图片时,ImageView 会按照比例缩放图片来适应最大的宽度和高度限制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值