Android ImageView 翻转 裁切 缩放

平台

Android 7.1 + AndroidStudio 4.2.1 + Ubuntu 20.04

需求

  1. 支持左右/上下翻转
  2. 支持缩放
  3. 支持裁切为圆角/圆形

原图:
在这里插入图片描述

原图
圆形 + 缩放1
圆形 + 缩放1
圆形 + 缩放2圆形 + 缩放2
圆形 + 缩放3圆形 + 缩放3
圆角 + 翻转圆角 + 翻转


几个需要注意的点

  1. 要使用setImageMatrix生效, 必须设置缩放模式: setScaleType(ScaleType.MATRIX);
  2. 缩放模式, 当图片未填满横向或纵向时, 应以图像的大小来计算圆角和显示区域.
  3. 直接操作Matrix的数值会更方便float[9]
private final float[] mtxVal = new float[9];
mMatrix = getMatrix();
mMatrix.getValues(mtxVal);

对应值说明

public static final int MPERSP_0
Constant Value: 6 (0x00000006)

public static final int MPERSP_1
Constant Value: 7 (0x00000007)

public static final int MPERSP_2
Constant Value: 8 (0x00000008)

public static final int MSCALE_X
Constant Value: 0 (0x00000000)

public static final int MSCALE_Y
Constant Value: 4 (0x00000004)

public static final int MSKEW_X
Constant Value: 1 (0x00000001)

public static final int MSKEW_Y
Constant Value: 3 (0x00000003)

public static final int MTRANS_X
Constant Value: 2 (0x00000002)

public static final int MTRANS_Y
Constant Value: 5 (0x00000005)

源码

使用:

	//设置圆角弧度
    public void setRadius(float r)

    //指定显示区域, 圆形模式下无效, 与前面的setRadius组合使用
    public void setArea(float left, float top, float right, float bottom)

    //设置圆形显示
    public void setCircle(boolean b)

    //设置左右/上下翻转
    public void setFlip(boolean flipHorizontal, boolean flipVertical)

    //设置缩放模式
    public void setScaleMode(int mode)
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

import com.ansondroider.acore.Logger;

/**
 * @author ansondroider@foxmail.com
 */
public class RoundImageView extends ImageView {
    final String TAG = "RoundImageView";
    final boolean D = false;
    //[0]NO: 不缩放
    //[1]AUTO_FIT: 自适应, 横向填满或纵向填满,不超出视图边界
    //[2]MATCH_VIEW_WIDTH 横向填满
    //[3]MATCH_VIEW_HEIGHT 纵向填满
    //[4]BH2VW 图片高度放大到视图宽度
    //[5]BW2VH 图片宽度放大到视频高度.
    public static final int SCALE_MODE_NO = 0;
    public static final int SCALE_MODE_AUTOFIT = 1;
    public static final int SCALE_MODE_VW = 2;
    public static final int SCALE_MODE_VH = 3;
    public static final int SCALE_MODE_BW2VH = 4;
    public static final int SCALE_MODE_BH2VW = 5;
    public static final int INVALID_RADIUS = -1;

    private final float[] mtxVal = new float[9];
    private float radius;

    private Matrix mMatrix;
    private final RectF mViewport = new RectF();
    private RectF area = null;

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

    public RoundImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private float specRadius = INVALID_RADIUS;
    public void setRadius(float r){
        specRadius = r;
        updateMatrix();
    }

    //指定显示区域
    private RectF specArea = null;
    public void setArea(float left, float top, float right, float bottom){
        specArea = new RectF(left, top, right, bottom);
    }

    //设置圆形显示
    private boolean isCircle;
    public void setCircle(boolean b){
        isCircle = b;
        //postInvalidate();
        updateMatrix();
    }

    //设置左右/上下翻转
    boolean flipH, flipV;
    public void setFlip(boolean flipHorizontal, boolean flipVertical){
        flipH = flipHorizontal;
        flipV = flipVertical;
        updateMatrix();
    }

    public boolean isFlipHorizontal(){return flipH;}
    public boolean isFlipVertical(){return flipV;}

    //设置缩放模式
    private int scaleMode = SCALE_MODE_AUTOFIT;
    public void setScaleMode(int mode){
        scaleMode = mode;
        updateMatrix();
    }
    public int getScaleMode(){return scaleMode;}

    private int bitmapWidth, bitmapHeight;
    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        if(bm != null && !bm.isRecycled()){
            bitmapWidth = bm.getWidth();
            bitmapHeight = bm.getHeight();
            updateMatrix();
        }
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        if(drawable != null){
            bitmapWidth = drawable.getIntrinsicWidth();
            bitmapHeight = drawable.getIntrinsicHeight();
            updateMatrix();
        }
    }

    //更新矩阵
    private void updateMatrix(){
        if(D)Logger.d(TAG, "updateMatrix " + scaleMode);
        if(mViewport == null || mMatrix == null || bitmapWidth <= 0 || bitmapHeight <= 0)return;
        float vw = mViewport.width();
        float vh = mViewport.height();

        float scale = 1f;//no scale
        float sx = vw / (float)bitmapWidth;
        float sy = vh / (float)bitmapHeight;
        switch(scaleMode){
            case SCALE_MODE_AUTOFIT:
                scale = Math.min(sx, sy);
                break;
            case SCALE_MODE_VW:
                scale = sx;
                break;
            case SCALE_MODE_VH:
                scale = sy;
                break;
            case SCALE_MODE_BH2VW:
                scale = vw / (float)bitmapHeight;
                break;
            case SCALE_MODE_BW2VH:
                scale = vh / (float)bitmapWidth;
                break;
        }

        if(D)Logger.d(TAG, "updateMatrix scale=" + scale);

        mtxVal[0] = flipH ? -scale : scale;//SX
        mtxVal[4] = flipV ? -scale : scale;//SY

        float nbw = bitmapWidth * scale;
        float nbh = bitmapHeight * scale;
        if(flipH){
            mtxVal[2] = (vw + nbw)/2f;//TX
        }else {
            mtxVal[2] = (vw - nbw)/2f;//TX
        }

        if(flipV){
            mtxVal[5] = (vh + nbh)/2f;//TY
        }else{
            mtxVal[5] = (vh - nbh)/2f;//TY
        }

        mMatrix.setValues(mtxVal);

        radius = specRadius;
        if(isCircle){
            //calculate default radius for circle.
            if(radius == INVALID_RADIUS){
                int mv = (int) Math.min(mViewport.width(), mViewport.height());
                int mb = (int) Math.min(nbw, nbh);
                radius = Math.min(mv, mb) / 2f;
            }
        }else{
            if(specArea == null) {
                float w = Math.min(mViewport.width(), nbw);
                float h = Math.min(mViewport.height(), nbh);
                float l = mViewport.centerX() - w/2f;
                float t = mViewport.centerY() - h/2f;
                area = new RectF(l, t, l + w, t + h);
            }

        }

        setImageMatrix(mMatrix);
        postInvalidate();
    }

    //限制缩放模式,否则前面的矩阵运算失效.
    @Override
    public void setScaleType(ScaleType scaleType) {
        if(scaleType != ScaleType.MATRIX){
            Logger.w(TAG, "setScaleType support Matrix only");
        }
        super.setScaleType(ScaleType.MATRIX);

        mMatrix = getMatrix();
        mMatrix.getValues(mtxVal);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mViewport.set(0, 0, w, h);
        setScaleType(ScaleType.FIT_CENTER);

        p.setColor(0xFFFFFFFF);
        p.setStyle(Paint.Style.FILL);
        if(D)p.setStrokeWidth(10);

        updateMatrix();
    }

    Xfermode srcIn = new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT);
    Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(radius > 0 || specArea != null){
            RectF rf = specArea != null ? specArea : area;
            canvas.saveLayer(mViewport, p, Canvas.ALL_SAVE_FLAG);
            canvas.drawRect(mViewport, p);
            p.setXfermode(srcIn);
            if(radius > 0) {//clip circle or round rect.
                if (isCircle) {
                    canvas.drawCircle(mViewport.centerX(), mViewport.centerY(), radius, p);
                } else {//round rect
                    canvas.drawRoundRect(rf, radius, radius, p);
                }
            }else if(specArea != null){
                //clip rect
                canvas.drawRect(specArea, p);
            }
            p.setXfermode(null);
            canvas.restore();
        }

        if(D)canvas.drawPoint(area.centerX(), area.centerY(), p);
    }
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值