Android中使用图形处理引擎,2D部分是android SDK内部自己提供,3D部分是用Open GL ES 1.0。
android.graphics和android.graphics.drawable包
大部分2D使用的api都在android.graphics和android.graphics.drawable包中。
- 他们提供了图形处理相关的: Canvas、ColorFilter、Point(点)和RetcF(矩形)等
- 还有一些动画相关的:AnimationDrawable、 BitmapDrawable和TransitionDrawable等。
以图形处理来说,我们最常用到的就是在一个View上画一些图片、形状或者自定义的文本内容,这里我们都是使用Canvas来实现的。你可以获取View中的Canvas对象,绘制一些自定义形状,然后调用View. invalidate方法让View重新刷新,然后绘制一个新的形状,这样达到2D动画效果。
Canvas对象的获取方式有两种
一种我们通过重写View.onDraw方法,View中的Canvas对象会被当做参数传递过来,我们操作这个Canvas,效果会直接反应在View中。
另一种就是当你想创建一个Canvas对象时使用的方法:
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b);
上面代码创建了一个尺寸是100*100的Bitmap,使用它作为Canvas操作的对象,这时候的Canvas就是使用创建的方式。
当你使用创建的Canvas在bitmap上执行绘制方法后,你还可以将绘制的结果提交给另外一个Canvas,这样就可以达到两个Canvas协作完成的效果,简化逻辑。
但是android SDK建议使用View.onDraw参数里提供的Canvas就好,没必要自己创建一个新的Canvas对象。
我们创建一个自定义View对象,使用onDraw方法提供的Canvas进行绘制图形。
CanvasActivity.java:
package com.example.guaguale; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Bundle; import android.view.View; /** * Canvas画布 * @author zhaoyazhi * * 2014-6-18 */ public class CanvasActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new CustomView(this)); } /** * 使用内部类 自定义一个简单的View * * @author Administrator * */ class CustomView extends View { Paint paint; public CustomView(Context context) { super(context); paint = new Paint(); // 设置一个笔刷大小是3的黄色的画笔 paint.setColor(Color.YELLOW); paint.setStrokeJoin(Paint.Join.ROUND); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeWidth(3); } // 在这里我们将测试canvas提供的绘制图形方法 @Override protected void onDraw(Canvas canvas) { } } }
执行结果是一片空白的区域,因为在自定义的CustomView中,我们没有做任何的绘制操作。canvas提供的绘制图形的方法都是以draw开头的,我们可以查看api:
从上面方法的名字看来我们可以知道
- Canvas可以绘制的对象有:弧线(arcs)、填充颜色(argb和color)、 Bitmap、圆(circle和oval)、点(point)、线(line)、矩形(Rect)、图片(Picture)、圆角矩形 (RoundRect)、文本(text)、顶点(Vertices)、路径(path)。
- 通过组合这些对象我们可以画出一些简单有趣的界面出来,但是光有这些功能还是不够的,如果我要画一个仪表盘(数字围绕显示在一个圆圈中)呢?幸好Android还提供了一些对Canvas位置转换的方法:rorate、scale、translate、skew(扭曲)等,而且它允许你通过获得它的转换矩阵对象(getMatrix方法)直接操作它。这些操作就像是虽然你的笔还是原来的地方画,但是画纸旋转或者移动了,所以你画的东西的方位就产生变化。
- 为了方便一些转换操作,Canvas 还提供了保存和回滚属性的方法(save和restore),比如你可以先保存目前画纸的位置(save),然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置。下面我们就演示下canvas的一些简单用法:
案例1
package com.example.guaguale; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.os.Bundle; import android.view.View; /** * Canvas画布 * * @author zhaoyazhi * * 2014-6-18 */ public class CanvasActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new CustomView(this)); } /** * 使用内部类 自定义一个简单的View * * @author Administrator * */ class CustomView extends View { Paint paint; public CustomView(Context context) { super(context); paint = new Paint(); // 设置一个笔刷大小是3的黄色的画笔 paint.setColor(Color.YELLOW); paint.setStrokeJoin(Paint.Join.ROUND);// 设置绘制时各图形的结合方式,如平滑效果等 paint.setStrokeCap(Paint.Cap.ROUND);// 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式Cap.ROUND,或方形样式Cap.SQUARE paint.setStrokeWidth(3);// 画笔粗细 } // 在这里我们将测试canvas提供的绘制图形方法 @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.BLUE); drawLine(canvas); drawCircle(canvas); drawArcUseCenterFalse(canvas); drawArcUseCenterTrue(canvas); drawOval(canvas); drawText(canvas); drawRect(canvas); drawRoundRect(canvas); drawPath(canvas); drawTextOnPath(canvas); } /** * 在一个区域内写字 * * @param canvas */ private void drawTextOnPath(Canvas canvas) { Path path = new Path(); // 定义一条路径 path.moveTo(10, 10); // 移动到 坐标10,10 path.lineTo(50, 60); path.lineTo(200, 80); path.lineTo(10, 10); // canvas.drawPath(path, paint); canvas.drawTextOnPath("赵雅智Android_convas", path, 10, 10, paint); } /** * 圆角图形 * * @param canvas */ private void drawRoundRect(Canvas canvas) { RectF rect = new RectF(280, 120, 380, 220); // canvas.drawRoundRect(rect, rx, ry, paint) // ry: x轴的半径rx: y轴的半径 canvas.drawRoundRect(rect, 30, 30, paint); } /** * 画矩形 * * @param canvas */ private void drawRect(Canvas canvas) { RectF rect = new RectF(170, 120, 270, 220); canvas.drawRect(rect, paint); } /** * 写文字 * * @param canvas */ private void drawText(Canvas canvas) { // 按照既定点 绘制文本内容 // 第一个字母在坐标10,10 canvas.drawPosText("Android", new float[] { 10, 120,// 第一个字母在坐标 20, 130, // 第二个字母在坐标20,130 30, 140, 40, 150, 50, 160, 60, 170, 70, 180, }, paint); } /** * 画椭圆 * * @param canvas */ private void drawOval(Canvas canvas) { // 定义一个矩形区域 // new Rect(left, top, right, bottom) RectF oval = new RectF(100, 120, 160, 220); // 矩形区域内切椭圆 canvas.drawOval(oval, paint); } /** * 画圆 * * @param canvas */ private void drawCircle(Canvas canvas) { // canvas.drawCircle(cx, cy, radius, // paint)画布开始位置(半径+距边框距离),画布长,圆形半径,画笔 canvas.drawCircle(250, 280, 50, paint); } /** * * 画多边形 * * @param canvas */ private void drawPath(Canvas canvas) { Path path = new Path(); // // path.moveTo(x, y);定义一条路径 // path.lineTo(x, y);移动到 坐标10,10 path.moveTo(10, 230); path.lineTo(50, 290); path.lineTo(200, 310); path.lineTo(10, 230); canvas.drawPath(path, paint); } /** * 画没有中心的弧线区域 * * @param canvas */ private void drawArcUseCenterFalse(Canvas canvas) { // 绘制弧线区域 // RectF(left, top, right, bottom) RectF rect = new RectF(120, 10, 220, 110); // canvas.drawArc(oval, startAngle, sweepAngle, useCenter, paint) // 弧线所使用的矩形区域大小,开始角度,扫过的角度,是否使用中心 canvas.drawArc(rect, 0, 150, false, paint); } /** * 画有中心的弧线区域 * * @param canvas */ private void drawArcUseCenterTrue(Canvas canvas) { RectF rect = new RectF(230, 10, 330, 110); canvas.drawArc(rect, 0, 150, true, paint); } /** * 画线 * * @param canvas */ private void drawLine(Canvas canvas) { // canvas.drawLine(startX, startY, stopX, stopY, paint) canvas.drawLine(340, 10, 440, 10, paint); canvas.drawLine(440, 10, 340, 110, paint); canvas.drawLine(340, 110, 440, 110, paint); } } }
案例2
package com.example.guaguale; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.graphics.Shader; import android.os.Bundle; import android.view.View; import android.widget.LinearLayout; /** * Canvas画布 * * @author zhaoyazhi * * 2014-6-18 */ public class CanvasActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_canvas); init(); } private void init() { LinearLayout layout = (LinearLayout) findViewById(R.id.root); final CustomView view = new CustomView(this); view.setMinimumHeight(500); view.setMinimumWidth(300); // 通知view组件重绘 view.invalidate(); layout.addView(view); } } /** * 使用内部类 自定义一个简单的View * * @author Administrator * */ class CustomView extends View { Paint paint; public CustomView(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /* * 方法 说明 drawRect 绘制矩形 drawCircle 绘制圆形 drawOval 绘制椭圆 drawPath 绘制任意多边形 * drawLine 绘制直线 drawPoin 绘制点 */ // 创建画笔 Paint p = new Paint(); p.setColor(Color.RED);// 设置红色 canvas.drawText("画圆:", 10, 20, p);// 画文本 canvas.drawCircle(60, 20, 10, p);// 小圆 p.setAntiAlias(true);// 设置画笔的锯齿效果。 true是去除,大家一看效果就明白了 canvas.drawCircle(120, 20, 20, p);// 大圆 canvas.drawText("画线及弧线:", 10, 60, p); p.setColor(Color.GREEN);// 设置绿色 canvas.drawLine(60, 40, 100, 40, p);// 画线 canvas.drawLine(110, 40, 190, 80, p);// 斜线 // 画笑脸弧线 p.setStyle(Paint.Style.STROKE);// 设置空心 RectF oval1 = new RectF(150, 20, 180, 40); canvas.drawArc(oval1, 180, 180, false, p);// 小弧形 oval1.set(190, 20, 220, 40); canvas.drawArc(oval1, 180, 180, false, p);// 小弧形 oval1.set(160, 30, 210, 60); canvas.drawArc(oval1, 0, 180, false, p);// 小弧形 canvas.drawText("画矩形:", 10, 80, p); p.setColor(Color.GRAY);// 设置灰色 p.setStyle(Paint.Style.FILL);// 设置填满 canvas.drawRect(60, 60, 80, 80, p);// 正方形 canvas.drawRect(60, 90, 160, 100, p);// 长方形 canvas.drawText("画扇形和椭圆:", 10, 120, p); /* 设置渐变色 这个正方形的颜色是改变的 */ Shader mShader = new LinearGradient(0, 0, 100, 100, new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.LTGRAY }, null, Shader.TileMode.REPEAT); // 一个材质,打造出一个线性梯度沿著一条线。 p.setShader(mShader); // p.setColor(Color.BLUE); RectF oval2 = new RectF(60, 100, 200, 240);// 设置个新的长方形,扫描测量 canvas.drawArc(oval2, 200, 130, true, p); // 画弧,第一个参数是RectF:该类是第二个参数是角度的开始,第三个参数是多少度,第四个参数是真的时候画扇形,是假的时候画弧线 // 画椭圆,把oval改一下 oval2.set(210, 100, 250, 130); canvas.drawOval(oval2, p); canvas.drawText("画三角形:", 10, 200, p); // 绘制这个三角形,你可以绘制任意多边形 Path path = new Path(); path.moveTo(80, 200);// 此点为多边形的起点 path.lineTo(120, 250); path.lineTo(80, 250); path.close(); // 使这些点构成封闭的多边形 canvas.drawPath(path, p); // 你可以绘制很多任意多边形,比如下面画六连形 p.reset();// 重置 p.setColor(Color.LTGRAY); p.setStyle(Paint.Style.STROKE);// 设置空心 Path path1 = new Path(); path1.moveTo(180, 200); path1.lineTo(200, 200); path1.lineTo(210, 210); path1.lineTo(200, 220); path1.lineTo(180, 220); path1.lineTo(170, 210); path1.close();// 封闭 canvas.drawPath(path1, p); /* * Path类封装复合(多轮廓几何图形的路径 * 由直线段*、二次曲线,和三次方曲线,也可画以油画。drawPath(路径、油漆),要么已填充的或抚摸 * (基于油漆的风格),或者可以用于剪断或画画的文本在路径。 */ // 画圆角矩形 p.setStyle(Paint.Style.FILL);// 充满 p.setColor(Color.LTGRAY); p.setAntiAlias(true);// 设置画笔的锯齿效果 canvas.drawText("画圆角矩形:", 10, 260, p); RectF oval3 = new RectF(80, 260, 200, 300);// 设置个新的长方形 canvas.drawRoundRect(oval3, 20, 15, p);// 第二个参数是x半径,第三个参数是y半径 // 画贝塞尔曲线 canvas.drawText("画贝塞尔曲线:", 10, 310, p); p.reset(); p.setStyle(Paint.Style.STROKE); p.setColor(Color.GREEN); Path path2 = new Path(); path2.moveTo(100, 320);// 设置Path的起点 path2.quadTo(150, 310, 170, 400); // 设置贝塞尔曲线的控制点坐标和终点坐标 canvas.drawPath(path2, p);// 画出贝塞尔曲线 // 画点 p.setStyle(Paint.Style.FILL); canvas.drawText("画点:", 10, 390, p); canvas.drawPoint(60, 390, p);// 画一个点 canvas.drawPoints(new float[] { 60, 400, 65, 400, 70, 400 }, p);// 画多个点 // 画图片,就是贴图 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); canvas.drawBitmap(bitmap, 250, 360, p); } }
位置转换方法,canvas.rorate和canvas.translate
案例3
package com.example.guaguale; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.RectF; import android.os.Bundle; import android.util.Log; import android.view.View; public class CanvaDemoActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new CustomView(this)); } /** * 使用内部类 自定义一个简单的View * * @author Administrator * */ class CustomView extends View { public CustomView(Context context) { super(context); } // 在这里我们将测试canvas提供的绘制图形方法 @Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Style.STROKE); canvas.translate(canvas.getWidth() / 2, 200); // 将位置移动画纸的坐标点:150,150 canvas.drawCircle(0, 0, 100, paint); // 画圆圈 // 使用path绘制路径文字 canvas.save(); canvas.translate(-75, -75); Path path = new Path(); path.addArc(new RectF(0, 0, 150, 150), -180, 180); Paint citePaint = new Paint(paint); citePaint.setTextSize(14); citePaint.setStrokeWidth(1); canvas.drawTextOnPath("http://www.android777.com", path, 28, 0, citePaint); canvas.restore(); Paint tmpPaint = new Paint(paint); // 小刻度画笔对象 tmpPaint.setStrokeWidth(1); float y = 100; int count = 60; // 总刻度数 for (int i = 0; i < count; i++) { if (i % 5 == 0) { canvas.drawLine(0f, y, 0, y + 12f, paint); canvas.drawText(String.valueOf(i / 5 + 1), -4f, y + 25f, tmpPaint); } else { canvas.drawLine(0f, y, 0f, y + 5f, tmpPaint); } canvas.rotate(360 / count, 0f, 0f); // 旋转画纸 } // 绘制指针 tmpPaint.setColor(Color.GRAY); tmpPaint.setStrokeWidth(4); canvas.drawCircle(0, 0, 7, tmpPaint); tmpPaint.setStyle(Style.FILL); tmpPaint.setColor(Color.YELLOW); canvas.drawCircle(0, 0, 5, tmpPaint); canvas.drawLine(0, 10, 0, -65, paint); } /** * 在canvas放置图片 * * @param canvas */ private void drawBitmap(Canvas canvas) { Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.e); canvas.drawBitmap(bmp, 0, 0, null); } /** * 缩放图片 * * @param canvas */ private void drawBitmapPostScale(Canvas canvas) { Bitmap bmp1 = BitmapFactory.decodeResource(getResources(), R.drawable.e); Matrix matrix = new Matrix(); matrix.postScale(0.5f, 0.5f); Bitmap dstbmp = Bitmap.createBitmap(bmp1, 0, 0, bmp1.getWidth(), bmp1.getHeight(), matrix, true); canvas.drawBitmap(dstbmp, 200, 0, null); } /** * 旋转图片 * * @param canvas */ private void drapBitmapPostRotate(Canvas canvas) { Bitmap bmp2 = BitmapFactory.decodeResource(getResources(), R.drawable.e); Matrix matrix1 = new Matrix(); matrix1.postScale(0.8f, 0.8f); matrix1.postRotate(45); Bitmap dstbmp1 = Bitmap.createBitmap(bmp2, 0, 0, bmp2.getWidth(), bmp2.getHeight(), matrix1, true); canvas.drawBitmap(dstbmp1, 130, 100, null); } /** * 加水印图片 * * @param canvas */ private void shuiyinBitmap(Canvas canvas) { Bitmap bmp = createBitmap( BitmapFactory.decodeResource(getResources(), R.drawable.e), BitmapFactory.decodeResource(getResources(), R.drawable.c)); canvas.drawBitmap(bmp, 10, 200, null); } // 图片添加水印处理 private Bitmap createBitmap(Bitmap src, Bitmap watermark) { String tag = "createBitmap"; Log.d(tag, "create a new bitmap"); if (src == null) { return null; } int w = src.getWidth(); int h = src.getHeight(); int ww = watermark.getWidth(); int wh = watermark.getHeight(); // create the new blank bitmap Bitmap newb = Bitmap.createBitmap(w, h, Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图 Canvas cv = new Canvas(newb); // draw src into cv.drawBitmap(src, 0, 0, null);// 在 0,0坐标开始画入src // draw watermark into cv.drawBitmap(watermark, w - ww + 5, h - wh + 5, null);// 在src的右下角画入水印 // save all clip cv.save(Canvas.ALL_SAVE_FLAG);// 保存 // store cv.restore();// 存储 return newb; } } }
案例4
上面几个例子基本已经将常用的canvas.draw*方法测试过了,我们结合一些事件,做一些有用户交互的应用
package com.example.guaguale; import java.util.ArrayList; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; public class CanvasDemoActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new CustomView1(this)); } /** * 使用内部类 自定义一个简单的View * * @author Administrator * */ class CustomView1 extends View { Paint paint; private ArrayList<PointF> graphics = new ArrayList<PointF>(); PointF point; public CustomView1(Context context) { super(context); paint = new Paint(); // 设置一个笔刷大小是3的黄色的画笔 paint.setColor(Color.BLACK); paint.setStrokeJoin(Paint.Join.ROUND); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeWidth(3); } @Override public boolean onTouchEvent(MotionEvent event) { graphics.add(new PointF(event.getX(), event.getY())); invalidate(); // 重新绘制区域 return true; } // 在这里我们将测试canvas提供的绘制图形方法 @Override protected void onDraw(Canvas canvas) { for (PointF point : graphics) { canvas.drawPoint(point.x, point.y, paint); } } } }
赵雅智_Android_Canvas,布布扣,bubuko.com