一、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.csdn.net/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.csdn.net/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.csdn.net/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.csdn.net/allen315410/article/details/45077165
http://blog.csdn.net/guolin_blog/article/details/16330267