当前位置:   article > 正文

【Android】自定义View、画家(画布)Canvas与画笔Paint的应用——画图、涂鸦板app的实现_android 自定义绘画 actionup

android 自定义绘画 actionup

利用一个简单的画图app来说明安卓的图形处理类与自定义View的应用。

如下图,有一个供用户自己任意画图、涂鸦的app,


这里不做那么花俏了,仅提供黑白两色,但可以改变笔尖的粗细。

实质上这里的橡皮擦就是白色的画笔,根本不用使用到画笔的setXfermode方法,要搞一堆复杂的工程。

用户画完图之后可以保存图像。图像的文件名是当前的时间,保存的位置是sdcard的根目录。

制作过程如下:

1、先设置好字体文件res\values\strings.xml,主要是app的名称与菜单各个子项的字符。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <string name="app_name">画图</string>
  4. <string name="menu1">画笔宽度</string>
  5. <string name="menu1_sub1">1</string>
  6. <string name="menu1_sub2">5</string>
  7. <string name="menu1_sub3">10</string>
  8. <string name="menu1_sub4">50</string>
  9. <string name="menu2">画笔</string>
  10. <string name="menu3">橡皮擦</string>
  11. <string name="menu4">保存</string>
  12. <string name="menu5">退出</string>
  13. <string name="menu_author">作者:yongh701</string>
  14. </resources>
2、之后就是菜单文件的设置,这里不再赘述了,在《【Android】日期拾取器、时间拾取器与菜单》( 点击打开链接)与《【Android】app透明与字体颜色更变、上下文菜单》( 点击打开链接)两篇文章都详细搞过菜单的东西,主要是第一个菜单选项“画笔宽度”是带有子项的,因此,设置菜单的id是分别给子项设置id,而不是主项,主项无须id。

  1. <menu xmlns:android="http://schemas.android.com/apk/res/android" >
  2. <item android:title="@string/menu1">
  3. <menu>
  4. <group android:checkableBehavior="single" >
  5. <item
  6. android:id="@+id/menu1_sub1"
  7. android:title="@string/menu1_sub1"/>
  8. <item
  9. android:id="@+id/menu1_sub2"
  10. android:title="@string/menu1_sub2"/>
  11. <item
  12. android:id="@+id/menu1_sub3"
  13. android:title="@string/menu1_sub3"/>
  14. <item
  15. android:id="@+id/menu1_sub4"
  16. android:title="@string/menu1_sub4"/>
  17. </group>
  18. </menu>
  19. </item>
  20. <item
  21. android:id="@+id/menu2"
  22. android:title="@string/menu2"/>
  23. <item
  24. android:id="@+id/menu3"
  25. android:title="@string/menu3"/>
  26. <item
  27. android:id="@+id/menu4"
  28. android:title="@string/menu4"/>
  29. <item
  30. android:id="@+id/menu5"
  31. android:title="@string/menu5"/>
  32. <item android:title="@string/menu_author"/>
  33. </menu>

3、由于一会儿还要把用户画出来的图片写入的sdcard卡,因此将在AndroidManifest.xml申请sdcard的写入的权限:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.painter"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="8"
  8. android:targetSdkVersion="18" />
  9. <!-- 需要在SD卡写入数据的权限 -->
  10. <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
  11. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  12. <application
  13. android:allowBackup="true"
  14. android:icon="@drawable/ic_launcher"
  15. android:label="@string/app_name"
  16. android:theme="@style/AppTheme" >
  17. <activity
  18. android:name="com.painter.MainActivity"
  19. android:label="@string/app_name" >
  20. <intent-filter>
  21. <action android:name="android.intent.action.MAIN" />
  22. <category android:name="android.intent.category.LAUNCHER" />
  23. </intent-filter>
  24. </activity>
  25. </application>
  26. </manifest>

4、之后就像《【Android】自定义View、画布Canvas与画笔Paint》( 点击打开链接)一样,新建一个自定义的View,这里是DrawView。这个DrawView是本app实现的核心。其构造方法,使用public DrawView(Context context, AttributeSet attrs) {super(context, attrs);} ,这个带有两个参数的构造方法,因为一会儿这个DrawView将以xml的方式直接布置在MainActivity。同时通过Alt+Shift+S->V选择继承protected void onDraw(Canvas canvas) {},public boolean onTouchEvent(MotionEvent event) {}这两个方法,一个是安卓图像处理技术的基本方法onDraw,一个是用户触摸这个View时发生的事件onTouchEvent方法。同时自己添加一个saveBitmap方法,用来实现图片的最终的保存。

DrawView.java的代码如下:

  1. package com.painter;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.text.SimpleDateFormat;
  5. import java.util.Date;
  6. import java.util.Locale;
  7. import android.content.Context;
  8. import android.graphics.Bitmap;
  9. import android.graphics.Bitmap.Config;
  10. import android.graphics.Canvas;
  11. import android.graphics.Color;
  12. import android.graphics.Paint;
  13. import android.graphics.Path;
  14. import android.os.Environment;
  15. import android.util.AttributeSet;
  16. import android.view.MotionEvent;
  17. import android.view.View;
  18. import android.widget.Toast;
  19. public class DrawView extends View {
  20. private Bitmap cacheBitmap;// 画纸
  21. private Canvas cacheCanvas;// 创建画布、画家
  22. private Path path;// 绘图的路径
  23. public Paint paint;// 画笔
  24. private float preX, preY;// 之前的XY的位置,用于下面的手势移动
  25. private int view_width, view_height;// 屏幕的高度与宽度
  26. public DrawView(Context context, AttributeSet attrs) {
  27. super(context, attrs);
  28. path = new Path();
  29. paint = new Paint();
  30. cacheCanvas = new Canvas();
  31. // 获取屏幕的高度与宽度
  32. view_width = context.getResources().getDisplayMetrics().widthPixels;
  33. view_height = context.getResources().getDisplayMetrics().heightPixels;
  34. cacheBitmap = Bitmap.createBitmap(view_width, view_height,
  35. Config.ARGB_8888);// 建立图像缓冲区用来保存图像
  36. cacheCanvas.setBitmap(cacheBitmap);
  37. cacheCanvas.drawColor(Color.WHITE);
  38. paint.setColor(Color.BLACK);// 设置画笔的默认颜色
  39. paint.setStyle(Paint.Style.STROKE);// 设置画笔的填充方式为无填充、仅仅是画线
  40. paint.setStrokeWidth(1);// 设置画笔的宽度为1
  41. }
  42. @Override
  43. protected void onDraw(Canvas canvas) {
  44. super.onDraw(canvas);
  45. canvas.drawBitmap(cacheBitmap, 0, 0, paint);// 把cacheBitmap画到DrawView上
  46. }
  47. @Override
  48. public boolean onTouchEvent(MotionEvent event) {
  49. // 获取触摸位置
  50. float x = event.getX();
  51. float y = event.getY();
  52. switch (event.getAction()) {// 获取触摸的各个瞬间
  53. case MotionEvent.ACTION_DOWN:// 手势按下
  54. path.moveTo(x, y);// 绘图的起始点
  55. preX = x;
  56. preY = y;
  57. break;
  58. case MotionEvent.ACTION_MOVE:
  59. float dx = Math.abs(x - preX);
  60. float dy = Math.abs(y - preY);
  61. if (dx > 5 || dy > 5) {// 用户要移动超过5像素才算是画图,免得手滑、手抖现象
  62. path.quadTo(preX, preY, (x + preX) / 2, (y + preY) / 2);
  63. preX = x;
  64. preY = y;
  65. cacheCanvas.drawPath(path, paint);// 绘制路径
  66. }
  67. break;
  68. case MotionEvent.ACTION_UP:
  69. path.reset();
  70. break;
  71. }
  72. invalidate();
  73. return true;
  74. }
  75. public void saveBitmap() throws Exception {
  76. String sdpath = Environment.getExternalStorageDirectory()
  77. .getAbsolutePath();// 获取sdcard的根路径
  78. String filename = new SimpleDateFormat("yyyyMMddhhmmss",
  79. Locale.getDefault())
  80. .format(new Date(System.currentTimeMillis()));// 产生时间戳,称为文件名
  81. File file = new File(sdpath + File.separator + filename + ".png");
  82. file.createNewFile();
  83. FileOutputStream fileOutputStream = new FileOutputStream(file);
  84. cacheBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);// 以100%的品质创建png
  85. // 人走带门
  86. fileOutputStream.flush();
  87. fileOutputStream.close();
  88. Toast.makeText(getContext(),
  89. "图像已保存到" + sdpath + File.separator + filename + ".png",
  90. Toast.LENGTH_SHORT).show();
  91. }
  92. }

整个DrawView.java做了如下的事情:

(1)设置一张画纸cacheBitmap,两个画家Canvas与cacheCanvas,一支画笔paint,这里之所以要有两位画家,是因为onDraw方法独占Canvas这个类成员。用户每触摸一次屏幕,都会触发onTouchEvent方法,设置cacheCanvas把用户触摸时绘制的路径放到画纸cacheBitmap上,通过invalidate();方法的调用再次onDraw方法,Canvas把画纸cacheBitmap放到DrawView这个我们自定义的View上。用户每触摸一次屏幕都会执行一次这个操作。

(2)由于每次执行invalidate()方法,都会触发onDraw方法,因此初始化的工作应通通放在自定义View的构造方法中以节省内存,这个问题在《【Android】利用自定义View的重绘实现拖动移动,获取组件的尺寸》(点击打开链接)已经讲过了,这里不再赘述。构造方法,完成画家(画布)Canvas与画笔Paint,绘图路径Path的初始化。

关键是要把初始化之后的画纸cacheBitmap放到画家cacheCanvas手上,同时命令画家cacheCanvas把这张画纸cacheBitmap全部涂白,也就是说把画图的背景颜色设置为白色。否则一会儿你保存出来的图像的背景色默认是黑色的。虽然你看到的自定义View是白色的。

在画笔Paint初始化的事情,注意要把画笔设置为paint.setStyle(Paint.Style.STROKE);仅仅是画边的方法,这样才能做到涂鸦的效果,否则画笔默认是,附件画图画矩形那种拖泥带水的效果。

(3)触摸事件onTouchEvent里的作图方法这里反而没什么好说的,计算机图形学中最基本的内容。不懂就照复制就是了。

(4)最后的保存图像的方法saveBitmap()也没什么好说的。就是安卓对sdcard卡的操作,具体见《【Android】读取sdcard上的图片》(点击打开链接),与Java对文件的操作的综合,具体见《【Java】输入与输出与JDK1.5之后的新型字符串StringBuilder》(点击打开链接)。

5、通过自定义的View,能让res\layout\activity_main.xml这个MainActivity的布局xml,与MainActivity.java的代码变得简洁。res\layout\activity_main.xml将变得如下的简短,就放一个DrawView,该实现的东西都在这个自定义View中完成。

  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent" >
  4. <com.painter.DrawView
  5. android:id="@+id/drawView1"
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent" />
  8. </FrameLayout>
6、最后在MainActivity.java中实现指明各个菜单的实现方法就能完成整个app,OnCreate方法根本就是什么都没有,仅仅是加载布局文件。

  1. package com.painter;
  2. import android.os.Bundle;
  3. import android.app.Activity;
  4. import android.graphics.Color;
  5. import android.view.Menu;
  6. import android.view.MenuItem;
  7. public class MainActivity extends Activity {
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. setContentView(R.layout.activity_main);
  12. }
  13. @Override
  14. public boolean onCreateOptionsMenu(Menu menu) {
  15. // Inflate the menu; this adds items to the action bar if it is present.
  16. getMenuInflater().inflate(R.menu.main, menu);
  17. return true;
  18. }
  19. // 处理菜单事件
  20. @Override
  21. public boolean onOptionsItemSelected(MenuItem item) {
  22. DrawView drawView = (DrawView) findViewById(R.id.drawView1);
  23. switch (item.getItemId()) {
  24. // 设置id为menu_exit的菜单子项所要执行的方法。
  25. case R.id.menu1_sub1:
  26. drawView.paint.setStrokeWidth(1);
  27. break;
  28. case R.id.menu1_sub2:
  29. drawView.paint.setStrokeWidth(5);
  30. break;
  31. case R.id.menu1_sub3:
  32. drawView.paint.setStrokeWidth(10);
  33. break;
  34. case R.id.menu1_sub4:
  35. drawView.paint.setStrokeWidth(50);
  36. break;
  37. case R.id.menu2:
  38. drawView.paint.setColor(Color.BLACK);
  39. break;
  40. case R.id.menu3:
  41. drawView.paint.setColor(Color.WHITE);
  42. break;
  43. case R.id.menu4:
  44. try {
  45. drawView.saveBitmap();
  46. } catch (Exception e) {
  47. e.printStackTrace();
  48. }
  49. break;
  50. case R.id.menu5:
  51. System.exit(0);// 结束程序
  52. break;
  53. }
  54. return true;
  55. }
  56. }
最后,我上传了一份源码给大家:http://download.csdn.net/detail/yongh701/8900457

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

闽ICP备14008679号