一、Canvas类Android.graphics.Canvas
先来看官网的一句话吧 The Canvas class holds the “draw” calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
Canvas类就像一块画布,可以在上面画画,如果我们想画的话,需要四个基本组件
1、Bitmap ,位图 :包含像素
2、canvas ,画板
3、a drawing primitive ,原始图(e.g. Rect, Path, text, Bitmap)
4、Paint: 画笔 :提供了颜色和样式
public class DrawView extends View {
public DrawView(Context context) {
super(context);
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//创建画笔
Paint p = new Paint();
p.setColor(Color.RED);
p.setTextSize(50); //设置字体大小
/**
* 画圆
*/
canvas.drawText("画圆", 10, 100, p);
canvas.drawCircle(200, 90, 15, p);
/**
* 画线
*/
canvas.drawText("画线", 10, 200, p);
p.setColor(Color.BLUE);
canvas.drawLine(300, 200, 600, 200, p); //只需要两个点的坐标
/**
* 画矩形
*/
canvas.drawText("画矩形", 10, 400, p);
// p.setStyle(Paint.Style.FILL);
canvas.drawRect(200, 300, 500, 400, p); //这个矩形是按边来画的 left:左边到X轴的距离(x坐标),top:上边到Y轴的距离(y的纵"zong"坐标),right:(右边到X的距离(X坐标),bottom:底边到Y轴的距离(Y坐标)
canvas.drawRect(600, 300, 1000, 500, p);
/**
* 画弧形
*/
canvas.drawText("画弧形", 10, 700, p);
canvas.drawRect(200, 600, 600, 800, p);
p.setColor(Color.YELLOW);
RectF rectF = new RectF(200, 600, 600, 800); //这个弧形就是以矩形的重心为原点,对角线的的一半为半径,以顺时针画
canvas.drawArc(rectF, 0, 160, true, p); //useCenter 为true就覆盖所有画的区域
/**
* 三角形
*/
p.setColor(Color.RED);
canvas.drawText("画三角形", 10, 900, p);
p.setStyle(Paint.Style.STROKE);//设置空心
//绘制三角形,也可以绘制任意多边形
Path path = new Path();
path.moveTo(300, 900); //第一个点为起点
path.lineTo(800, 100); //注意这里是lineto
path.lineTo(300, 970);
path.close(); //封闭
canvas.drawPath(path, p);
//画圆角矩形
p.setStyle(Paint.Style.FILL);
p.setColor(Color.LTGRAY);
p.setAntiAlias(true); //设置画笔的锯齿效果
canvas.drawText("画圆角矩形",10,1000,p);
RectF rectF1= new RectF(200,1000,500,1100);
canvas.drawRoundRect(rectF1,50,200,p); //第二个参数和第三个x\y方向上的半径,不是很理解
}
}
这里写的也挺详细的 http://blog.youkuaiyun.com/rhljiayou/article/details/7212620
二 接着看下经典之图 (很多博客第一个和我的不同)
ApiDemos导入各种报错,浪费了不少时间,干脆把代码拎出来,“点我进图”可以看到
1、这么多图是啥意思呢? 其实就花了两个图,
先画个圆(黄色)
再画个矩形(蓝色)
两个叠加通过属性判断要显示哪部分,最后就可以构成我们需要的各种图 形了,感觉和红绿蓝三原色似的。
2、这里面不得不提到 PorterDuff.Mode16种模式
从上面我们可以看到PorterDuff.Mode为枚举类,一共有16个枚举值:
1.PorterDuff.Mode.CLEAR 所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC 显示上层绘制图片
3.PorterDuff.Mode.DST 显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER 正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER 上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN 取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN 取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT 取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT 取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP 取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP 取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR 异或:去除两图层交集部分
13.PorterDuff.Mode.DARKEN 两图层全部区域,交集部分颜色加深
14.PorterDuff.Mode.LIGHTEN 取两图层全部,点亮交集部分颜色
15.PorterDuff.Mode.MULTIPLY 取两图层交集部分叠加后颜色
16.PorterDuff.Mode.SCREEN 两图层全部区域,交集部分变为透明色
3、ApiDemos 示例
public class Xfermodes extends Activity {
public static final Xfermode[] sModes = {
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
new PorterDuffXfermode(PorterDuff.Mode.SRC),
new PorterDuffXfermode(PorterDuff.Mode.DST),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.XOR),
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
};
// create a bitmap with a circle, used for the "dst" image 画一个圆
static Bitmap makeDst(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);
return bm;
}
// create a bitmap with a rect, used for the "src" image 画一个矩形
static Bitmap makeSrc(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
c.drawRect(w/3, h/3, w*19/20, h*19/20, p);
return bm;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private static final int W = 200;
private static final int H = 200;
private static final int ROW_MAX = 4; // number of samples per row
private Bitmap mSrcB,mOvalB;
private Shader mBG; // background checker-board pattern
private static final String[] sLabels = {
"Clear", "Src", "Dst", "SrcOver",
"DstOver", "SrcIn", "DstIn", "SrcOut",
"DstOut", "SrcATop", "DstATop", "Xor",
"Darken", "Lighten", "Multiply", "Screen"
};
public SampleView(Context context) {
super(context);
mSrcB = makeSrc(W, H);
mOvalB = makeDst(W, H);
// make a ckeckerboard pattern
Bitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,
0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,
Bitmap.Config.RGB_565);
mBG = new BitmapShader(bm,
Shader.TileMode.REPEAT,
Shader.TileMode.REPEAT);
Matrix m = new Matrix(); //矩阵
m.setScale(6, 6);
mBG.setLocalMatrix(m);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
labelP.setTextSize(30);
labelP.setTextAlign(Paint.Align.CENTER);
Paint paint = new Paint();
paint.setFilterBitmap(false);
canvas.translate(15, 35);
int x = 0;
int y = 0;
for (int i = 0; i < sModes.length; i++) {
// draw the border
paint.setStyle(Paint.Style.STROKE);
paint.setShader(null);
canvas.drawRect(x - 0.5f, y - 0.5f,
x + W + 0.5f, y + H + 0.5f, paint); //绘制正方形的外边框,长宽就是W 、 H
// draw the checker-board pattern
paint.setStyle(Paint.Style.FILL);
paint.setShader(mBG);
canvas.drawRect(x, y, x + W, y + H, paint); //内边框
// draw the src/dst example into our offscreen bitmap
int sc = canvas.saveLayer(x, y, x + W, y + H, null,
Canvas.MATRIX_SAVE_FLAG |
Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.translate(x, y); //设置矩阵的偏移量
canvas.drawBitmap(mOvalB, 0, 0, paint); //先绘制圆
paint.setXfermode(sModes[i]);
canvas.drawBitmap(mSrcB, 0, 0, paint); //再绘制矩形
paint.setXfermode(null);
canvas.restoreToCount(sc);
// draw the label
canvas.drawText(sLabels[i],
x + W/2, y - labelP.getTextSize()/2, labelP); //写字
x += W + 10;
// wrap around when we've drawn enough for one row
if ((i % ROW_MAX) == ROW_MAX - 1) {
x = 0;
y += H + 30;
}
}
}
}
}
4、单个进行测试
/**
* Created by Administrator on 2016/8/28.
* 对每一个属性单个进行测试, !!!!!有一个问题 onDraw() 偶尔不执行 !!!!!
*/
public class XfermodeView extends View {
//源图和目标图宽高
int width = 200, height = 200;
Bitmap rectBitmap, cirBitmap;
int screenW, screenH;
public XfermodeView(Context context) {
// super(context);
//init(context);
//把上面的改成
this(context,null); //发现改成这样每次都会走了
}
public XfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
setWillNotDraw(false);
}
private void init(Context context) {
WindowManager windowManager = (WindowManager) context.getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metrics);
screenW = metrics.widthPixels;
screenH = metrics.heightPixels;
}
// create a bitmap with a circle, used for the "dst" image 画一个圆
static Bitmap makeDst(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
return bm;
}
// create a bitmap with a rect, used for the "src" image 画一个矩形
static Bitmap makeSrc(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, p);
return bm;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//创建原图和目标图
rectBitmap = makeSrc(width, height);
cirBitmap = makeDst(width, height);
Paint paint = new Paint();
paint.setFilterBitmap(false);
paint.setStyle(Paint.Style.FILL);
//绘制“src”蓝色矩形原图
canvas.drawBitmap(rectBitmap, (screenW / 8 - width / 4), (screenH / 12 - height / 4), paint);
//绘制“dst”黄色圆形原图
canvas.drawBitmap(cirBitmap, screenW / 2, screenH / 12, paint);
//创建一个图层,在图层上演示图形混合后的效果
int sc = canvas.saveLayer(0, 0, screenW, screenH, null, Canvas.MATRIX_SAVE_FLAG |
Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG);
// 先绘制圆形
canvas.drawBitmap(cirBitmap, screenW / 4, screenH / 3, paint);
//设置Paint的Xfermode
paint.setXfermode(Xfermodes.sModes[4]); //这里修改显示模式
//绘制矩形
canvas.drawBitmap(rectBitmap, screenW / 4, screenH / 3, paint);
paint.setXfermode(null);
// 还原画布
canvas.restoreToCount(sc);
}
}
Reference :http://blog.youkuaiyun.com/allen315410/article/details/45077165
三、接下来跟着大神来画图了
1、先绘bitmap,然后绘制形状,显示底部
/**
* Created by Administrator on 2016/8/29.
*/
public class RoundImageViewByXfermode extends ImageView {
private int mBorderRadius; //圆角的大小
private Paint mPaint;
/**
* 图片的类型,圆形or圆角
*/
private int type;
public static final int TYPE_CIRCLE = 0;
public static final int TYPE_ROUND = 1;
private Xfermode mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
private WeakReference<Bitmap> mWeakBitmap;
private Bitmap mMaskBitmap;
public RoundImageViewByXfermode(Context context) {
this(context, null);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
public RoundImageViewByXfermode(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImageViewByXfermode);
//角度默认10dp
mBorderRadius = a.getDimensionPixelSize(R.styleable.RoundImageViewByXfermode_borderRadius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
type = a.getInt(R.styleable.RoundImageViewByXfermode_type, TYPE_CIRCLE); //默认圆角
a.recycle();
}
@Override
public void invalidate() {
mMaskBitmap = null;
if (mMaskBitmap != null) {
mMaskBitmap.recycle();
mMaskBitmap = null;
}
super.invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas); // 这个要注释否则就会没效果
//从缓存中取出bitmap
Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();
if (null == bitmap || bitmap.isRecycled()) {
//拿到Drawable
Drawable drawable = getDrawable();
//获取Drawable的宽和高
int dWidth = drawable.getIntrinsicWidth();
int dHeight = drawable.getIntrinsicHeight();
if (drawable != null) {
//创建Bitmap
bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
float scale = 1.0f;
//创建画布
Canvas drawCanvas = new Canvas(bitmap);
//按照bitmap的宽高,以及view的宽高,计算缩放比例;因为设置的src宽高比例可能和imageview的宽高比例不同,这里我们不希望图片失真;
if (type == TYPE_ROUND) {
// 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
scale = Math.max(getWidth() * 1.0f / dWidth, getHeight() * 1.0f / dHeight);
}
//根据缩放比例,设置bounds,相当于缩放图片
drawable.setBounds(0, 0, (int) (scale * dWidth), (int) (scale * dHeight));
drawable.draw(drawCanvas);
if (mMaskBitmap == null || mMaskBitmap.isRecycled())
mMaskBitmap = getBitmap();
//Draw Bitmap
mPaint.reset();
mPaint.setFilterBitmap(false);
mPaint.setXfermode(mXfermode); // 取两层绘制交集。显示下层。
//绘制圆角矩形
drawCanvas.drawBitmap(mMaskBitmap, 0, 0, mPaint);
mPaint.setXfermode(null);
//将准备好的bitmap绘制出来
canvas.drawBitmap(bitmap, 0, 0, null);
mWeakBitmap = new WeakReference<Bitmap>(bitmap); //bitmap缓存起来,避免每次调用onDraw,分配内存
}
}
//如果bitmap还存在,则直接绘制即可
if (bitmap != null) {
mPaint.setXfermode(null);
canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
return;
}
}
public Bitmap getBitmap() {
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
if (type == TYPE_ROUND) {
canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()),
mBorderRadius, mBorderRadius, paint);
}
return bitmap;
}
}
运行的时候编译器碰到一个很坑的问题
android studio Error:java.lang.OutOfMemoryError: GC overhead limit exceeded
解决方法是
REFERENCE :http://blog.youkuaiyun.com/lmj623565791/article/details/42094215
二、BitmapShader 这个也能画出圆角矩形的效果
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.china, null);
Bitmap target = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
RectF rect = new RectF(0, 0, target.getWidth(), target.getHeight());
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setShader(shader);
//绘制一个圆角矩形
Canvas canvas = new Canvas(target);
canvas.drawRoundRect(rect, 40, 40, paint);
imageView1.setImageBitmap(target);
参考:
http://gavinliu.cn/2016/04/12/Android-%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E5%9C%86%E8%A7%92%E6%98%BE%E7%A4%BA%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F/
http://blog.youkuaiyun.com/allen315410/article/details/45077165
http://blog.youkuaiyun.com/guolin_blog/article/details/16330267