android Graphics

转载地址:http://blog.csdn.net/harvic880925/article/details/38875149



一、Paint与Canvas

像我们平时画图一样,需要两个工具,纸和笔。Paint就是相当于笔,而Canvas就是纸,这里叫画布。

所以,凡有跟要要画的东西的设置相关的,比如大小,粗细,画笔颜色,透明度,字体的样式等等,都是在Paint里设置;同样,凡是要画出成品的东西,比如圆形,矩形,文字等相关的都是在Canvas里生成。

下面先说下Paint的基本设置函数:

  • paint.setAntiAlias(true);//抗锯齿功能
  • paint.setColor(Color.RED);  //设置画笔颜色    
  • paint.setStyle(Style.FILL);//设置填充样式
  • paint.setStrokeWidth(30);//设置画笔宽度
  • paint.setShadowLayer(10, 15, 15, Color.GREEN);//设置阴影

前两个没什么好说的,看填充样式的区别:

1、void setStyle (Paint.Style style)     设置填充样式

Paint.Style.FILL    :填充内部
Paint.Style.FILL_AND_STROKE  :填充内部和描边
Paint.Style.STROKE  :仅描边

看下这三个类型的不同,下面以画的一个圆形为例:


可见,FILL与FILL_AND_STROKE没什么区别。

2、setShadowLayer (float radius, float dx, float dy, int color)    添加阴影

参数:

radius:阴影的倾斜度
dx:水平位移
dy:垂直位移

使用代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. paint.setShadowLayer(101515, Color.GREEN);//设置阴影  


然后是Canvas的基本设置:

画布背景设置:

  • canvas.drawColor(Color.BLUE);
  • canvas.drawRGB(255, 255, 0);   //这两个功能一样,都是用来设置背景颜色的。

二、实例操作

先给大家讲下怎么在写画图程序,大家可以边看边写边看效果是怎样的,先利用一个画直线的函数来测试一下吧,划直线当然是canvas.drawline();上面说了,有关画笔设置的都是Paint,有关画图的都在Canvas类中;

1、首先新建一个工程,把默认的XML改成FrameLayout布局,布局代码如下:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:id="@+id/root"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     tools:context="com.example.try_paint_blog.MainActivity" >  
  7.   
  8.   
  9. </FrameLayout>  

这里有个特别要注意的地方,给根结点FrameLayout加一个ID号,后面用来在它的内部添加视图用的

2、然后新建一个视图类,派生自View, 为什么要新建视图类呢,因为我们要自由的显示东西,当然要重写视图类了,在OnDraw()函数中返回什么,这个视图就会长什么样。话不多说,新建一个派生自View的类MyView,然后重写OnDraw()函数:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class MyView extends View {  
  2.   
  3.     Context m_context;  
  4.     public MyView(Context context) {  
  5.         super(context);  
  6.         // TODO Auto-generated constructor stub  
  7.           
  8.         m_context=context;  
  9.     }  
  10.       
  11.     //重写OnDraw()函数,在每次重绘时自主实现绘图  
  12.     @Override  
  13.     protected void onDraw(Canvas canvas) {  
  14.         // TODO Auto-generated method stub  
  15.         super.onDraw(canvas);  
  16.           
  17.           
  18.         //设置画笔基本属性  
  19.         Paint paint=new Paint();  
  20.         paint.setAntiAlias(true);//抗锯齿功能  
  21.         paint.setColor(Color.RED);  //设置画笔颜色      
  22.         paint.setStyle(Style.FILL);//设置填充样式   Style.FILL/Style.FILL_AND_STROKE/Style.STROKE  
  23.         paint.setStrokeWidth(5);//设置画笔宽度  
  24.         paint.setShadowLayer(101515, Color.GREEN);//设置阴影  
  25.           
  26.         //设置画布背景颜色       
  27.         canvas.drawRGB(255255,255);  
  28.           
  29.         //画圆  
  30.         canvas.drawCircle(190200150, paint);      
  31.     }  
  32.   
  33. }  
在这段代码中,首先对画笔进行基本的样式设置,(对几何图形设置阴影,好像没作用),然后利用DrawCircle()画了一个圆。关于这个函数,下面会再讲,大家只知道用来画圆就好了。
3、视图类写好了,下面就要将其加入到Activity中,让它显示出来,在MainActiviy中添加如下代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.           
  7.       setContentView(R.layout.activity_main);  
  8.         
  9.       FrameLayout root=(FrameLayout)findViewById(R.id.root);  
  10.       root.addView(new MyView(MainActivity.this));  
  11.     }  
  12.   
  13. }  
首先找到FrameLayout结点,然后把新生成的MyView实例加入到其中。
运行之后,效果如下:


源码下载地址:http://download.csdn.net/detail/harvic880925/7834517


三、基本几何图形绘制

1、画直线

void drawLine (float startX, float startY, float stopX, float stopY, Paint paint)

参数:

startX:开始点X坐标
startY:开始点Y坐标
stopX:结束点X坐标
stopY:结束点Y坐标

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.FILL);//设置填充样式   
  4. paint.setStrokeWidth(5);//设置画笔宽度  
  5.   
  6. canvas.drawLine(100100200200, paint);  


2、多条直线

void drawLines (float[] pts, Paint paint)
void drawLines (float[] pts, int offset, int count, Paint paint)

参数:

pts:是点的集合,大家下面可以看到,这里不是形成连接线,而是每两个点形成一条直线,pts的组织方式为{x1,y1,x2,y2,x3,y3,……}

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.FILL);//设置填充样式   
  4. paint.setStrokeWidth(5);//设置画笔宽度  
  5.   
  6. float []pts={10,10,100,100,200,200,400,400};  
  7. canvas.drawLines(pts, paint);  

(上面有四个点:(10,10)、(100,100),(200,200),(400,400)),两两连成一条直线;



3、点

void drawPoint (float x, float y, Paint paint)

参数:
float X:点的X坐标
float Y:点的Y坐标

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.FILL);//设置填充样式   
  4. paint.setStrokeWidth(15);//设置画笔宽度  
  5.   
  6. canvas.drawPoint(100100, paint);  


4、多个点

void drawPoints (float[] pts, Paint paint)
void drawPoints (float[] pts, int offset, int count, Paint paint)

参数:
float[] pts:点的合集,与上面直线一直,样式为{x1,y1,x2,y2,x3,y3,……}
int offset:集合中跳过的数值个数,注意不是点的个数!一个点是两个数值;
count:参与绘制的数值的个数,指pts[]里人数值个数,而不是点的个数,因为一个点是两个数值

下面举例说明上面offset与count的含义:(跳过第一个点,画出后面两个点,第四个点不画),注意一个点是两个数值!

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.FILL);//设置填充样式   
  4. paint.setStrokeWidth(15);//设置画笔宽度  
  5.   
  6. float []pts={10,10,100,100,200,200,400,400};  
  7. canvas.drawPoints(pts, 24, paint);  
(同样是上面的四个点:(10,10)、(100,100),(200,200),(400,400),drawPoints里路过前两个数值,即第一个点横纵坐标,画出后面四个数值代表的点,即第二,第三个点,第四个点没画;效果图如下)


5、矩形工具类RectF与Rect

这两个都是矩形辅助类,区别不大,用哪个都行,根据四个点构建一个矩形结构;在画图时,利用这个矩形结构可以画出对应的矩形或者与其它图形Region相交、相加等等;

RectF:

构造函数有下面四个,但最常用的还是第二个,根据四个点构造出一个矩形;

RectF()
RectF(float left, float top, float right, float bottom)
RectF(RectF r)
RectF(Rect r)


Rect

构造函数如下,最常用的也是根据四个点来构造矩形

Rect()
Rect(int left, int top, int right, int bottom)
Rect(Rect r)

6、矩形

void drawRect (float left, float top, float right, float bottom, Paint paint)
void drawRect (RectF rect, Paint paint)
void drawRect (Rect r, Paint paint)

参数:

第一个的写法是直接传入矩形的四个点,画出矩形

第二、三个构造函数是根据传入RectF或者Rect矩形变量来指定所画的矩形的

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.FILL);//设置填充样式   
  4. paint.setStrokeWidth(15);//设置画笔宽度  
  5.   
  6. canvas.drawRect(1010100100, paint);//直接构造  
  7.   
  8. RectF rect = new RectF(12010210100);  
  9. canvas.drawRect(rect, paint);//使用RectF构造  
  10.   
  11. Rect rect2 =  new Rect(23010320100);   
  12. canvas.drawRect(rect2, paint);//使用Rect构造  


7、圆角矩形

void drawRoundRect (RectF rect, float rx, float ry, Paint paint)

参数:
RectF rect:要画的矩形
float rx:生成圆角的椭圆的X轴半径
float ry:生成圆角的椭圆的Y轴半径

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.FILL);//设置填充样式   
  4. paint.setStrokeWidth(15);//设置画笔宽度  
  5.   
  6. RectF rect = new RectF(10010300100);  
  7. canvas.drawRoundRect(rect, 2010, paint);  


8、圆形

void drawCircle (float cx, float cy, float radius, Paint paint)

参数:
float cx:圆心点X轴坐标 
float cy:圆心点Y轴坐标
float radius:圆的半径

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.FILL);//设置填充样式   
  4. paint.setStrokeWidth(15);//设置画笔宽度  
  5.   
  6. canvas.drawCircle(150150100, paint);  


9、椭圆

椭圆是根据矩形生成的,以矩形的长为椭圆的X轴,矩形的宽为椭圆的Y轴,建立的椭圆图形

void drawOval (RectF oval, Paint paint)

参数:
RectF oval:用来生成椭圆的矩形

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.STROKE);//填充样式改为描边   
  4. paint.setStrokeWidth(5);//设置画笔宽度  
  5.   
  6. RectF rect = new RectF(10010300100);  
  7. canvas.drawRect(rect, paint);//画矩形  
  8.   
  9. paint.setColor(Color.GREEN);//更改画笔颜色  
  10. canvas.drawOval(rect, paint);//同一个矩形画椭圆  


10、弧

弧是椭圆的一部分,而椭圆是根据矩形来生成的,所以弧当然也是根据矩形来生成的;

void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

参数:
RectF oval:生成椭圆的矩形
float startAngle:弧开始的角度,以X轴正方向为0度
float sweepAngle:弧持续的角度
boolean useCenter:是否有弧的两边,True,还两边,False,只有一条弧

(1)将画笔设为描边,效果:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.STROKE);//填充样式改为描边   
  4. paint.setStrokeWidth(5);//设置画笔宽度  
  5.   
  6. RectF rect1 = new RectF(10010300100);  
  7. canvas.drawArc(rect1, 090true, paint);  
  8.   
  9. RectF rect2 = new RectF(40010600100);  
  10. canvas.drawArc(rect2, 090false, paint);  

(二)、将画笔设为填充

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.FILL);//填充样式改为描边   
  4. paint.setStrokeWidth(5);//设置画笔宽度  
  5.   
  6. RectF rect1 = new RectF(10010300100);  
  7. canvas.drawArc(rect1, 090true, paint);  
  8.   
  9. RectF rect2 = new RectF(40010600100);  
  10. canvas.drawArc(rect2, 090false, paint);  



android Graphics(二):路径及文字


一、创建路径

canvas中绘制路径利用:

void drawPath (Path path, Paint paint)


1、直线路径

void moveTo (float x1, float y1):直线的开始点;即将直线路径的绘制点定在(x1,y1)的位置;
void lineTo (float x2, float y2):直线的结束点,又是下一次绘制直线路径的开始点;lineTo()可以一直用;
void close ():如果连续画了几条直线,但没有形成闭环,调用Close()会将路径首尾点连接起来,形成闭环;

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.STROKE);//填充样式改为描边   
  4. paint.setStrokeWidth(5);//设置画笔宽度  
  5.   
  6. Path path = new Path();  
  7.   
  8. path.moveTo(1010); //设定起始点  
  9. path.lineTo(10100);//第一条直线的终点,也是第二条直线的起点  
  10. path.lineTo(300100);//画第二条直线  
  11. path.lineTo(500100);//第三条直线  
  12. path.close();//闭环  
  13.   
  14. canvas.drawPath(path, paint);  


2、矩形路径

void addRect (float left, float top, float right, float bottom, Path.Direction dir)
void addRect (RectF rect, Path.Direction dir)

这里Path类创建矩形路径的参数与上篇canvas绘制矩形差不多,唯一不同的一点是增加了Path.Direction参数;
Path.Direction有两个值:
Path.Direction.CCW:是counter-clockwise缩写,指创建逆时针方向的矩形路径;
Path.Direction.CW:是clockwise的缩写,指创建顺时针方向的矩形路径;

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //先创建两个大小一样的路径  
  2. //第一个逆向生成  
  3. Path CCWRectpath = new Path();  
  4. RectF rect1 =  new RectF(5050240200);  
  5. CCWRectpath.addRect(rect1, Direction.CCW);  
  6.   
  7. //第二个顺向生成  
  8. Path CWRectpath = new Path();  
  9. RectF rect2 =  new RectF(29050480200);  
  10. CWRectpath.addRect(rect2, Direction.CW);  
  11.   
  12. //先画出这两个路径   
  13. canvas.drawPath(CCWRectpath, paint);  
  14. canvas.drawPath(CWRectpath, paint);  

问:从效果图中,看不出顺时针生成和逆时针生成的任何区别,怎么会没区别呢?
答:当然没区别啦,无论正时针还是逆时针,仅仅是生成方式不同而已,矩形就那么大画出来的路径矩形当然与矩形一样大了。
问:那生成方式有什么区别呢?
答:生成方式的区别在于,依据生成方向排版的文字!后面我们会讲到文字,文字是可以依据路径排版的,那文字的行走方向就是依据路径的生成方向;

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //先创建两个大小一样的路径  
  2. //第一个逆向生成  
  3. Path CCWRectpath = new Path();  
  4. RectF rect1 =  new RectF(5050240200);  
  5. CCWRectpath.addRect(rect1, Direction.CCW);  
  6.   
  7. //第二个顺向生成  
  8. Path CWRectpath = new Path();  
  9. RectF rect2 =  new RectF(29050480200);  
  10. CWRectpath.addRect(rect2, Direction.CW);  
  11.   
  12. //先画出这两个路径   
  13. canvas.drawPath(CCWRectpath, paint);  
  14. canvas.drawPath(CWRectpath, paint);  
  15.   
  16. //依据路径写出文字  
  17. String text="风萧萧兮易水寒,壮士一去兮不复返";  
  18. paint.setColor(Color.GRAY);  
  19. paint.setTextSize(35);  
  20. canvas.drawTextOnPath(text, CCWRectpath, 018, paint);//逆时针生成  
  21. canvas.drawTextOnPath(text, CWRectpath, 018, paint);//顺时针生成  

(有关文字的代码部分,可以先忽略,后面会讲到,这里只是让大家知道生成方式的用处)

3、圆角矩形路径

void addRoundRect (RectF rect, float[] radii, Path.Direction dir)
void addRoundRect (RectF rect, float rx, float ry, Path.Direction dir)

这里有两个构造函数,部分参数说明如下:
第一个构造函数:可以定制每个角的圆角大小:
float[] radii:必须传入8个数值,分四组,分别对应每个角所使用的椭圆的横轴半径和纵轴半径,如{x1,y1,x2,y2,x3,y3,x4,y4},其中,x1,y1对应第一个角的(左上角)用来产生圆角的椭圆的横轴半径和纵轴半径,其它类推……
第二个构造函数:只能构建统一圆角大小
float rx:所产生圆角的椭圆的横轴半径;
float ry:所产生圆角的椭圆的纵轴半径;

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Path path = new Path();  
  2. RectF rect1 =  new RectF(5050240200);  
  3. path.addRoundRect(rect1, 1015 , Direction.CCW);  
  4.   
  5. RectF rect2 =  new RectF(29050480200);  
  6. float radii[] ={10,15,20,25,30,35,40,45};  
  7. path.addRoundRect(rect2, radii, Direction.CCW);  
  8.   
  9. canvas.drawPath(path, paint);  


4、圆形路径

void addCircle (float x, float y, float radius, Path.Direction dir)

参数说明:
float x:圆心X轴坐标 
float y:圆心Y轴坐标
float radius:圆半径

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Path path = new Path();  
  2. path.addCircle(200200100, Direction.CCW);  
  3. canvas.drawPath(path, paint);  


5、椭圆路径

void addOval (RectF oval, Path.Direction dir)

参数说明:
RectF oval:生成椭圆所对应的矩形
Path.Direction :生成方式,与矩形一样,分为顺时针与逆时针,意义完全相同,不再重复

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Path path = new Path();  
  2. RectF rect =  new RectF(5050240200);  
  3. path.addOval(rect, Direction.CCW);  
  4. canvas.drawPath(path, paint);  


6、弧形路径

void addArc (RectF oval, float startAngle, float sweepAngle)

参数:
RectF oval:弧是椭圆的一部分,这个参数就是生成椭圆所对应的矩形;
float startAngle:开始的角度,X轴正方向为0度
float sweepAngel:持续的度数;

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3. paint.setStyle(Style.STROKE);//填充样式改为描边   
  4. paint.setStrokeWidth(5);//设置画笔宽度  
  5.   
  6. Path path = new Path();  
  7. RectF rect =  new RectF(5050240200);  
  8. path.addArc(rect, 0100);  
  9.   
  10. canvas.drawPath(path, paint);//画出路径  


7、线段轨迹---待补充

void quadTo (float x1, float y1, float x2, float y2)


二、文字

1、Paint相关设置

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //普通设置  
  2. paint.setStrokeWidth (5);//设置画笔宽度  
  3. paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢  
  4. paint.setStyle(Paint.Style.FILL);//绘图样式,对于设文字和几何图形都有效  
  5. paint.setTextAlign(Align.CENTER);//设置文字对齐方式,取值:align.CENTER、align.LEFT或align.RIGHT  
  6. paint.setTextSize(12);//设置文字大小  
  7.   
  8. //样式设置  
  9. paint.setFakeBoldText(true);//设置是否为粗体文字  
  10. paint.setUnderlineText(true);//设置下划线  
  11. paint.setTextSkewX((float) -0.25);//设置字体水平倾斜度,普通斜体字是-0.25  
  12. paint.setStrikeThruText(true);//设置带有删除线效果  
  13.   
  14. //其它设置  
  15. paint.setTextScaleX(2);//只会将水平方向拉伸,高度不会变  

示例1:绘图样式的区别:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3.   
  4. paint.setStrokeWidth (5);//设置画笔宽度  
  5. paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢  
  6. paint.setTextSize(80);//设置文字大小  
  7.   
  8. //绘图样式,设置为填充      
  9. paint.setStyle(Paint.Style.FILL);     
  10. canvas.drawText("欢迎光临Harvic的博客"10,100, paint);  
  11.   
  12. //绘图样式设置为描边   
  13. paint.setStyle(Paint.Style.STROKE);  
  14. canvas.drawText("欢迎光临Harvic的博客"10,200, paint);  
  15.   
  16. //绘图样式设置为填充且描边    
  17. paint.setStyle(Paint.Style.FILL_AND_STROKE);  
  18. canvas.drawText("欢迎光临Harvic的博客"10,300, paint);  

示例二:文字样式设置及倾斜度正负区别

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3.   
  4. paint.setStrokeWidth (5);//设置画笔宽度  
  5. paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢  
  6. paint.setTextSize(80);//设置文字大小  
  7. paint.setStyle(Paint.Style.FILL);//绘图样式,设置为填充     
  8.   
  9. //样式设置  
  10. paint.setFakeBoldText(true);//设置是否为粗体文字  
  11. paint.setUnderlineText(true);//设置下划线  
  12. paint.setStrikeThruText(true);//设置带有删除线效果  
  13.   
  14. //设置字体水平倾斜度,普通斜体字是-0.25,可见往右斜  
  15. paint.setTextSkewX((float) -0.25);  
  16. canvas.drawText("欢迎光临Harvic的博客"10,100, paint);  
  17.   
  18. //水平倾斜度设置为:0.25,往左斜  
  19. paint.setTextSkewX((float0.25);  
  20. canvas.drawText("欢迎光临Harvic的博客"10,200, paint);  

示例三:水平拉伸设置( paint.setTextScaleX(2);)

写三行字,第一行,水平未拉伸的字体;第二行,水平拉伸两倍的字体;第三行,水平未拉伸和水平拉伸两部的字体写在一起,可以发现,仅是水平方向拉伸,高度并未改变;

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3.   
  4. paint.setStrokeWidth (5);//设置画笔宽度  
  5. paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢  
  6. paint.setTextSize(80);//设置文字大小  
  7. paint.setStyle(Paint.Style.FILL);//绘图样式,设置为填充     
  8.   
  9. //变通样式字体  
  10. canvas.drawText("欢迎光临Harvic的博客"10,100, paint);  
  11.   
  12. //水平方向拉伸两倍  
  13. paint.setTextScaleX(2);//只会将水平方向拉伸,高度不会变  
  14. canvas.drawText("欢迎光临Harvic的博客"10,200, paint);  
  15.   
  16. //写在同一位置,不同颜色,看下高度是否看的不变  
  17. paint.setTextScaleX(1);//先还原拉伸效果  
  18. canvas.drawText("欢迎光临Harvic的博客"10,300, paint);  
  19.   
  20. paint.setColor(Color.GREEN);  
  21. paint.setTextScaleX(2);//重新设置拉伸效果  
  22. canvas.drawText("欢迎光临Harvic的博客"10,300, paint);  


2、canvas绘图方式

(1)、普通水平绘制

构造函数:

void drawText (String text, float x, float y, Paint paint)
void drawText (CharSequence text, int start, int end, float x, float y, Paint paint)
void drawText (String text, int start, int end, float x, float y, Paint paint)
void drawText (char[] text, int index, int count, float x, float y, Paint paint)

说明:
第一个构造函数:最普通简单的构造函数;
第三、四个构造函数:实现截取一部分字体给图;
第二个构造函数:最强大,因为传入的可以是charSequence类型字体,所以可以实现绘制带图片的扩展文字(待续),而且还能截取一部分绘制

这几个函数就不再多说了,很简单,前面我们也一直在用第一个构造函数,文字截取一般用不到,我也不多说了,浪费时间,可能大家看到有个构造函数中,可以传入charSequence类型的字符串,charSequence是可以利用spannableString来构造有图片的字符串的,那这里是不是可以画出带有图片的字符串来呢 ,我想多了,实际证明,canvas画图是不支持Span替换的。所以这里的charSequence跟普通的String没有任何区别的。


(2)、指定个个文字位置

void drawPosText (char[] text, int index, int count, float[] pos, Paint paint)
void drawPosText (String text, float[] pos, Paint paint)

说明:
第一个构造函数:实现截取一部分文字绘制;

参数说明:
char[] text:要绘制的文字数组
int index::第一个要绘制的文字的索引
int count:要绘制的文字的个数,用来算最后一个文字的位置,从第一个绘制的文字开始算起
float[] pos:每个字体的位置,同样两两一组,如{x1,y1,x2,y2,x3,y3……}

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3.   
  4. paint.setStrokeWidth (5);//设置画笔宽度  
  5. paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢  
  6. paint.setTextSize(80);//设置文字大小  
  7. paint.setStyle(Paint.Style.FILL);//绘图样式,设置为填充     
  8.   
  9. float []pos=new float[]{80,100,  
  10.                         80,200,  
  11.                         80,300,  
  12.                         80,400};  
  13. canvas.drawPosText("画图示例", pos, paint);//两个构造函数  

(3)、沿路径绘制

void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint)
void drawTextOnPath (char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)

参数说明:

有关截取部分字体绘制相关参数(index,count),没难度,就不再讲了,下面首重讲hOffset、vOffset
float hOffset  : 与路径起始点的水平偏移距离
float vOffset  : 与路径中心的垂直偏移量

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint=new Paint();  
  2. paint.setColor(Color.RED);  //设置画笔颜色      
  3.   
  4. paint.setStrokeWidth (5);//设置画笔宽度  
  5. paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢  
  6. paint.setTextSize(45);//设置文字大小  
  7. paint.setStyle(Paint.Style.STROKE);//绘图样式,设置为填充  
  8.   
  9. String string="风萧萧兮易水寒,壮士一去兮不复返";  
  10.   
  11. //先创建两个相同的圆形路径,并先画出两个路径原图  
  12. Path circlePath=new Path();  
  13. circlePath.addCircle(220,200180, Path.Direction.CCW);//逆向绘制,还记得吗,上篇讲过的  
  14. canvas.drawPath(circlePath, paint);//绘制出路径原形  
  15.   
  16. Path circlePath2=new Path();  
  17. circlePath2.addCircle(750,200180, Path.Direction.CCW);  
  18. canvas.drawPath(circlePath2, paint);//绘制出路径原形  
  19.   
  20. paint.setColor(Color.GREEN);  
  21. //hoffset、voffset参数值全部设为0,看原始状态是怎样的  
  22. canvas.drawTextOnPath(string, circlePath, 00, paint);  
  23. //第二个路径,改变hoffset、voffset参数值  
  24. canvas.drawTextOnPath(string, circlePath2, 8030, paint);  

3、字体样式设置(Typeface)

在Paint中设置字体样式:

paint.setTypeface(typeface);


Typeface相关

概述:Typeface是专门用来设置字体样式的,通过paint.setTypeface()来指定。可以指定系统中的字体样式,也可以指定自定义的样式文件中获取。要构建Typeface时,可以指定所用样式的正常体、斜体、粗体等,如果指定样式中,没有相关文字的样式就会用系统默认的样式来显示,一般默认是宋体。

创建Typeface:

 Typeface create(String familyName, int style) //直接通过指定字体名来加载系统中自带的文字样式
 Typeface create(Typeface family, int style)     //通过其它Typeface变量来构建文字样式
 Typeface createFromAsset(AssetManager mgr, String path) //通过从Asset中获取外部字体来显示字体样式
 Typeface createFromFile(String path)//直接从路径创建
 Typeface createFromFile(File path)//从外部路径来创建字体样式
 Typeface defaultFromStyle(int style)//创建默认字体

上面的各个参数都会用到Style变量,Style的枚举值如下:
Typeface.NORMAL  //正常体
Typeface.BOLD  //粗体
Typeface.ITALIC  //斜体
Typeface.BOLD_ITALIC //粗斜体

(1)、使用系统中的字体

从上面创建Typeface的所有函数中可知,使用系统中自带的字体有下面两种方式来构造Typeface:

 Typeface defaultFromStyle(int style)//创建默认字体
 Typeface create(String familyName, int style) //直接通过指定字体名来加载系统中自带的文字样式

其实,第一个仅仅是使用系统默认的样式来绘制字体,基本没有可指定性,就不再讲了,使用起来难度也不大,下面只以第二个构造函数为例,指定宋体绘制:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //使用系统自带字体绘制          
  2. Paint paint=new Paint();  
  3. paint.setColor(Color.RED);  //设置画笔颜色      
  4.   
  5. paint.setStrokeWidth (5);//设置画笔宽度  
  6. paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢  
  7. paint.setTextSize(60);//设置文字大小  
  8. paint.setStyle(Paint.Style.STROKE);//绘图样式,设置为填充  
  9.   
  10. String familyName = "宋体";  
  11. Typeface font = Typeface.create(familyName,Typeface.NORMAL);  
  12. paint.setTypeface(font);  
  13. canvas.drawText("欢迎光临Harvic的博客",10,100, paint);  

(不知道为什么,我用其它系统中也带有字体的时候,也还是用宋体写,烦死,有知道的同学,教一下,谢谢!)

2、自字义字体

自定义字体的话,我们就需要从外部字体文件加载我们所需要的字形的,从外部文件加载字形所使用的Typeface构造函数如下面三个:
 Typeface createFromAsset(AssetManager mgr, String path) //通过从Asset中获取外部字体来显示字体样式
 Typeface createFromFile(String path)//直接从路径创建
 Typeface createFromFile(File path)//从外部路径来创建字体样式

后面两个从路径加载难度不大,而我们一般也不会用到,这里我们说说从Asset文件中加载;

首先在Asset下建一个文件夹,命名为Fonts,然后将字体文件jian_luobo.ttf 放入其中

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //自定义字体,,,迷你简罗卜  
  2. Paint paint=new Paint();  
  3. paint.setColor(Color.RED);  //设置画笔颜色      
  4.   
  5. paint.setStrokeWidth (5);//设置画笔宽度  
  6. paint.setAntiAlias(true); //指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢  
  7. paint.setTextSize(60);//设置文字大小  
  8. paint.setStyle(Paint.Style.FILL);//绘图样式,设置为填充  
  9.   
  10. AssetManager mgr=m_context.getAssets();//得到AssetManager  
  11. Typeface typeface=Typeface.createFromAsset(mgr, "fonts/jian_luobo.ttf");//根据路径得到Typeface  
  12. paint.setTypeface(typeface);  
  13. Log.v("msg",typeface.toString());  
  14. canvas.drawText("欢迎光临Harvic的博客",10,100, paint);//两个构造函数  


android Graphics(三):区域(Range)


一、构造Region

1、基本构造函数

 public Region()  //创建一个空的区域  
 public Region(Region region) //拷贝一个region的范围  
 public Region(Rect r)  //创建一个矩形的区域  
 public Region(int left, int top, int right, int bottom) //创建一个矩形的区域  

上面的四个构造函数,第一个还要配合其它函数使用,暂时不提。
第二个构造函数是通过其它的Region来复制一个同样的Region变量
第三个,第四个才是正规常的,根据一个矩形或矩形的左上角和右下角点构造出一个矩形区域

2、间接构造

 public void setEmpty()  //置空
 public boolean set(Region region)   
 public boolean set(Rect r)   
 public boolean set(int left, int top, int right, int bottom)   
 public boolean setPath(Path path, Region clip)//后面单独讲

这是Region所具有的一系列Set方法,我这里全部列了出来,下面一一对其讲解:
注意:无论调用Set系列函数的Region是不是有区域值,当调用Set系列函数后,原来的区域值就会被替换成Set函数里的区域。
SetEmpty():从某种意义上讲置空也是一个构造函数,即将原来的一个区域变量变成了一个空变量,要再利用其它的Set方法重新构造区域。
set(Region region):利用新的区域值来替换原来的区域
set(Rect r):利用矩形所代表的区域替换原来的区域
set(int left, int top, int right, int bottom):同样,根据矩形的两个点构造出矩形区域来替换原来的区域值
setPath(Path path, Region clip):根据路径的区域与某区域的交集,构造出新区域,这个后面具体讲解

举个小例子,来说明一个Set系列函数的替换概念:

关于重写新建一个类,并派生自view,并且要重写OnDraw函数的问题我就不再讲了,有问题的同学,可以参考下《android Graphics(一):概述及基本几何图形绘制》,当然最后我也会给出相关的源码,直接看源码也行。

下面写了一个函数,先把Set函数注释起来,看看画出来的区域的位置,然后开启Set函数,然后再看画出来的区域
注:里面有个函数drawRegion(Canvas canvas,Region rgn,Paint paint),只知道它可以画出指定的区域就可以了,具体里面是什么意思,后面我们再仔细讲。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class MyRegionView extends View {  
  2.   
  3.     public MyRegionView(Context context) {  
  4.         super(context);  
  5.         // TODO Auto-generated constructor stub  
  6.     }  
  7.       
  8.     @Override  
  9.     protected void onDraw(Canvas canvas) {  
  10.         // TODO Auto-generated method stub  
  11.         super.onDraw(canvas);  
  12.           
  13.         //初始化画笔  
  14.         Paint paint = new Paint();  
  15.         paint.setColor(Color.RED);  
  16.         paint.setStyle(Style.FILL);  
  17.         paint.setStrokeWidth(2);  
  18.           
  19.         Region rgn = new Region(10,10,100,100);  
  20.   
  21. //      rgn.set(100, 100, 200, 200);  
  22.         drawRegion(canvas, rgn, paint);  
  23.           
  24.     }  
  25.       
  26.     //这个函数不懂没关系,下面会细讲  
  27.     private void drawRegion(Canvas canvas,Region rgn,Paint paint)  
  28.     {  
  29.         RegionIterator iter = new RegionIterator(rgn);  
  30.         Rect r = new Rect();  
  31.           
  32.         while (iter.next(r)) {  
  33.           canvas.drawRect(r, paint);  
  34.         }   
  35.     }  
  36.   
  37. }  
看下效果:
                            未开启Set函数时                                                                                           

                       使用Set函数后,替换为新区域

3、使用SetPath()构造不规则区域

boolean setPath (Path path, Region clip)

参数说明:
Path path:用来构造的区域的路径
Region clip:与前面的path所构成的路径取交集,并将两交集设置为最终的区域

由于路径有很多种构造方法,而且可以轻意构造出非矩形的路径,这就摆脱了前面的构造函数只能构造矩形区域的限制。但这里有个问题是要指定另一个区域来取共同的交集,当然如果想显示路径构造的区域,Region clip参数可以传一个比Path范围大的多的区域,取完交集之后,当然是Path参数所对应的区域喽。机智的孩子。

下面,先构造一个椭圆路径,然后在SetPath时,传进去一个比Path小的矩形区域,让它们两个取交集

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class MyRegionView extends View {  
  2.   
  3.     public MyRegionView(Context context) {  
  4.         super(context);  
  5.         // TODO Auto-generated constructor stub  
  6.     }  
  7.       
  8.     @Override  
  9.     protected void onDraw(Canvas canvas) {  
  10.         // TODO Auto-generated method stub  
  11.         super.onDraw(canvas);  
  12.         //初始化Paint  
  13.         Paint paint = new Paint();  
  14.         paint.setColor(Color.RED);  
  15.         paint.setStyle(Style.FILL);  
  16.         paint.setStrokeWidth(2);  
  17.         //构造一个椭圆路径  
  18.         Path ovalPath = new Path();  
  19.         RectF rect =  new RectF(5050200500);    
  20.         ovalPath.addOval(rect, Direction.CCW);  
  21.         //SetPath时,传入一个比椭圆区域小的矩形区域,让其取交集  
  22.         Region rgn = new Region();  
  23.         rgn.setPath(ovalPath,new  Region(5050200200));  
  24.         //画出路径  
  25.         drawRegion(canvas, rgn, paint);  
  26.     }  
  27.       
  28.     //这个函数不懂没关系,下面会细讲  
  29.     private void drawRegion(Canvas canvas,Region rgn,Paint paint)  
  30.     {  
  31.         RegionIterator iter = new RegionIterator(rgn);  
  32.         Rect r = new Rect();  
  33.           
  34.         while (iter.next(r)) {  
  35.           canvas.drawRect(r, paint);  
  36.         }   
  37.     }  
  38. }  
结果如下:

二、矩形集枚举区域——RegionIterator类

对于特定的区域,我们都可以使用多个矩形来表示其大致形状。事实上,如果矩形足够小,一定数量的矩形就能够精确表示区域的形状,也就是说,一定数量的矩形所合成的形状,也可以代表区域的形状。RegionIterator类,实现了获取组成区域的矩形集的功能,其实RegionIterator类非常简单,总共就两个函数,一个构造函数和一个获取下一个矩形的函数;
RegionIterator(Region region) //根据区域构建对应的矩形集
boolean next(Rect r) //获取下一个矩形,结果保存在参数Rect r 中

由于在Canvas中没有直接绘制Region的函数,我们想要绘制一个区域,就只能通过利用RegionIterator构造矩形集来逼近的显示区域。用法如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void drawRegion(Canvas canvas,Region rgn,Paint paint)  
  2. {  
  3.     RegionIterator iter = new RegionIterator(rgn);  
  4.     Rect r = new Rect();  
  5.       
  6.     while (iter.next(r)) {  
  7.       canvas.drawRect(r, paint);  
  8.     }   
  9. }  
上面我们也都看到了它的用法,首先,根据区域构建一个矩形集,然后利用next(Rect r)来逐个获取所有矩形,绘制出来,最终得到的就是整个区域,如果我们将上面的画笔Style从FILL改为STROKE,重新绘制椭圆路径,会看得更清楚。


三、区域的合并、交叉等操作

无论是区域还是矩形,都会涉及到与另一个区域的一些操作,比如取交集、取并集等,涉及到的函数有:

public final boolean union(Rect r)   
public boolean op(Rect r, Op op) {  
public boolean op(int left, int top, int right, int bottom, Op op)   
public boolean op(Region region, Op op)   
public boolean op(Rect rect, Region region, Op op)   

除了Union(Rect r)是指定合并操作以外,其它四个op()构造函数,都是指定与另一个区域的操作。其中最重要的指定Op的参数,Op的参数有下面四个:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 假设用region1  去组合region2     
  2. public enum Op {    
  3.         DIFFERENCE(0), //最终区域为region1 与 region2不同的区域    
  4.         INTERSECT(1), // 最终区域为region1 与 region2相交的区域    
  5.         UNION(2),      //最终区域为region1 与 region2组合一起的区域    
  6.         XOR(3),        //最终区域为region1 与 region2相交之外的区域    
  7.         REVERSE_DIFFERENCE(4), //最终区域为region2 与 region1不同的区域    
  8.         REPLACE(5); //最终区域为为region2的区域    
  9.  }   
至于这六个参数的具体意义,后面给个具体的图给大家显示出来,先举个取交集的例子。

效果图:


先构造两个相交叉的矩形,并画出它们的轮廓

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //构造两个矩形  
  2. Rect rect1 = new Rect(100,100,400,200);  
  3. Rect rect2 = new Rect(200,0,300,300);  
  4.   
  5. //构造一个画笔,画出矩形轮廓  
  6. Paint paint = new Paint();  
  7. paint.setColor(Color.RED);  
  8. paint.setStyle(Style.STROKE);  
  9. paint.setStrokeWidth(2);  
  10.   
  11. canvas.drawRect(rect1, paint);  
  12. canvas.drawRect(rect2, paint);  
然后利用上面的两年rect,(rect1和rect2)来构造区域,并在rect1的基础上取与rect2的交集

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //构造两个Region  
  2. Region region = new Region(rect1);  
  3. Region region2= new Region(rect2);  
  4.   
  5. //取两个区域的交集        
  6. region.op(region2, Op.INTERSECT);  
最后构造一个填充画笔,将所选区域用绿色填充起来

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Paint paint_fill = new Paint();  
  2. paint_fill.setColor(Color.GREEN);  
  3. paint_fill.setStyle(Style.FILL);  
  4. drawRegion(canvas, region, paint_fill);  
全部代码为:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * created by harvic 
  3.  * 2014/9/4 
  4.  */  
  5. import android.content.Context;  
  6. import android.graphics.Canvas;  
  7. import android.graphics.Color;  
  8. import android.graphics.Paint;  
  9. import android.graphics.Rect;  
  10. import android.graphics.Region;  
  11. import android.graphics.Paint.Style;  
  12. import android.graphics.Region.Op;  
  13. import android.graphics.RegionIterator;  
  14. import android.view.View;  
  15.   
  16. public class MyRegionView extends View {  
  17.   
  18.     public MyRegionView(Context context) {  
  19.         super(context);  
  20.         // TODO Auto-generated constructor stub  
  21.     }  
  22.       
  23.     @Override  
  24.     protected void onDraw(Canvas canvas) {  
  25.         // TODO Auto-generated method stub  
  26.         super.onDraw(canvas);  
  27.           
  28.         //构造两个矩形  
  29.         Rect rect1 = new Rect(100,100,400,200);  
  30.         Rect rect2 = new Rect(200,0,300,300);  
  31.           
  32.         //构造一个画笔,画出矩形轮廓  
  33.         Paint paint = new Paint();  
  34.         paint.setColor(Color.RED);  
  35.         paint.setStyle(Style.STROKE);  
  36.         paint.setStrokeWidth(2);  
  37.           
  38.         canvas.drawRect(rect1, paint);  
  39.         canvas.drawRect(rect2, paint);  
  40.           
  41.           
  42.           
  43.         //构造两个Region  
  44.         Region region = new Region(rect1);  
  45.         Region region2= new Region(rect2);  
  46.   
  47.         //取两个区域的交集        
  48.         region.op(region2, Op.INTERSECT);  
  49.           
  50.         //再构造一个画笔,填充Region操作结果  
  51.         Paint paint_fill = new Paint();  
  52.         paint_fill.setColor(Color.GREEN);  
  53.         paint_fill.setStyle(Style.FILL);  
  54.         drawRegion(canvas, region, paint_fill);  
  55.   
  56.     }  
  57.       
  58.   
  59. private void drawRegion(Canvas canvas,Region rgn,Paint paint)  
  60. {  
  61.     RegionIterator iter = new RegionIterator(rgn);  
  62.     Rect r = new Rect();  
  63.       
  64.     while (iter.next(r)) {  
  65.       canvas.drawRect(r, paint);  
  66.     }   
  67. }  
  68. }  
其它参数的操作与这个类似,其实只需要改动region.op(region2, Op.INTERSECT);的Op参数值即可,下面就不再一一列举,给出操作后的对比图。


四、其它一些方法

Region类除了上面的一些重要的方法以外,还有一些比较容易理解的方法,我就不再一一列举用法了,下面一并列出给大家

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /**几个判断方法*/    
  2. public native boolean isEmpty();//判断该区域是否为空    
  3. public native boolean isRect(); //是否是一个矩阵    
  4. public native boolean isComplex();//是否是多个矩阵组合    
  5.     
  6.     
  7. /**一系列的getBound方法,返回一个Region的边界*/    
  8. public Rect getBounds()     
  9. public boolean getBounds(Rect r)     
  10. public Path getBoundaryPath()     
  11. public boolean getBoundaryPath(Path path)     
  12.     
  13.     
  14. /**一系列的判断是否包含某点 和是否相交*/    
  15. public native boolean contains(int x, int y);//是否包含某点    
  16. public boolean quickContains(Rect r)   //是否包含某矩阵    
  17. public native boolean quickContains(int left, int top, int right,    
  18.                                         int bottom) //是否没有包含某矩阵    
  19.  public boolean quickReject(Rect r) //是否没和该矩阵相交    
  20.  public native boolean quickReject(int left, int top, int right, int bottom); //是否没和该矩阵相交    
  21.  public native boolean quickReject(Region rgn);  //是否没和该矩阵相交    
  22.     
  23. /**几个平移变换的方法*/    
  24. public void translate(int dx, int dy)     
  25. public native void translate(int dx, int dy, Region dst);    
  26. public void scale(float scale) //hide    
  27. public native void scale(float scale, Region dst);//hide    

android Graphics(四):canvas变换与操作


一、平移(translate)

canvas中有一个函数translate()是用来实现画布平移的,画布的原状是以左上角为原点,向左是X轴正方向,向下是Y轴正方向,如下图所示


translate函数其实实现的相当于平移坐标系,即平移坐标系的原点的位置。translate()函数的原型如下:

void translate(float dx, float dy)

参数说明:
float dx:水平方向平移的距离,正数指向正方向(向右)平移的量,负数为向负方向(向左)平移的量
flaot dy:垂直方向平移的距离,正数指向正方向(向下)平移的量,负数为向负方向(向上)平移的量

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5.     //translate  平移,即改变坐标系原点位置  
  6.       
  7.     Paint paint = new Paint();  
  8.     paint.setColor(Color.GREEN);  
  9.     paint.setStyle(Style.FILL);  
  10.       
  11. //  canvas.translate(100, 100);  
  12.     Rect rect1 = new Rect(0,0,400,220);  
  13.     canvas.drawRect(rect1, paint);  
  14. }  

1、上面这段代码,先把canvas.translate(100, 100);注释掉,看原来矩形的位置,然后打开注释,看平移后的位置,对比如下图:

                                 未平移                                                                            平移后

  

二、屏幕显示与Canvas的关系

很多童鞋一直以为显示所画东西的改屏幕就是Canvas,其实这是一个非常错误的理解,比如下面我们这段代码:

这段代码中,同一个矩形,在画布平移前画一次,平移后再画一次,大家会觉得结果会怎样?

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5.     //构造两个画笔,一个红色,一个绿色  
  6.     Paint paint_green = generatePaint(Color.GREEN, Style.STROKE, 3);  
  7.     Paint paint_red   = generatePaint(Color.RED, Style.STROKE, 3);  
  8.       
  9.     //构造一个矩形  
  10.     Rect rect1 = new Rect(0,0,400,220);  
  11.   
  12.     //在平移画布前用绿色画下边框  
  13.     canvas.drawRect(rect1, paint_green);  
  14.       
  15.     //平移画布后,再用红色边框重新画下这个矩形  
  16.     canvas.translate(100100);  
  17.     canvas.drawRect(rect1, paint_red);  
  18.   
  19. }  
  20. private Paint generatePaint(int color,Paint.Style style,int width)  
  21. {  
  22.     Paint paint = new Paint();  
  23.     paint.setColor(color);  
  24.     paint.setStyle(style);  
  25.     paint.setStrokeWidth(width);  
  26.     return paint;  
  27. }  
代码分析:
这段代码中,对于同一个矩形,在平移画布前利用绿色画下矩形边框,在平移后,再用红色画下矩形边框。大家是不是会觉得这两个边框会重合?实际结果是这样的。


从到这个结果大家可能会狠蛋疼,我第一次看到这个结果的时候蛋都碎一地了要。淡定……
这个结果的关键问题在于,为什么绿色框并没有移动?

这是由于屏幕显示与Canvas根本不是一个概念!Canvas是一个很虚幻的概念,相当于一个透明图层(用过PS的同学应该都知道),每次Canvas画图时(即调用Draw系列函数),都会产生一个透明图层,然后在这个图层上画图,画完之后覆盖在屏幕上显示。所以上面的两个结果是由下面几个步骤形成的:

1、调用canvas.drawRect(rect1, paint_green);时,产生一个Canvas透明图层,由于当时还没有对坐标系平移,所以坐标原点是(0,0);再在系统在Canvas上画好之后,覆盖到屏幕上显示出来,过程如下图:


2、然后再第二次调用canvas.drawRect(rect1, paint_red);时,又会重新产生一个全新的Canvas画布,但此时画布坐标已经改变了,即向右和向下分别移动了100像素,所以此时的绘图方式为:(合成视图,从上往下看的合成方式)


上图展示了,上层的Canvas图层与底部的屏幕的合成过程,由于Canvas画布已经平移了100像素,所以在画图时是以新原点来产生视图的,然后合成到屏幕上,这就是我们上面最终看到的结果了。我们看到屏幕移动之后,有一部分超出了屏幕的范围,那超出范围的图像显不显示呢,当然不显示了!也就是说,Canvas上虽然能画上,但超出了屏幕的范围,是不会显示的。当然,我们这里也没有超出显示范围,两框框而已。

下面对上面的知识做一下总结:
1、每次调用canvas.drawXXXX系列函数来绘图进,都会产生一个全新的Canvas画布。
2、如果在DrawXXX前,调用平移、旋转等函数来对Canvas进行了操作,那么这个操作是不可逆的!每次产生的画布的最新位置都是这些操作后的位置。(关于Save()、Restore()的画布可逆问题的画布再讲)
3、在Canvas与屏幕合成时,超出屏幕范围的图像是不会显示出来的。

三、旋转(Rotate)

画布的旋转是默认是围绕坐标原点来旋转的,这里容易产生错觉,看起来觉得是图片旋转了,其实我们旋转的是画布,以后在此画布上画的东西显示出来的时候全部看起来都是旋转的。其实Roate函数有两个构造函数:

void rotate(float degrees)
void rotate (float degrees, float px, float py)

第一个构造函数直接输入旋转的度数,正数是顺时针旋转,负数指逆时针旋转,它的旋转中心点是原点(0,0)
第二个构造函数除了度数以外,还可以指定旋转的中心点坐标(px,py)

下面以第一个构造函数为例,旋转一个矩形,先画出未旋转前的图形,然后再画出旋转后的图形;

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5.   
  6.     Paint paint_green = generatePaint(Color.GREEN, Style.FILL, 5);  
  7.     Paint paint_red   = generatePaint(Color.RED, Style.STROKE, 5);  
  8.       
  9.     Rect rect1 = new Rect(300,10,500,100);  
  10.     canvas.drawRect(rect1, paint_red); //画出原轮廓  
  11.       
  12.     canvas.rotate(30);//顺时针旋转画布  
  13.     canvas.drawRect(rect1, paint_green);//画出旋转后的矩形  
  14. }   
效果图是这样的:


这个最终屏幕显示的构造过程是这样的:

下图显示的是第一次画图合成过程,此时仅仅调用canvas.drawRect(rect1, paint_red); 画出原轮廓


然后是先将Canvas正方向依原点旋转30度,然后再与上面的屏幕合成,最后显示出我们的复合效果。


有关Canvas与屏幕的合成关系我觉得我已经讲的够详细了,后面的几个操作Canvas的函数,我就不再一一讲它的合成过程了。

四、缩放(scale )

public void scale (float sx, float sy) 
public final void scale (float sx, float sy, float px, float py)

其实我也没弄懂第二个构造函数是怎么使用的,我就先讲讲第一个构造函数的参数吧
float sx:水平方向伸缩的比例,假设原坐标轴的比例为n,不变时为1,在变更的X轴密度为n*sx;所以,sx为小数为缩小,sx为整数为放大
float sy:垂直方向伸缩的比例,同样,小数为缩小,整数为放大

注意:这里有X、Y轴的密度的改变,显示到图形上就会正好相同,比如X轴缩小,那么显示的图形也会缩小。一样的。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5. //  //scale 缩放坐标系密度  
  6.     Paint paint_green = generatePaint(Color.GREEN, Style.STROKE, 5);  
  7.     Paint paint_red   = generatePaint(Color.RED, Style.STROKE, 5);  
  8.       
  9.     Rect rect1 = new Rect(10,10,200,100);  
  10.     canvas.drawRect(rect1, paint_green);  
  11.       
  12.     canvas.scale(0.5f, 1);  
  13.     canvas.drawRect(rect1, paint_red);  
  14. }   

五、扭曲(skew)

其实我觉得译成斜切更合适,在PS中的这个功能就差不多叫斜切。但这里还是直译吧,大家都是这个名字。看下它的构造函数:
void skew (float sx, float sy)

参数说明:
float sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值,
float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值,

注意,这里全是倾斜角度的tan值哦,比如我们打算在X轴方向上倾斜60度,tan60=根号3,小数对应1.732

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5.     //skew 扭曲  
  6.     Paint paint_green = generatePaint(Color.GREEN, Style.STROKE, 5);  
  7.     Paint paint_red   = generatePaint(Color.RED, Style.STROKE, 5);  
  8.       
  9.     Rect rect1 = new Rect(10,10,200,100);  
  10.   
  11.     canvas.drawRect(rect1, paint_green);  
  12.     canvas.skew(1.732f,0);//X轴倾斜60度,Y轴不变  
  13.     canvas.drawRect(rect1, paint_red);  
  14. }   

五、裁剪画布(clip系列函数)

裁剪画布是利用Clip系列函数,通过与Rect、Path、Region取交、并、差等集合运算来获得最新的画布形状。除了调用Save、Restore函数以外,这个操作是不可逆的,一但Canvas画布被裁剪,就不能再被恢复!
Clip系列函数如下:
boolean clipPath(Path path)
boolean clipPath(Path path, Region.Op op)
boolean clipRect(Rect rect, Region.Op op)
boolean clipRect(RectF rect, Region.Op op)
boolean clipRect(int left, int top, int right, int bottom)
boolean clipRect(float left, float top, float right, float bottom)
boolean clipRect(RectF rect)
boolean clipRect(float left, float top, float right, float bottom, Region.Op op)
boolean clipRect(Rect rect)
boolean clipRegion(Region region)
boolean clipRegion(Region region, Region.Op op)

以上就是根据Rect、Path、Region来取得最新画布的函数,难度都不大,就不再一一讲述。利用ClipRect()来稍微一讲。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5.     canvas.drawColor(Color.RED);  
  6.     canvas.clipRect(new Rect(100100200200));  
  7.     canvas.drawColor(Color.GREEN);  
  8. }   
先把背景色整个涂成红色。显示在屏幕上
然后裁切画布,最后最新的画布整个涂成绿色。可见绿色部分,只有一小块,而不再是整个屏幕了。
关于两个画布与屏幕合成,我就不再画图了,跟上面的合成过程是一样的。


六、画布的保存与恢复(save()、restore())

前面我们讲的所有对画布的操作都是不可逆的,这会造成很多麻烦,比如,我们为了实现一些效果不得不对画布进行操作,但操作完了,画布状态也改变了,这会严重影响到后面的画图操作。如果我们能对画布的大小和状态(旋转角度、扭曲等)进行实时保存和恢复就最好了。
这小节就给大家讲讲画布的保存与恢复相关的函数——Save()、Restore()。
int save ()
void restore()

这两个函数没有任何的参数,很简单。
Save():每次调用Save()函数,都会把当前的画布的状态进行保存,然后放入特定的栈中;
restore():每当调用Restore()函数,就会把栈中最顶层的画布状态取出来,并按照这个状态恢复当前的画布,并在这个画布上做画。

为了更清晰的显示这两个函数的作用,下面举个例子:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5.     canvas.drawColor(Color.RED);  
  6.       
  7.     //保存当前画布大小即整屏  
  8.     canvas.save();   
  9.       
  10.     canvas.clipRect(new Rect(100100800800));  
  11.     canvas.drawColor(Color.GREEN);  
  12.       
  13.     //恢复整屏画布  
  14.     canvas.restore();  
  15.       
  16.     canvas.drawColor(Color.BLUE);  
  17. }   
他图像的合成过程为:(最终显示为全屏幕蓝色)

下面我通过一个多次利用Save()、Restore()来讲述有关保存Canvas画布状态的栈的概念:代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5.     canvas.drawColor(Color.RED);  
  6.     //保存的画布大小为全屏幕大小  
  7.     canvas.save();  
  8.       
  9.     canvas.clipRect(new Rect(100100800800));  
  10.     canvas.drawColor(Color.GREEN);  
  11.     //保存画布大小为Rect(100, 100, 800, 800)  
  12.     canvas.save();  
  13.       
  14.     canvas.clipRect(new Rect(200200700700));  
  15.     canvas.drawColor(Color.BLUE);  
  16.     //保存画布大小为Rect(200, 200, 700, 700)  
  17.     canvas.save();  
  18.       
  19.     canvas.clipRect(new Rect(300300600600));  
  20.     canvas.drawColor(Color.BLACK);  
  21.     //保存画布大小为Rect(300, 300, 600, 600)  
  22.     canvas.save();  
  23.       
  24.     canvas.clipRect(new Rect(400400500500));  
  25.     canvas.drawColor(Color.WHITE);  
  26. }   

显示效果为:


在这段代码中,总共调用了四次Save操作。上面提到过,每调用一次Save()操作就会将当前的画布状态保存到栈中,所以这四次Save()所保存的状态的栈的状态如下:


注意在,第四次Save()之后,我们还对画布进行了canvas.clipRect(new Rect(400, 400, 500, 500));操作,并将当前画布画成白色背景。也就是上图中最小块的白色部分,是最后的当前的画布。

如果,现在使用Restor(),会怎样呢,会把栈顶的画布取出来,当做当前画布的画图,试一下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5.     canvas.drawColor(Color.RED);  
  6.     //保存的画布大小为全屏幕大小  
  7.     canvas.save();  
  8.       
  9.     canvas.clipRect(new Rect(100100800800));  
  10.     canvas.drawColor(Color.GREEN);  
  11.     //保存画布大小为Rect(100, 100, 800, 800)  
  12.     canvas.save();  
  13.       
  14.     canvas.clipRect(new Rect(200200700700));  
  15.     canvas.drawColor(Color.BLUE);  
  16.     //保存画布大小为Rect(200, 200, 700, 700)  
  17.     canvas.save();  
  18.       
  19.     canvas.clipRect(new Rect(300300600600));  
  20.     canvas.drawColor(Color.BLACK);  
  21.     //保存画布大小为Rect(300, 300, 600, 600)  
  22.     canvas.save();  
  23.       
  24.     canvas.clipRect(new Rect(400400500500));  
  25.     canvas.drawColor(Color.WHITE);  
  26.       
  27.     //将栈顶的画布状态取出来,作为当前画布,并画成黄色背景  
  28.     canvas.restore();  
  29.     canvas.drawColor(Color.YELLOW);  
  30. }   
上段代码中,把栈顶的画布状态取出来,作为当前画布,然后把当前画布的背景色填充为黄色

那如果我连续Restore()三次,会怎样呢?
我们先分析一下,然后再看效果:Restore()三次的话,会连续出栈三次,然后把第三次出来的Canvas状态当做当前画布,也就是Rect(100, 100, 800, 800),所以如下代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onDraw(Canvas canvas) {  
  2.     // TODO Auto-generated method stub  
  3.     super.onDraw(canvas);  
  4.       
  5.     canvas.drawColor(Color.RED);  
  6.     //保存的画布大小为全屏幕大小  
  7.     canvas.save();  
  8.       
  9.     canvas.clipRect(new Rect(100100800800));  
  10.     canvas.drawColor(Color.GREEN);  
  11.     //保存画布大小为Rect(100, 100, 800, 800)  
  12.     canvas.save();  
  13.       
  14.     canvas.clipRect(new Rect(200200700700));  
  15.     canvas.drawColor(Color.BLUE);  
  16.     //保存画布大小为Rect(200, 200, 700, 700)  
  17.     canvas.save();  
  18.       
  19.     canvas.clipRect(new Rect(300300600600));  
  20.     canvas.drawColor(Color.BLACK);  
  21.     //保存画布大小为Rect(300, 300, 600, 600)  
  22.     canvas.save();  
  23.       
  24.     canvas.clipRect(new Rect(400400500500));  
  25.     canvas.drawColor(Color.WHITE);  
  26.       
  27.     //连续出栈三次,将最后一次出栈的Canvas状态作为当前画布,并画成黄色背景  
  28.     canvas.restore();  
  29.     canvas.restore();  
  30.     canvas.restore();  
  31.     canvas.drawColor(Color.YELLOW);  
  32. }   
结果为:


OK啦,这篇就到了啦。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值