目录
1. 效果图展示
这次要绘制的自定义view的效果就是实现图片的底部是个圆弧。
2. 绘制思路
- 基于贝塞尔曲线API,绘制出底部的弧线
- 利用addRoundRect方法绘制出带圆角的矩形
- 将以上图形进行叠加,取叠加的部分
2.1 Path的使用
关于Path的用法,参考文章android绘图之Path总结
2.2 Xfermode用法
Xfermode在这次自定义view中发挥了很重要的作用是,所以很有必要挑出来说(PS:其实是因为我自己也忘记了O(∩_∩)O~) 下面上经典图:
上图显示的是两个图形通过各种叠加模式后显示的结果,测试代码如下:(来自文章Android 画笔 Paint - 了解Android Paint,一篇就够)
public class PaintCanvas extends View {
private Paint mPaint;
private PorterDuffXfermode porterDuffXfermode;
private Context mContext;
private Bitmap mBitmap;
private void init() {
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.logo);
mPaint = new Paint();
mPaint.setAntiAlias(true);
porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SCREEN);
}
@Override protected void onDraw(Canvas canvas) {
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
mPaint.setXfermode(porterDuffXfermode);
mPaint.setColor(0xFFFFCC44);
canvas.drawCircle(600, 600, 200, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
}
复制代码
具体实现
完整代码奉上:
public class BottomRoundImageView extends AppCompatImageView {
private static final String TAG = BottomRoundImageView.class.getSimpleName();
protected Context mContext;
private static final Xfermode sXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
private Bitmap mMaskBitmap;
private Paint mPaint;
private WeakReference<Bitmap> mWeakBitmap;
/**
* 圆角半径
*/
private int radius;
private int mWidth ;
private int mHeight ;
private Path path;
private RectF rectF;
public BottomRoundImageView(Context context) {
super(context);
sharedConstructor(context);
}
public BottomRoundImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructor(context);
}
public BottomRoundImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
sharedConstructor(context);
}
private void sharedConstructor(Context context) {
mContext = context;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
radius = ViewUtils.dipToPx(6);
}
@Override
public void invalidate() {
mWeakBitmap = null;
if (mMaskBitmap != null) {
mMaskBitmap.recycle();
}
super.invalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
if (!isInEditMode()) {
int i = canvas.saveLayer(0.0f, 0.0f, mWidth,mHeight,
null, Canvas.ALL_SAVE_FLAG);
try {
Bitmap bitmap = mWeakBitmap != null ? mWeakBitmap.get() : null;
if (bitmap == null || bitmap.isRecycled()) {
Drawable drawable = getDrawable();
if (drawable != null) {
bitmap = Bitmap.createBitmap(mWidth,
mHeight, Bitmap.Config.ARGB_8888);
Canvas bitmapCanvas = new Canvas(bitmap);
drawable.setBounds(0, 0, mWidth, mHeight);
drawable.draw(bitmapCanvas);
if (mMaskBitmap == null || mMaskBitmap.isRecycled()) {
mMaskBitmap = getBitmap();
}
mPaint.reset();
mPaint.setFilterBitmap(false);
mPaint.setXfermode(sXfermode);
bitmapCanvas.drawBitmap(mMaskBitmap, 0.0f, 0.0f, mPaint);
mWeakBitmap = new WeakReference<>(bitmap);
}
}
if (bitmap != null) {
mPaint.setXfermode(null);
canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
return;
}
} catch (Exception e) {
System.gc();
Log.e(TAG, String.format("Failed to draw, Id :: %s. Error occurred :: %s", getId(), e.toString()));
} finally {
canvas.restoreToCount(i);
}
} else {
super.onDraw(canvas);
}
}
public Bitmap getBitmap(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
path = new Path();
path.moveTo(0,0);
path.lineTo(0,height * 2/ 3);
path.quadTo(width / 2 , height , width, height * 2/ 3);
path.lineTo(width,0);
path.close();
rectF = new RectF();
rectF.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
rectF.set(0, 0, getWidth(), getHeight() + radius); //这里要加上半径的高度,不然最后底部两个角会截取不干净
path.addRoundRect(rectF, radius, radius, Path.Direction.CW);
path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
canvas.drawPath(path,paint);
return bitmap;
}
public Bitmap getBitmap() {
return getBitmap(mWidth, mHeight);
}
}
复制代码