View实现涂鸦、撤销以及重做功能

  1. import java.io.File;  
  2. import java.io.FileNotFoundException;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.util.ArrayList;  
  6. import java.util.Iterator;  
  7. import java.util.List;  
  8.   
  9. import android.content.Context;  
  10. import android.graphics.Bitmap;  
  11. import android.graphics.Canvas;  
  12. import android.graphics.Paint;  
  13. import android.graphics.Path;  
  14. import android.graphics.Bitmap.CompressFormat;  
  15. import android.os.Environment;  
  16. import android.view.MotionEvent;  
  17. import android.view.View;  
  18.   
  19. /** 
  20.  * View实现涂鸦、撤销以及重做功能 
  21.  */  
  22.   
  23. public class TuyaView extends View {  
  24.   
  25.     private Bitmap mBitmap;  
  26.     private Canvas mCanvas;  
  27.     private Path mPath;  
  28.     private Paint mBitmapPaint;// 画布的画笔  
  29.     private Paint mPaint;// 真实的画笔  
  30.     private float mX, mY;// 临时点坐标  
  31.     private static final float TOUCH_TOLERANCE = 4;  
  32.       
  33.     // 保存Path路径的集合,用List集合来模拟栈  
  34.     private static List<DrawPath> savePath;  
  35.     // 记录Path路径的对象  
  36.     private DrawPath dp;  
  37.   
  38.     private int screenWidth, screenHeight;  
  39.   
  40.     private class DrawPath {  
  41.         public Path path;// 路径  
  42.         public Paint paint;// 画笔  
  43.     }  
  44.   
  45.     public TuyaView(Context context, int w, int h) {  
  46.         super(context);  
  47.         screenWidth = w;  
  48.         screenHeight = h;  
  49.   
  50.         mBitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888);  
  51.         // 保存一次一次绘制出来的图形  
  52.         mCanvas = new Canvas(mBitmap);  
  53.   
  54.         mBitmapPaint = new Paint(Paint.DITHER_FLAG);  
  55.         mPaint = new Paint();  
  56.         mPaint.setAntiAlias(true);  
  57.         mPaint.setStyle(Paint.Style.STROKE);  
  58.         mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘  
  59.         mPaint.setStrokeCap(Paint.Cap.ROUND);// 形状  
  60.         mPaint.setStrokeWidth(5);// 画笔宽度  
  61.   
  62.         savePath = new ArrayList<DrawPath>();  
  63.     }  
  64.   
  65.     @Override  
  66.     public void onDraw(Canvas canvas) {  
  67.         canvas.drawColor(0xFFAAAAAA);  
  68.         // 将前面已经画过得显示出来  
  69.         canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);  
  70.         if (mPath != null) {  
  71.             // 实时的显示  
  72.             canvas.drawPath(mPath, mPaint);  
  73.         }  
  74.     }  
  75.   
  76.     private void touch_start(float x, float y) {  
  77.         mPath.moveTo(x, y);  
  78.         mX = x;  
  79.         mY = y;  
  80.     }  
  81.   
  82.     private void touch_move(float x, float y) {  
  83.         float dx = Math.abs(x - mX);  
  84.         float dy = Math.abs(mY - y);  
  85.         if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {  
  86.             // 从x1,y1到x2,y2画一条贝塞尔曲线,更平滑(直接用mPath.lineTo也是可以的)  
  87.             mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);  
  88.             mX = x;  
  89.             mY = y;  
  90.         }  
  91.     }  
  92.   
  93.     private void touch_up() {  
  94.         mPath.lineTo(mX, mY);  
  95.         mCanvas.drawPath(mPath, mPaint);  
  96.         //将一条完整的路径保存下来(相当于入栈操作)  
  97.         savePath.add(dp);  
  98.         mPath = null;// 重新置空  
  99.     }  
  100.     /** 
  101.      * 撤销的核心思想就是将画布清空, 
  102.      * 将保存下来的Path路径最后一个移除掉, 
  103.      * 重新将路径画在画布上面。 
  104.      */  
  105.     public void undo() {  
  106.         if (savePath != null && savePath.size() > 0) {  
  107.             savePath.remove(savePath.size() - 1);  
  108.             redrawOnBitmap();  
  109.         }  
  110.     }  
  111.     /** 
  112.      * 重做 
  113.      */  
  114.     public void redo(){  
  115.         if (savePath != null && savePath.size() > 0) {  
  116.             savePath.clear();  
  117.             redrawOnBitmap();  
  118.         }  
  119.     }  
  120.       
  121.     private void redrawOnBitmap(){  
  122.         mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,  
  123.                 Bitmap.Config.ARGB_8888);  
  124.         mCanvas.setBitmap(mBitmap);// 重新设置画布,相当于清空画布   
  125.         Iterator<DrawPath> iter = savePath.iterator();  
  126.         while (iter.hasNext()) {  
  127.             DrawPath drawPath = iter.next();  
  128.             mCanvas.drawPath(drawPath.path, drawPath.paint);  
  129.         }  
  130.         invalidate();// 刷新  
  131.     }  
  132.   
  133.     @Override  
  134.     public boolean onTouchEvent(MotionEvent event) {  
  135.         float x = event.getX();  
  136.         float y = event.getY();  
  137.   
  138.         switch (event.getAction()) {  
  139.         case MotionEvent.ACTION_DOWN:  
  140.             // 每次down下去重新new一个Path  
  141.             mPath = new Path();  
  142.             //每一次记录的路径对象是不一样的  
  143.             dp = new DrawPath();  
  144.             dp.path = mPath;  
  145.             dp.paint = mPaint;  
  146.             touch_start(x, y);  
  147.             invalidate();  
  148.             break;  
  149.         case MotionEvent.ACTION_MOVE:  
  150.             touch_move(x, y);  
  151.             invalidate();  
  152.             break;  
  153.         case MotionEvent.ACTION_UP:  
  154.             touch_up();  
  155.             invalidate();  
  156.             break;  
  157.         }  
  158.         return true;  
  159.     }  
  160.   
  161.     public void saveToSDCard(){  
  162.         String fileUrl = Environment.getExternalStorageDirectory()  
  163.                 .toString() + "/android/data/test.png";  
  164.         try {  
  165.             FileOutputStream fos = new FileOutputStream(new File(fileUrl));  
  166.             mBitmap.compress(CompressFormat.PNG, 100, fos);  
  167.             fos.flush();  
  168.             fos.close();  
  169.         } catch (FileNotFoundException e) {  
  170.             e.printStackTrace();  
  171.         } catch (IOException e) {  
  172.             e.printStackTrace();  
  173.         }  
  174.     }  
  175. }  


Java代码  收藏代码
  1. import android.app.Activity;  
  2. import android.os.Bundle;  
  3. import android.util.DisplayMetrics;  
  4. import android.util.Log;  
  5. import android.view.KeyEvent;  
  6.   
  7. public class TuyaActivity extends Activity {  
  8.   
  9.     private TuyaView tuyaView = null;  
  10.   
  11.     @Override  
  12.     public void onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.   
  15.         DisplayMetrics dm = new DisplayMetrics();  
  16.         getWindowManager().getDefaultDisplay().getMetrics(dm);  
  17.   
  18.         tuyaView = new TuyaView(this, dm.widthPixels, dm.heightPixels);  
  19.         setContentView(tuyaView);  
  20.     }  
  21.   
  22.     @Override  
  23.     public boolean onKeyDown(int keyCode, KeyEvent event) {  
  24.         if (keyCode == KeyEvent.KEYCODE_BACK) {// 返回键  
  25.             tuyaView.undo();  
  26.             return true;  
  27.         }else if(keyCode == KeyEvent.KEYCODE_MENU){//MENU  
  28.             tuyaView.redo();  
  29.             return true;  
  30.         }  
  31.         return super.onKeyDown(keyCode, event);  
  32.     }  
  33.   
  34. }  


 
  • Tuya.rar (234.4 KB)
  • 下载次数: 202
原文地址:https://www.cnblogs.com/Free-Thinker/p/4750391.html