自定义ImageView——支持ImageView的形状和倒角大
2019-06-04 本文已影响7人
Peakmain
Github地址:https://github.com/Peakmain/extendUI
效果图
image.png
首先需要自定义属性
<declare-styleable name="ExtendImageView">
<attr name="extend_border_color" format="color" />
<attr name="extend_border_width" format="dimension" />
<attr name="extend_press_alpha" format="integer" />
<attr name="extend_press_color" format="color" />
<attr name="extend_radius" format="dimension" />
<attr name="extend_shape_type" format="enum">
<enum name="none" value="0" />
<enum name="round" value="1" />
<enum name="rectangle" value="2" />
</attr>
</declare-styleable>
初始化数据和解析自定义属性
//当用户按下的时候paint
private Paint pressPaint;
private int width;
private int height;
//默认图片配置
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
//宽度的颜色
private int borderColor;
//边框的宽度
private int borderWidth;
//按下的时候的透明度变化
private int pressAlpha;
//按下的时候的颜色
private int pressColor;
//半径
private int radius;
//类型:1、圆形 2、矩形
private int shapeType;
public ExtendImageView(Context context) {
super(context);
init(context, null);
}
public ExtendImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public ExtendImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
//初始化值
borderWidth = 0;
borderColor = 0xddffffff;
pressAlpha = 0x42;
pressColor = 0x4200000;
radius = 16;
shapeType = 0;
//解析自定义属性
if (attrs != null) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ExtendImageView);
borderColor = array.getColor(R.styleable.ExtendImageView_extend_border_color, borderColor);
borderWidth = array.getDimensionPixelOffset(R.styleable.ExtendImageView_extend_border_width, borderWidth);
pressAlpha = array.getInteger(R.styleable.ExtendImageView_extend_press_alpha, pressAlpha);
pressColor = array.getColor(R.styleable.ExtendImageView_extend_press_color, pressColor);
radius = array.getDimensionPixelOffset(R.styleable.ExtendImageView_extend_radius, radius);
shapeType = array.getInteger(R.styleable.ExtendImageView_extend_shape_type, shapeType);
array.recycle();
}
//按下的时候设置画笔
pressPaint = new Paint();
pressPaint.setAntiAlias(true);
pressPaint.setStyle(Paint.Style.FILL);
pressPaint.setColor(pressColor);
pressPaint.setAlpha(0);
pressPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
//保证我们可以重写onDraw方法
setDrawingCacheEnabled(true);
setWillNotDraw(false);
}
接下来就是绘制了,我们首先需要去获取到bitmap然后进行绘制
@Override
protected void onDraw(Canvas canvas) {
//默认情况下就直接返回就可以了
if (shapeType == 0) {
super.onDraw(canvas);
return;
}
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
//宽高为空也直接返回
if (getWidth() == 0 || getHeight() == 0) {
return;
}
//从drawable中获取bitmao
Bitmap bitmap = getBitmapFromDrawable(drawable);
drawDrawable(canvas, bitmap);
if (isClickable()) {
drawPress(canvas);
}
drawBorder(canvas);
}
从drawable中获取bitmap
/**
* 从drawable中获取bitmap
*
* @param drawable
* @return
*/
private Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bitmap;
//返回图片的真实宽高
int width = Math.max(drawable.getIntrinsicWidth(), 2);
int height = Math.max(drawable.getIntrinsicHeight(), 2);
//创建bitmap
try {
bitmap = Bitmap.createBitmap(width, height, BITMAP_CONFIG);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
} catch (Exception e) {
e.printStackTrace();
bitmap = null;
}
return bitmap;
}
绘制drawable
/**
* 绘制drawable
*
* @param canvas canvas
* @param bitmap bitmap
*/
private void drawDrawable(Canvas canvas, Bitmap bitmap) {
Paint paint = new Paint();
paint.setColor(0xffffffff);
//设置抗锯齿
paint.setAntiAlias(true);
PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
//6.0之前需要通过反射执行
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// 设置 flags
int saveFlags = CanvasSave.MATRIX_SAVE_FLAG
| CanvasSave.CLIP_SAVE_FLAG
| CanvasSave.HAS_ALPHA_LAYER_SAVE_FLAG
| CanvasSave.FULL_COLOR_LAYER_SAVE_FLAG
| CanvasSave.CLIP_TO_LAYER_SAVE_FLAG;
CanvasSave.saveLayer(canvas, 0, 0, width, height, null, saveFlags);
} else {
canvas.saveLayer(0, 0, width, height, null);
}
if (shapeType == 1) {
canvas.drawCircle(width / 2, height / 2, width / 2 - 1, paint);
} else if (shapeType == 2) {
RectF rectF = new RectF(1, 1, getWidth() - 1, getHeight() - 1);
canvas.drawRoundRect(rectF, radius + 1, radius + 1, paint);
}
//设置图层样式
paint.setXfermode(xfermode);
float scaleWidth = ((float) getWidth()) / bitmap.getWidth();
float scaleHeight = ((float) getHeight()) / bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
canvas.drawBitmap(bitmap, 0, 0, paint);
canvas.restore();
}
绘制border和按下时的绘制
/**
* 当手指按下的时候
*
* @param canvas 画布
*/
private void drawPress(Canvas canvas) {
if (shapeType == 1) {
//圆形
int radius = width > height ? (width - borderWidth) / 2 : (height - borderWidth) / 2;
canvas.drawCircle(width / 2, height / 2, radius, pressPaint);
} else {
RectF rectF = new RectF(1, 1, width - 1, height - 1);
canvas.drawRoundRect(rectF, radius + 1, radius + 1, pressPaint);
}
}
/**
* 绘制自定义border
*/
private void drawBorder(Canvas canvas) {
if (borderWidth > 0) {
Paint paint = new Paint();
paint.setStrokeWidth(borderWidth);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(borderColor);
paint.setAntiAlias(true);
//圆形
if (shapeType == 1) {
int radius = width > height ? (width - borderWidth) / 2 : (height - borderWidth) / 2;
canvas.drawCircle(width / 2, height / 2, radius, paint);
} else if (shapeType == 2) {
RectF rectf = new RectF(borderWidth / 2, borderWidth / 2, getWidth() - borderWidth / 2,
getHeight() - borderWidth / 2);
canvas.drawRoundRect(rectf, radius, radius, paint);
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressPaint.setAlpha(pressAlpha);
invalidate();
break;
case MotionEvent.ACTION_UP:
pressPaint.setAlpha(0);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
break;
default:
pressPaint.setAlpha(0);
invalidate();
break;
}
return super.onTouchEvent(event);
}
/**
* 设置 border color
*/
public void setBorderColor(int borderColor) {
this.borderColor = borderColor;
invalidate();
}
/**
* 设置 border width
*
* @param borderWidth
*/
public void setBorderWidth(int borderWidth) {
this.borderWidth = borderWidth;
}
/**
* 设置 press alpha
*/
public void setPressAlpha(int pressAlpha) {
this.pressAlpha = pressAlpha;
}
/**
* 设置 pressColor
*/
public void setPressColor(int pressColor) {
this.pressColor = pressColor;
}
/**
* 设置 radius
*/
public void setRadius(int radius) {
this.radius = radius;
}
/**
* 设置shapeType
*/
public void setShapeType(int shapeType) {
this.shapeType = shapeType;
invalidate();
}
CanvasSave类通过反射执行saveLayer
class CanvasSave {
static final int MATRIX_SAVE_FLAG;
static final int CLIP_SAVE_FLAG;
static final int HAS_ALPHA_LAYER_SAVE_FLAG;
static final int FULL_COLOR_LAYER_SAVE_FLAG;
static final int CLIP_TO_LAYER_SAVE_FLAG;
private static final Method SAVE;
static {
try {
MATRIX_SAVE_FLAG = (int) Canvas.class.getField("MATRIX_SAVE_FLAG").get(null);
CLIP_SAVE_FLAG = (int) Canvas.class.getField("CLIP_SAVE_FLAG").get(null);
HAS_ALPHA_LAYER_SAVE_FLAG = (int) Canvas.class.getField("HAS_ALPHA_LAYER_SAVE_FLAG").get(null);
FULL_COLOR_LAYER_SAVE_FLAG = (int) Canvas.class.getField("FULL_COLOR_LAYER_SAVE_FLAG").get(null);
CLIP_TO_LAYER_SAVE_FLAG = (int) Canvas.class.getField("CLIP_TO_LAYER_SAVE_FLAG").get(null);
SAVE = Canvas.class.getMethod("saveLayer", float.class, float.class, float.class, float.class, Paint.class, int.class);
} catch (Throwable e) {
throw sneakyThrow(e);
}
}
static void saveLayer(Canvas canvas, float left, float top, float right, float bottom, @Nullable Paint paint, int saveFlags) {
try {
SAVE.invoke(canvas, left, top, right, bottom, paint, saveFlags);
} catch (Throwable e) {
throw sneakyThrow(e);
}
}
private static RuntimeException sneakyThrow(Throwable t) {
if (t == null) throw new NullPointerException("t");
return CanvasSave.sneakyThrow0(t);
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
throw (T) t;
}
}