当前位置:   article > 正文

Android APP完整基础教程(15)图形系统-Canvas绘图_android canvas

android canvas

1 绘图基础

1.1 绘图基础类解读与实战

绘图基础类涉及 Canvas(画布)、Paint(画笔)、Path(多条直线任意图形)。

@1 Canvas类解读

Android绘图方式是继承View组件,并重写它的onDraw()方法来实现绘制。Canvas的绘制方法有:

API详细内容可以参照文档:Android Canvas 各种drawXXX绘制方法

除了绘制drawXXX方法,还有rotate(旋转)、scale(缩放)skew(倾斜)、translate(平移)来对其进行坐标变换

关于Canvas画布类,更多解读可查看官方文档:Android Canvas类详细解读

@2 Paint类解读

Paint表示Canvas画布上的画笔,主要用于设置颜色、粗细、绘制风格和填充风格等。Paint的设置方式有:

 API详细内容可以参照文档:Android Paint 各种setXXX方法解读

关于Paint画笔类,更多解读可查看官方文档:Android Paint类详细解读

@3 Path类解读

Path可以预先在View上将多个点连接成一条“路径”,然后调用Canvas的drawPath方法沿着路径绘制任意图形,灵活度很高。Path中的方法有:

 API详细内容可以参照官方文档:Android Path类详细解读

@4 基础类实战(基于Canvas、Paint、Path绘制图形训练)

绘制内容及程序最终运行结果如下所示:

关于该程序,自定义View的关键代码如下所示:

  1. public class MyView extends View {
  2. private static final String TAG = "MyView";
  3. private Paint paint =new Paint();
  4. private Path path1 = new Path();
  5. private Path path2 = new Path();
  6. private Path path3 = new Path();
  7. public MyView(Context context) {
  8. super(context);
  9. }
  10. @Override
  11. protected void onDraw(Canvas canvas) {
  12. super.onDraw(canvas);
  13. canvas.drawColor(Color.WHITE);
  14. paint.setAntiAlias(true);//抗锯齿
  15. paint.setColor(Color.RED);//画笔颜色
  16. paint.setStyle(Paint.Style.STROKE);//描边模式
  17. paint.setStrokeWidth(4f);//设置画笔粗细度
  18. paint.setTextSize(52f);
  19. drawsth(canvas,paint,path1,0f);
  20. paint.setStyle(Paint.Style.FILL);//填充模式
  21. paint.setColor(Color.BLUE);//画笔颜色
  22. drawsth(canvas,paint,path2,220f);
  23. paint.setColor(Color.MAGENTA);
  24. paint.setStyle(Paint.Style.STROKE);//描边模式
  25. drawTextOnCurve(canvas,paint,path3);
  26. }
  27. //绘制曲线和曲线上的文字
  28. private void drawTextOnCurve(Canvas canvas, Paint paint,Path path){
  29. path.moveTo(440f,10f);
  30. path.lineTo(460f,80f);
  31. path.lineTo(490f,150f);
  32. path.lineTo(550f,250f);
  33. path.lineTo(700f,400f);
  34. canvas.drawPath(path,paint);
  35. canvas.drawTextOnPath("曲线上的文字",path,10f,10f,paint);
  36. }
  37. //绘制基本图形、Path不规则图形和文字
  38. private void drawsth(Canvas canvas, Paint paint,Path path,float offset){
  39. //paint绘制圆形,参数:圆心坐标(x,y)半径r,画笔paint
  40. canvas.drawCircle(100f+offset,100f,90f,paint);
  41. //paint绘制矩形,参数:左上角坐标(x,y),右下角坐标(x,y),画笔paint
  42. canvas.drawRect(10f+offset,200f,210f+offset,400f,paint);
  43. //paint绘制圆角矩形,参数:左上角坐标(x,y),右下角坐标(x,y),x方向上的圆角半径,y方向上的圆角半径,画笔paint
  44. canvas.drawRoundRect(10f+offset,410f,210f+offset,610f,40f,40f,paint);
  45. //paint绘制椭圆,参数:左上角坐标(x,y),右下角坐标(x,y)[参数表示矩形内切的椭圆],画笔paint
  46. canvas.drawOval(10f+offset,620f,210f+offset,720f,paint);
  47. //Path绘制三角形,moveto表示第1个坐标,lineto分别表示第2、3个坐标。以此类推。
  48. path.moveTo(110f+offset,730f);
  49. path.lineTo(10f+offset,930f);
  50. path.lineTo(210f+offset,930f);
  51. path.close();
  52. canvas.drawPath(path,paint);
  53. //Path绘制不规则多点多边形
  54. path.moveTo(10f+offset,950f);
  55. path.lineTo(50f+offset,1000f);
  56. path.lineTo(200f+offset,970f);
  57. path.lineTo(150f+offset,1070f);
  58. path.lineTo(10f+offset,1110f);
  59. path.lineTo(210f+offset,1130f);
  60. path.close();
  61. canvas.drawPath(path,paint);
  62. //注意:这个坐标(x,y)并不是文字的左上角,而是一个与左下角比较接近的位置。
  63. canvas.drawText("测试文字",10f+offset,1190f,paint);
  64. }
  65. }

在MainActivity中实现代码为:

  1. public class MainActivity extends AppCompatActivity {
  2. private static String TAG = "MainActivity";
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(new MyView(this));
  7. }
  8. }

说明:该程序利用关键类Canvas、Paint、Path类来绘制基本图形、文字和沿曲线绘制文字。

1.2 PathEffect解读

@1 PathEffect类解读

PathEffect派生类主要有:

ComposePathEffect:Android ComposePathEffect类详细解读,组合2种效果。
CornerPathEffect:Android CornerPathEffect类详细解读,平滑过渡。
DashPathEffect:Android DashPathEffect类详细解读,线段虚线化。
DiscretePathEffect:Android DiscretePathEffect类详细解读,打散线段。
PathDashPathEffect:Android PathDashPathEffect类详细解读,使用Path图形来填充当前的路径。
SumPathEffect:Android SumPathEffect类详细解读,叠加2种效果。

@2 PathEffect类实战

分别展示以上几种PathEffect派生类的效果,最终呈现 如下所示:

关于该程序,自定义View的关键代码如下所示:

  1. public class MyView extends View {
  2. private static final String TAG = "MyView";
  3. private Paint paint =new Paint();
  4. private PathEffect[] effects = new PathEffect[7];
  5. private int[] colors = new int[]{Color.BLACK,Color.BLUE,
  6. Color.RED,Color.GREEN,Color.CYAN,Color.MAGENTA,Color.GRAY};
  7. private String[] effect = new String[]
  8. {"null","CornerPathEffect","DiscretePathEffect","DashPathEffect",
  9. "PathDashPathEffect","ComposePathEffect","SumPathEffect"};
  10. private Path path = new Path();
  11. public MyView(Context context) {
  12. super(context);
  13. paint.setStyle(Paint.Style.STROKE);
  14. paint.setStrokeWidth(4f);
  15. paint.setTextSize(38f);
  16. path.moveTo(0f,0f);
  17. for(int i=0;i<=25;i++){
  18. path.lineTo(i*25f,(float)(Math.random()*90));
  19. }
  20. effects[0] = null;
  21. effects[1] = new CornerPathEffect(10f);
  22. effects[2] = new DiscretePathEffect(3.0f,5.0f);
  23. effects[3] = new DashPathEffect(new float[]{20f,10f,5f,10f},0f);
  24. Path p = new Path();
  25. p.addRect(0f,0f,8f,8f,Path.Direction.CCW);
  26. effects[4] = new PathDashPathEffect(p,12f,3f,PathDashPathEffect.Style.ROTATE);
  27. effects[5] = new ComposePathEffect(effects[3],effects[4]);
  28. effects[6] = new SumPathEffect(effects[2],effects[4]);
  29. }
  30. @Override
  31. protected void onDraw(Canvas canvas) {
  32. canvas.drawColor(Color.WHITE);
  33. canvas.translate(10f,10f);
  34. for(int k=0;k<effects.length;k++){
  35. paint.setColor(colors[k]);
  36. canvas.drawText(k+" "+effect[k],10f,50f+k*90f,paint);
  37. }
  38. canvas.translate(400f,0f);
  39. for(int j=0;j<effects.length;j++){
  40. paint.setPathEffect(effects[j]);
  41. paint.setColor(colors[j]);
  42. canvas.drawPath(path,paint);
  43. canvas.translate(0f,90f);
  44. }
  45. //注意:这里如果不设置为null重绘时paint依然持有effect效果
  46. paint.setPathEffect(null);
  47. }
  48. }

在MainActivity中实现代码同上,依然为:

  1. public class MainActivity extends AppCompatActivity {
  2. private static String TAG = "MainActivity";
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(new MyView(this));
  7. }
  8. }

该程序主要展现几种PathEffect派生类的实际效果。

1.3 绘制简单的动画内容

@1 动起来的View

以上只是静态图像的显示,如果要动起来,需要让View组件上的绘图不断刷新,通知View重绘的方法是:invalidate(UI线程)和postInvalidate(非UI线程)

为了保留之前的绘制内容,采用双缓冲技术。它的主要原理是:当一些列动画争先显示时,程序在不断改变它,前面的画面还没有显示完,程序又请求重新绘制,这就回导致屏幕不停闪烁。为了避免闪烁,可以使用双缓冲技术,将要处理的图片都在内存中处理好之后,再将其显示到屏幕上。这样显示出来的总是完整的图像,不会出现闪烁现象。而在android系统中可以理解为:当程序需要在指定View上绘制时,不直接绘制,而是先绘制在内存中的Bitmap图片上,等到内存中Bitmap图片绘制好后,一次性将Bitmap图片绘制到View组件上。

@2 实战(使用双缓冲的绘图板)

实现功能为:触摸屏幕时候沿着触摸的痕迹自由绘制内容。效果如下:

关于该程序,自定义View的关键代码如下所示:

  1. public class MyView extends View {
  2. private static final String TAG = "MyView";
  3. private Paint paint =new Paint(Paint.DITHER_FLAG);
  4. private Paint bitmapPaint = new Paint();
  5. private Path path = new Path();
  6. private float preX = 0.0f;
  7. private float preY = 0.0f;
  8. private Bitmap cacheBitmap;
  9. private Canvas cacheCanvas = new Canvas();
  10. public MyView(Context context) {
  11. super(context);
  12. }
  13. public MyView(Context context,AttributeSet attrs){
  14. super(context,attrs);
  15. }
  16. public MyView(Context context,AttributeSet attrs,int defStyleAttr){
  17. super(context,attrs,defStyleAttr);
  18. }
  19. public void clear(){
  20. cacheBitmap.eraseColor(Color.WHITE);
  21. invalidate();
  22. }
  23. public void init(int w,int h){
  24. //双缓冲绘制设置
  25. cacheBitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
  26. cacheCanvas.setBitmap(cacheBitmap);
  27. paint.setColor(Color.RED);//画笔颜色
  28. paint.setStyle(Paint.Style.STROKE);//描边模式
  29. paint.setStrokeWidth(1f);//设置画笔粗细度
  30. paint.setAntiAlias(true);//抗锯齿
  31. paint.setDither(true);//防抖动
  32. }
  33. @Override
  34. protected void onDraw(Canvas canvas) {
  35. canvas.drawColor(Color.WHITE);
  36. //绘制过去内容
  37. canvas.drawBitmap(cacheBitmap,0f,0f,bitmapPaint);
  38. //绘制当下内容
  39. canvas.drawPath(path,paint);
  40. }
  41. @Override
  42. public boolean onTouchEvent(MotionEvent event) {
  43. float x = event.getX();
  44. float y = event.getY();
  45. switch(event.getAction()) {
  46. case MotionEvent.ACTION_DOWN:
  47. path.moveTo(x,y);
  48. preX = x;
  49. preY = y;
  50. break;
  51. case MotionEvent.ACTION_MOVE:
  52. path.lineTo(x,y);
  53. preX = x;
  54. preY = y;
  55. break;
  56. case MotionEvent.ACTION_UP:
  57. //将之前的轨迹图像记录在cacheCanvas中的cacheBitmap中
  58. cacheCanvas.drawPath(path,paint);
  59. path.reset();
  60. break;
  61. }
  62. invalidate();//刷新,下一帧
  63. return true;
  64. }
  65. }

在MainActivity中实现代码如下所示:

  1. public class MainActivity extends AppCompatActivity {
  2. private static String TAG = "MainActivity";
  3. private MyView myView;
  4. private Button btn_clear;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.sample_my_view);
  9. myView = findViewById(R.id.myView);
  10. btn_clear = findViewById(R.id.btn_clear);
  11. //获取屏幕宽度和高度
  12. DisplayMetrics displayMetrics = new DisplayMetrics();
  13. getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
  14. myView.init(displayMetrics.widthPixels,displayMetrics.heightPixels);
  15. btn_clear.setOnClickListener(new View.OnClickListener() {
  16. @Override
  17. public void onClick(View v) {
  18. myView.clear();
  19. }
  20. });
  21. }
  22. }

2 基本图形变换-Matrix

@1 了解Matrix

Matrix在程序中可用于平移、旋转、缩放、倾斜,不仅可用于绘制中的图形,还可用于View组件中。Matrxi的常见方法也主要是针对平移、旋转、缩放、倾斜4个操作。

关于Matrix矩阵类,更多解读可查看官方文档:Android Matrix类详细解读

@2 Matrix实战

使用按键响应Matrix的常见操作,效果如下所示:

关于该程序,自定义View的关键代码如下所示:

  1. public class MyView extends View {
  2. private static final String TAG = "MyView";
  3. private Matrix initMatrix = new Matrix();
  4. private Matrix matrix = new Matrix();
  5. private int h,w;
  6. public Bitmap bitmap = null;
  7. public MyView(Context context) {
  8. super(context);
  9. init(context);
  10. }
  11. public MyView(Context context,AttributeSet attrs){
  12. super(context,attrs);
  13. init(context);
  14. }
  15. public MyView(Context context,AttributeSet attrs,int defStyleAttr){
  16. super(context,attrs,defStyleAttr);
  17. init(context);
  18. }
  19. public void setMatrix(Matrix matrix){
  20. this.matrix = matrix;
  21. invalidate();
  22. }
  23. private void init(Context context){
  24. initMatrix.reset();
  25. initMatrix.setScale(5.0f,5.0f);
  26. try {
  27. InputStream isImage = context.getAssets().open("test1.png");
  28. bitmap = BitmapFactory.decodeStream(isImage);
  29. w = bitmap.getWidth();
  30. h = bitmap.getHeight();
  31. bitmap = Bitmap.createBitmap(bitmap,0,0, w, h, initMatrix, true);
  32. } catch (IOException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. @Override
  37. protected void onDraw(Canvas canvas) {
  38. canvas.drawColor(Color.WHITE);
  39. //居中显示
  40. float centerStartX = (canvas.getWidth()-bitmap.getWidth())*1.0f/2;
  41. float centerStartY = (canvas.getHeight()-bitmap.getHeight())*1.0f/2;
  42. canvas.translate(centerStartX,centerStartY);
  43. //bitmap绘制
  44. canvas.drawBitmap(bitmap, matrix, null);
  45. }
  46. }

在MainActivity中实现代码如下所示:

  1. public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  2. private static String TAG = "MainActivity";
  3. private MyView myView;
  4. private Button btn_reset,btn_scale,btn_skew,btn_rotate,btn_translate;
  5. private Matrix matrix = new Matrix();
  6. private float scaleX=0.5f,scaleY=0.5f;
  7. private float rotate = 0.0f;
  8. private float skewX = 0.0f,skewY = 0.0f;
  9. private float translateX=0.0f,translateY=0.0f;
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState) {
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.sample_my_view);
  14. myView = findViewById(R.id.myView);
  15. btn_reset = findViewById(R.id.btn_reset);
  16. btn_scale = findViewById(R.id.btn_scale);
  17. btn_rotate = findViewById(R.id.btn_rotate);
  18. btn_translate = findViewById(R.id.btn_translate);
  19. btn_skew = findViewById(R.id.btn_skew);
  20. matrix.reset();
  21. }
  22. @Override
  23. public void onClick(View v) {
  24. switch (v.getId()){
  25. case R.id.btn_rotate://旋转:持续,参数表度数,可持续叠加
  26. rotate+=10.0f;
  27. matrix.setRotate(rotate);
  28. break;
  29. case R.id.btn_scale://缩放范围0.5f-1.5f
  30. scaleX+=0.1f;
  31. scaleY+=0.1f;
  32. if(scaleX>=1.5f ||scaleY>=1.5f){
  33. scaleX = 0.5f;
  34. scaleY = 0.5f;
  35. }
  36. matrix.setScale(scaleX,scaleY);
  37. break;
  38. case R.id.btn_skew://错切范围:0.0f-1.0f
  39. skewX+=0.1f;
  40. if(skewX>1.0f || skewY>1.0f){
  41. skewX = 0.0f;
  42. }
  43. matrix.setSkew(skewX,skewY);
  44. break;
  45. case R.id.btn_translate://平移范围:0.0f-200.f
  46. translateX+=10.0f;
  47. if(translateX>=200.0f ||translateY>=200.0f){
  48. translateX = 0.0f;
  49. }
  50. matrix.setTranslate(translateX,translateY);
  51. break;
  52. default:
  53. matrix.reset();
  54. break;
  55. }
  56. myView.setMatrix(matrix);
  57. }
  58. }

关于该程序,sample_my_view.xml 布局参考文件如下所示:

  1. <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. xmlns:android="http://schemas.android.com/apk/res/android"
  6. android:orientation="vertical">
  7. <LinearLayout
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. android:orientation="horizontal"
  11. android:visibility="visible">
  12. <Button
  13. android:id="@+id/btn_scale"
  14. android:layout_width="wrap_content"
  15. android:layout_height="wrap_content"
  16. android:layout_weight="20"
  17. android:onClick="onClick"
  18. android:text="@string/scale" />
  19. <Button
  20. android:id="@+id/btn_rotate"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"
  23. android:layout_weight="20"
  24. android:onClick="onClick"
  25. android:text="@string/rotate" />
  26. <Button
  27. android:id="@+id/btn_skew"
  28. android:layout_width="wrap_content"
  29. android:layout_height="wrap_content"
  30. android:layout_weight="20"
  31. android:onClick="onClick"
  32. android:text="@string/skew" />
  33. <Button
  34. android:id="@+id/btn_translate"
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:layout_weight="20"
  38. android:onClick="onClick"
  39. android:text="@string/translate" />
  40. <Button
  41. android:id="@+id/btn_reset"
  42. android:layout_width="wrap_content"
  43. android:layout_height="wrap_content"
  44. android:layout_weight="20"
  45. android:onClick="onClick"
  46. android:text="@string/reset" />
  47. </LinearLayout>
  48. <com.ags.myapplication.MyView
  49. android:id="@+id/myView"
  50. android:layout_width="match_parent"
  51. android:layout_height="match_parent"
  52. android:background="@mipmap/ic_launcher" />

Matrix变换是基本的图形变换,关于一些图形特效的效果,在下一节中详细讲解。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/254580
推荐阅读
相关标签
  

闽ICP备14008679号