在自定义view中可以通过canvas在屏幕上绘制一些文字,图片,图形之类的效果,canvas这个类给我们提供了很多绘制的方法,比如绘制一段文字在屏幕上:
package com.example.customview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; /** * Created by admin on 2016/5/9. */ public class CustomView extends View { private Paint mPaint = null; public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setTextSize(35);//设置文字大小 canvas.drawText("canvas绘制文字",100,100,mPaint); } }这是在父view的100,100坐标哪里绘制文字的,canvas绘制的方法一般都是前缀draw....比入drawBitmap()绘制图片,Canvas类如下方法:
canvas方法多用几篇就知道了,今天主要是讲canvas与图层的关系,先讲下canvas几个方法
1:translate(floatdx, floatdy)
package com.example.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; /** * Created by Adminis on 2016/5/9. */ public class CustomView extends View { private Paint mPaint; public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Rect rect = new Rect(0,0,400,400); mPaint.setColor(Color.RED); mPaint.setStrokeWidth(5); canvas.drawRect(rect,mPaint); } }
效果如下:
现在通过canvas的translate(100,100)向右平移100px,向下平移100px,效果:
从效果来看就知道translate(dx,dy)这二个参数的意思
dx:是向右移动dx距离
dy:是向下移动dy距离
canvas还有其他类似view的动画方法
scale(dx,dy)缩放
rotate(dx,dy)旋转
这是一些canvas实现对画布的操作,但是有没有想过这个矩形是怎么画上去的,也许会说这不是废话么,通过canvas画上去的,所以就得出结论就是画布就是屏幕显示区域,现在把上面绘制的矩形改为空心的
package com.example.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; /** * Created by Adminis on 2016/5/9. */ public class CustomView extends View { private Paint mPaint; public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Rect rect = new Rect(0,0,400,400); mPaint.setColor(Color.RED); mPaint.setStrokeWidth(5); mPaint.setStyle(Paint.Style.STROKE); canvas.drawRect(rect, mPaint); canvas.translate(100,100); mPaint.setColor(Color.GREEN); canvas.drawRect(rect, mPaint); } }图:
发现平移后的矩形和平移前的矩形没有重合,这是为什么呢?这是因为屏幕显示和canvas不是一个等同的概念,屏幕相当于一个显示我们所看到效果的一个容器,你可以随便往屏幕上显示任何可在坐标点范围内可见的,而canvas可以看作是一个透明的图层,我们每次调用canvas的draw...方法都会产生一个透明的层,既然知道了canvas每次draw都会产生一个透明的图层,这样也就好解释上面的问题,在没有平移前,它的坐标点为(0,0) 平移了(100,100)相当于(100,100)是第二个图层的原点,这样二个图层就有重叠的部分,而canvas对象是系统通过onDraw()方法提供给我们的显示到屏幕上,关于屏幕于canvas图层我在别的博客找到了一个很好的图演示图层合成到屏幕上
而图层是无限大的,而屏幕大小是有限的,比如在使用canvas把一段位子绘制到图层上的坐标是负数,而在屏幕上显示不出来就是就是因为屏幕是从(0,0)到(屏幕的宽度,屏幕的高度)这个范围是可见的,超过这二个点的范围是不可见的,既然在图层上画出来也显示看不见的,从上面的分析中可得出2个结论
1:每次调用canvas对象的draw...方法都会产生一个透明的图层
2:canvas与屏幕合成过程中,如果超出屏幕范围是不可见的,
canvas还有2个非常重要的方法 一般使用了canvas的什么平移,缩放等操作必用到的2个方法就是save()和restore()
你可以在屏幕上绘制多个图层,我们知道activity有activity任务栈,你也可以想象的把图层集合想象成一个栈,栈是一个先进后出的数据结构,你可以把图层看作是一个activity任务栈,一层一层的往屏幕上叠起来,现在写个例子:
package com.example.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; /** * Created by Adminis on 2016/5/9. */ public class CustomView extends View { private Paint mPaint; public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制一个图层背景 没有在图层上画什么东西 一个空的图层 canvas.drawColor(Color.parseColor("#e5e5e5")); //保存当前画布大小即整屏 (这是保存上一个画布的操作) canvas.save(); //绘制一个矩形 canvas.clipRect(new Rect(100, 100, 800, 800)); canvas.drawColor(Color.GREEN); //恢复整屏画布 也就是回复到(0,0)坐标点 canvas.restore(); canvas.drawColor(Color.); } }这三个图层可以一个个画出来看出它的合成到屏幕的过程,结果是这样的,
第二个图层:第二个图层的宽度800-100,高度为800-100就是图中显示的区域大小
第三层:
最终是显示整屏的红色,因为最后一层是(0,0)开始会把前2层图层覆盖了,如果canvas不调用restore()回复的方法 效果是这样的:
你会发现它刚好在第二个图层的上面,我们可以在第三个图层画一个矩形,但是宽和高比第二个图层的矩形小点,这样就可以看出来它在第二个层上面,
package com.example.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; /** * Created by Adminis on 2016/5/9. */ public class CustomView extends View { private Paint mPaint; public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制一个图层背景 没有在图层上画什么东西 一个空的图层 canvas.drawColor(Color.parseColor("#e5e5e5")); //保存当前画布大小即整屏 (这是保存上一个画布的操作) canvas.save(); //绘制一个矩形 canvas.clipRect(new Rect(100, 100, 800, 800)); canvas.drawColor(Color.GREEN); // //恢复整屏画布 也就是回复到(0,0)坐标点 // canvas.restore(); canvas.clipRect(new Rect(100, 100,700, 700)); canvas.drawColor(Color.RED); } }效果:
从效果上可以得出一个结论,如果canvas没有调用restore()方法的话,canvas画图层是从它上一个图层栈中以上一个图层原点为原点开始绘制并合并到屏幕上,现在写一个例子多次调用save()和restore()方法会有什么奇怪的效果出来:
package com.example.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; /** * Created by Adminis on 2016/5/9. */ public class CustomView extends View { private Paint mPaint; public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.parseColor("#e5e5e5")); //保存的画布大小为全屏幕大小 canvas.save(); //这个矩形会在上一个画布上进行draw 也就是(0,0)位置 canvas.clipRect(new Rect(100, 100, 600, 600)); //这个画布在前一个矩形大小为500,500的位置 canvas.drawColor(Color.GREEN); //保存画布大小为Rect(100, 100, 600, 600) canvas.save(); //这个会在上一个矩形为500,500的位置上 canvas.clipRect(new Rect(200, 200, 700, 700)); canvas.drawColor(Color.BLUE); //保存画布大小为Rect(200, 200, 700, 700) canvas.save(); //这个会在原点为(200,200)大小为500的我、canvas上 canvas.clipRect(new Rect(300, 300, 600, 600)); canvas.drawColor(Color.BLACK); //保存画布大小为Rect(300, 300, 600, 600) canvas.save(); //这个会在原点为(300,300)为原点大小为300的位置上进行canvas canvas.clipRect(new Rect(400, 400, 500, 500)); canvas.drawColor(Color.YELLOW); } }这个注释写的很仔细了,最终的效果图:
这是调用了四次save()方法但是没调用restore()方法,可以模拟activity栈画一个类似activity任务栈的图:
现在可以先调用一次restore()方法看效果:为了看出效果我把最后一层的大小和上一层大小一样
你会发现把上一层黑色的给盖住了,如果连续三次调用restore()方法,和刚才一样思考就是到了绿色的那个区域(canvas)效果是和你想象的一样,集合图层栈所描述的那样每次调用一次日store()方法都会把这个图层拉出栈顶,然后把下一个图层当作当前要使用的画布
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.parseColor("#e5e5e5")); //保存的画布大小为全屏幕大小 canvas.save(); canvas.clipRect(new Rect(100, 100, 600, 600)); canvas.drawColor(Color.GREEN); canvas.save(); canvas.clipRect(new Rect(200, 200, 500, 500)); canvas.drawColor(Color.BLUE); canvas.save(); canvas.clipRect(new Rect(300, 300, 400, 400)); canvas.drawColor(Color.BLACK); }效果如下:
我现在调用一次restore()效果是这样的:
因为调用了一次restore()方法,就会把蓝色的图层当作新的画布,而调用3次restore()方法效果是这样的
总结下这二个方法的使用
Save():每次调用Save()函数,都会把当前的画布的状态进行保存,然后放入特定的栈中;
restore():每当调用Restore()函数,就会把栈中最顶层的画布状态取出来,并按照这个状态恢复当前的画布,并在这个画布上做画
终于写完了 洗澡睡觉