Android ImageView 翻转 裁切 缩放

简介: Android ImageView 翻转 裁切 缩放

平台


Android 7.1 + AndroidStudio 4.2.1 + Ubuntu 20.04


需求


1.支持左右/上下翻转

2.支持缩放

3.支持裁切为圆角/圆形

原图:


0a2653c851af460fa595bd959398a8f1.pngimage.png


圆形 + 缩放1

image.png

圆形 + 缩放2

image.png

圆形 + 缩放3

image.png

圆角 + 翻转

image.png


几个需要注意的点


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月前
|
Java Android开发 Kotlin
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
36 0
|
5月前
|
Java Android开发
18. 【Android教程】图片控件 ImageView
18. 【Android教程】图片控件 ImageView
83 4
|
Android开发
#6,Android Studio Android 开发控件 显示图片 ImageView的使用
#6,Android Studio Android 开发控件 显示图片 ImageView的使用
|
Android开发
Android ImageView视图的七种图片缩放类型
Android ImageView视图的七种图片缩放类型
297 0
|
Android开发
Android ImageView 使用
Android ImageView 使用
85 0
|
Android开发
Android ImageView scaleType 属性详细介绍与使用
Android ImageView scaleType 属性详细介绍与使用
130 0
|
XML Android开发 数据格式
Android ImageView的src和background的区别、padding的使用技巧
Android ImageView的src和background的区别、padding的使用技巧
387 0
|
前端开发 JavaScript Java
Android 眼睛 显示隐藏密码(ImageView)
Android 眼睛 显示隐藏密码(ImageView)
209 0
Android 眼睛 显示隐藏密码(ImageView)
|
Android开发 开发者
Android 14 开发代号泄露!Upside Down Cake “翻转蛋糕”
Android 14 开发代号泄露!Upside Down Cake “翻转蛋糕”
158 0
Android 14 开发代号泄露!Upside Down Cake “翻转蛋糕”
|
XML Android开发 数据格式
Android ImageView及其子类 介绍+实例(下)
ImageButton 什么是ImageButton 通过实例了解ImageButton 1、创建布局文件 运行效果如下: ImageButton灰色边框的产生原因和解决方案 QuickContactBadge 什么是QuickContactBadge QuickContactBadge的调用方法 通过实例了解QuickContactBadge 1、创建布局文件 2、让QuickContactBadge与特定联系人建立联系 3、运行效果
222 0
Android ImageView及其子类 介绍+实例(下)