平台
Android 7.1 + AndroidStudio 4.2.1 + Ubuntu 20.04
需求
1.支持左右/上下翻转
2.支持缩放
3.支持裁切为圆角/圆形
原图:
圆形 + 缩放1
圆形 + 缩放2
圆形 + 缩放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); } }