平台
Android 7.1 + AndroidStudio 4.2.1 + Ubuntu 20.04
需求
- 支持左右/上下翻转
- 支持缩放
- 支持裁切为圆角/圆形
原图:
圆形 + 缩放1
圆形 + 缩放2
圆形 + 缩放3
圆角 + 翻转
几个需要注意的点
- 要使用setImageMatrix生效, 必须设置缩放模式: setScaleType(ScaleType.MATRIX);
- 缩放模式, 当图片未填满横向或纵向时, 应以图像的大小来计算圆角和显示区域.
- 直接操作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);
}
}