一、圆角矩形图片实现
图1
实现方案与网上其它实现方式略有不同,本方案处理圆角效果针对的不是图片本身,而是显示区域,代码如下:
@Override
protected void onDraw(Canvas canvas) {
//通过setImageResource()
Drawable drawable = getDrawable();
//setBackgroundResource
if (null == drawable)
drawable =getBackground();
if (null != drawable) {
//清空之前画布的内容
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(mPaint);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
final Rect rectSrc = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final Rect rectDes = new Rect(0,0,getWidth(),getHeight());
final RectF rectF = new RectF(rectDes);
float radius= getResources().getDimension(R.dimen.radius_activity);
mPaint.reset();
//绘制圆角区域,颜色随意设置即可
mPaint.setColor(Color.GREEN);
canvas.drawRoundRect(rectF, radius, radius, mPaint);
//取上下两层的交集
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//将图像真正绘制到View区域
canvas.drawBitmap(bitmap, rectSrc, rectDes, mPaint);
//至此整个圆角区域的绘制成功,但是有黑边,如下图2
//将黑边渲染成背景
mPaint.setColor(getResources().getColor(R.color.color_common_bg));
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
canvas.drawPaint(mPaint);
mPaint.setXfermode(null);
} else {
super.onDraw(canvas);
}
}
至于为什么会有黑色区域,黑边,答案也是显然的,因为这一块区域在两个图层取交集的时候没有绘制。
图2
二、仅有上半圆角矩形图片实现
图3
在用xml定义各种shape时,可以分别定义矩形各个角的圆角半径大小,其对应的Drawable实现类为GradientDrawable,查看源代码可知,其主要绘制Path路径实现,实现代码如下:
protected void onDraw(Canvas canvas) {
//setImageResource()
Drawable drawable = getDrawable();
//setBackgroundResource
if (null == drawable)
drawable =getBackground();
if (null != drawable) {
//清空之前画布的内容
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(mPaint);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
final Rect rectSrc = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final Rect rectDes = new Rect(0,0,getWidth(),getHeight());
final RectF rectF = new RectF(rectDes);
float radius= getResources().getDimension(R.dimen.radius_activity);
mPaint.reset();
//绘制上半部分圆角区域,颜色随意设置即可
mPaint.setColor(Color.GREEN);
mPath.reset();
mPath.addRoundRect(rectF, new float[]{radius, radius, radius, radius, 0, 0, 0, 0}, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
//取上下两层的交集
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//将图像真正绘制到View区域
canvas.drawBitmap(bitmap, rectSrc, rectDes, mPaint);
//将黑边渲染成背景
mPaint.setColor(getResources().getColor(R.color.color_common_bg));
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
canvas.drawPaint(mPaint);
mPaint.setXfermode(null);
} else {
super.onDraw(canvas);
}
}
三、仿微信聊天图片样式实现
图4
其主要实现办法,是在圆角形状区域加一个三角形区域,代码如下:
protected void onDraw(Canvas canvas) {
//setImageResource()
Drawable drawable = getDrawable();
//setBackgroundResource()
if (null == drawable)
drawable =getBackground();
if (null != drawable) {
//清空之前画布的内容
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(mPaint);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
final Rect rectSrc = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final Rect rectDes = new Rect(0,0,getWidth(),getHeight());
final Rect rectImage = new Rect(FINAL_X,0,getWidth(),getHeight());
final RectF rectF = new RectF(rectImage);
float radius= getResources().getDimension(R.dimen.radius_activity);
mPaint.reset();
//绘制三角形和圆角矩形组合的区域,FINAL_X:圆角矩形显示区域向X移动的距离;可通过修改DISTANCE来调整三角区域的宽度
mPaint.setColor(Color.GREEN);
mPath.reset();
mPath.moveTo(FINAL_X, FINAL_Y - DISTANCE);
mPath.quadTo(FINAL_X, FINAL_Y - DISTANCE, 0, FINAL_Y);
mPath.quadTo(0, FINAL_Y, FINAL_X, DISTANCE + FINAL_Y);
mPath.close();
mPath.addRoundRect(rectF, new float[]{radius, radius, radius, radius, 0, 0, 0, 0}, Path.Direction.CW);
canvas.drawPath(mPath, mPaint);
//取上下两层的交集
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//将图像真正绘制到View区域
canvas.drawBitmap(bitmap, rectSrc, rectDes, mPaint);
mPaint.setColor(getResources().getColor(R.color.color_common_bg));
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
canvas.drawPaint(mPaint);
mPaint.setXfermode(null);
} else {
super.onDraw(canvas);
}
}
四、仿微信聊天图片样式优化
图5
在上面的仿微信聊天图片样式中,本打算用贝塞尔曲线实现圆滑的效果,但定义坐标点完全是直线点,其形成的三角区域有点太直角了,不太美观,所以这次想通过圆角矩形的一部分作为三角头来达到圆滑效果,区域形状如下:
图6
其实现的主要代码如下:
protected void onDraw(Canvas canvas) {
//通过setImageResource()
Drawable drawable = getDrawable();
//setBackgroundResource
if (null == drawable)
drawable =getBackground();
if (null != drawable) {
//清空之前画布的内容
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(mPaint);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
final Rect rectSrc = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final Rect rectDes = new Rect(0,0,getWidth(),getHeight());
final Rect rectImage = new Rect(FINAL_X,0,getWidth(),getHeight());
final RectF rectF = new RectF(rectImage);
float radius= getResources().getDimension(R.dimen.radius_activity);
mPaint.reset();
//将原始图形绘制到新区域,下图层a
mPaint.setColor(Color.GREEN);
canvas.drawBitmap(bitmap, rectSrc, rectDes, mPaint);
//取上下两层的交集
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
//形状区域上图层b
int sc = canvas.saveLayer(0,0,getWidth(),getHeight(), mPaint, Canvas.CLIP_SAVE_FLAG);
Paint paint=new Paint();
//绿色大的圆角矩形
paint.setColor(Color.GREEN);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
mPath.addRoundRect(rectF, new float[]{radius, radius, radius, radius, 0, 0, 0, 0}, Path.Direction.CW);
canvas.drawPath(mPath, paint);
mPath.reset();
//灰色圆角矩形
paint.setColor(Color.GRAY);
canvas.save();
canvas.translate(0, FINAL_Y);
canvas.rotate(45);
final RectF tempF = new RectF(0f,-(float)(Math.sqrt(2)*FINAL_X),(float)(Math.sqrt(2)*FINAL_X),0f);
mPath.addRoundRect(tempF, 6, 6, Path.Direction.CW);
canvas.drawPath(mPath, paint);
canvas.restore();
// 图层a和b合并,形成新效果
canvas.restoreToCount(sc);
//将黑边渲染成背景
mPaint.setColor(getResources().getColor(R.color.color_common_bg));
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
canvas.drawPaint(mPaint);
mPaint.setXfermode(null);
} else {
super.onDraw(canvas);
}
}