回顾一下,自定义view重写的关键方法有:
measure 测量组件本身的大小
layout 确定组件在视图中的位置
draw 根据位置和大小,将组件画出来
float[] radii:必须传入8个数值,分四组,分别对应每个角所使用的椭圆的横轴半径和纵轴半径,如{x1,y1,x2,y2,x3,y3,x4,y4},其中,x1,y1对应第一个角的(左上角)用来产生圆角的椭圆的横轴半径和纵轴半径,其它类推……
定义几种圆角模式
/**
* 圆形模式
*/
private static final int MODE_CIRCLE = 1;
/**
* 普通模式
*/
private static final int MODE_NONE = 0;
/**
* 圆角模式
*/
private static final int MODE_ROUND = 2;
/**
* 上面的圆角模式
*/
private static final int MODE_ROUND_TOP = 3;
/**
* 下方圆角模式
*/
private static final int MODE_ROUND_BOTTOM = 4;
- attr文件中定义
<declare-styleable name="RoundImageview">
<attr name="viewtype" format="enum">
<enum name="circle" value="1"/>
<enum name="round" value="2"/>
<enum name="roundtop" value="3"/>
<enum name="round_bottom" value="4"/>
</attr>
<attr name="radius" format="dimension"/>
</declare-styleable>
- 准备就绪(画笔,矩阵等对象,不要在ondraw的时候去new对象,因为ondraw会被调用多次,容易导致内存泄露)
private static final int MODE_ROUND_BOTTOM = 4;
private BitmapShader bitmapShader;
private Paint mPaint;
private int currMode = 0;
private Path path = new Path();
private RectF rectF = new RectF();
private Matrix matrix = new Matrix();
private Bitmap bitmap;
// 圆角矩形路径
private float[] radii = new float[8];
/**
* 圆角半径
*/
private int currRound = dp2px(10);
private int dp2px(float value) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
}
- 重写三个构造函数 ,并在里面初始化数据
public RoundImageview(Context context) {
super(context);
initViews();
}
public RoundImageview(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundImageview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
obtainStyledAttrs(context, attrs, defStyleAttr);
initViews();
}
private void obtainStyledAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImageview, defStyleAttr, 0);
currMode = a.hasValue(R.styleable.RoundImageview_viewtype) ? a.getInt(R.styleable.RoundImageview_viewtype, MODE_NONE) : MODE_NONE;
currRound = a.hasValue(R.styleable.RoundImageview_radius) ? a.getDimensionPixelSize(R.styleable.RoundImageview_radius, currRound) : currRound;
//用完后记得回收
a.recycle();
}
private void initViews() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
}
- 测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/*
* 当模式为圆形模式的时候,我们强制让宽高一致
*/
if (currMode == MODE_CIRCLE) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int result = Math.min(getMeasuredHeight(), getMeasuredWidth());
setMeasuredDimension(result, result);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
path.reset();
rectF.left = getPaddingLeft();
rectF.top = getPaddingTop();
rectF.right = getMeasuredWidth() - getPaddingRight();
rectF.bottom = getMeasuredHeight() - getPaddingBottom();
switch (currMode) {
case MODE_ROUND_TOP:
fillRadii(currRound, currRound, currRound, currRound, 0, 0, 0, 0);
break;
case MODE_ROUND_BOTTOM:
fillRadii(0, 0, 0, 0, currRound, currRound, currRound, currRound);
break;
case MODE_ROUND:
fillRadii(currRound, currRound, currRound, currRound, currRound, currRound, currRound, currRound);
break;
default:
fillRadii(0, 0, 0, 0, 0, 0, 0, 0);
break;
}
path.addRoundRect(rectF, radii, Path.Direction.CCW);
if (bitmapShader != null && bitmap != null && getMeasuredHeight() > 0 && getMeasuredWidth() > 0) {
matrix.reset();
float sx = getMeasuredWidth() / (float)bitmap.getWidth();
float sy = getMeasuredHeight() / (float)bitmap.getHeight();
float scale = Math.max(sx,sy);
matrix.postScale(scale, scale);
bitmapShader.setLocalMatrix(matrix);
}
}
private void fillRadii(float... radii) {
System.arraycopy(radii, 0, this.radii, 0, 8);
}
- 开始画啦
@Override
protected void onDraw(Canvas canvas) {
final int saveCount = canvas.getSaveCount();
canvas.save();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (getCropToPadding()) {
final int scrollX = getScrollX();
final int scrollY = getScrollY();
canvas.clipRect(scrollX + getPaddingLeft(), scrollY + getPaddingTop(),
scrollX + getRight() - getLeft() - getPaddingRight(),
scrollY + getBottom() - getTop() - getPaddingBottom());
}
}
canvas.translate(getPaddingLeft(), getPaddingTop());
mPaint.reset();
if (bitmapShader != null) {
if (currMode == MODE_CIRCLE) {//当为圆形模式的时候
mPaint.setShader(bitmapShader);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mPaint);
} else if (currMode == MODE_ROUND) {//当为圆角模式的时候
mPaint.setShader(bitmapShader);
canvas.drawRoundRect(rectF,
currRound, currRound, mPaint);
} else if (currMode == MODE_ROUND_TOP || currMode == MODE_ROUND_BOTTOM) {//当为上/下圆角模式的时候
mPaint.setShader(bitmapShader);
canvas.drawPath(path, mPaint);
} else {
mPaint.setShader(bitmapShader);
canvas.drawPath(path, mPaint);
}
}
canvas.restoreToCount(saveCount);
}
- 最后暴露一个方法来设置图片资源
public void setImage(@Nullable Bitmap bitmap) {
if (bitmap == null) return;
bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
this.bitmap = bitmap;
requestLayout();
}
参考:https://blog.csdn.net/harvic880925/article/details/38926877