Android 涂鸦 quadTo android图片涂鸦_android

Android 涂鸦 quadTo android图片涂鸦_android_02

Android 涂鸦 quadTo android图片涂鸦_Android 涂鸦 quadTo_03


要看源码猛搓这里

/**  
  * @Title: MyView.java 

  */ 

 package com.zero.view; 


 import java.io.File; 

 import java.io.FileOutputStream; 

 import java.io.IOException; 

 import java.util.ArrayList; 

 import java.util.Iterator; 

 import java.util.List; 

 import java.util.Formatter.BigDecimalLayoutForm; 


 import android.content.Context; 

 import android.graphics.Bitmap; 

 import android.graphics.Bitmap.CompressFormat; 

 import android.graphics.BitmapFactory; 

 import android.graphics.Canvas; 

 import android.graphics.Paint; 

 import android.graphics.Path; 

 import android.os.Environment; 

 import android.util.AttributeSet; 

 import android.view.MotionEvent; 

 import android.view.View; 


 /** 

  * @ClassName: MyView 

  * @Description: 塗鴉功能實現 

  * @author ZeRo_Ci 

  * @date 2014-3-21 下午3:06:42 

  * 

  */ 

 public class MyView extends View { 

     /** 用于画线 */ 

     private Paint mPaint = null; 

     /** 用于绘制背景 */ 

     private Paint mBitmapPaint = null; 

     /** 保存点 */ 

     private Path mPath = null; 

     /** 用于选择图片 */ 

     private Bitmap mBitmap = null; 

     /** 用于背景图 */ 

     private Bitmap mBottomBitmap = null; 

     Canvas mCanvas = null; 

     /** 用于触摸点 */ 

     float posX, posY; 


     private final float TOUCH_TOLERANCE = 4; 


     private DrawPath mDrawPath = null; 

     /** 保存 */ 

     private List<DrawPath> mSavePath = null; 

     /** 清除 */ 

     private List<DrawPath> mDeletePath = null; 

     /** 图片路径 */ 

     private String mImagePath = null; 

     /** 图片的宽度 */ 

     private int mImageWidth = 480; 

     /** 图片的长度 */ 

     private int mImageHeight = 800; 

     private int mBottomBitmapDrawHeight = 0; 


     public MyView(Context context, AttributeSet attrs, int defStyle) { 

         super(context, attrs, defStyle); 

         init(); 

     } 


     public MyView(Context context, AttributeSet attrs) { 

         super(context, attrs); 

         init(); 

     } 


     public MyView(Context context) { 

         super(context); 

         init(); 

     } 


     /** 

      * 初始化实例 

      */ 

     private void init() { 

         mPaint = new Paint(); 

         mPaint.setAntiAlias(true); 

         mPaint.setDither(true); 

         mPaint.setColor(0xFFCCCCCC); 

         mPaint.setStyle(Paint.Style.STROKE); 

         mPaint.setStrokeJoin(Paint.Join.ROUND); 

         mPaint.setStrokeCap(Paint.Cap.ROUND); 

         mPaint.setStrokeWidth(12); 


         mBitmapPaint = new Paint(Paint.DITHER_FLAG); 


         mSavePath = new ArrayList<MyView.DrawPath>(); 

         mDeletePath = new ArrayList<MyView.DrawPath>(); 

         mImagePath = initPath(); 

     } 


     /** 

      * 初始化路径 

      */ 

     private String initPath() { 

         /** 获取外部存储的路径返回绝对路径的,其实就是你的SD卡的文件路径 */ 

         String ph = Environment.getExternalStorageDirectory().getAbsolutePath(); 

         if (ph == null) { 

             return null; 

         } 

         ph += "/zerotuya"; 

         File imageFile = new File(ph); 

         /** 文件操作 */ 

         if (!imageFile.exists()) { 

             /** 创建目录 */ 

             imageFile.mkdir(); 

         } 

         return ph; 

     } 


     private class DrawPath { 

         Path path; 

         Paint paint; 

     } 


     /* 

      * (non-Javadoc) 

      * 

      * @see android.view.View#onSizeChanged(int, int, int, int) 

      */ 

     @Override 

     protected void onSizeChanged(int w, int h, int oldw, int oldh) { 

         super.onSizeChanged(w, h, oldw, oldh); 

         mBottomBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 

         mCanvas = new Canvas(mBottomBitmap); 

     } 


     /* 

      * (non-Javadoc) 

      * 

      * @see android.view.View#onDraw(android.graphics.Canvas) 

      */ 

     @Override 

     protected void onDraw(Canvas canvas) { 

         canvas.drawColor(0xFF000000); 

         int nCanvasWidth = canvas.getWidth(); 

         int nCanvasHeight = canvas.getHeight(); 

         int nBitmapWidth = mBottomBitmap.getWidth(); 

         int nBitmaopHeight = mBottomBitmap.getHeight(); 

         mBottomBitmapDrawHeight = (nCanvasHeight - nBitmaopHeight) / 2; 

         canvas.drawBitmap(mBottomBitmap, 0, mBottomBitmapDrawHeight, 

                 mBitmapPaint); 

         if (mPath != null) { 

             canvas.drawPath(mPath, mPaint); 

         } 

     } 


     /* 

      * (non-Javadoc) 

      * 

      * @see android.view.View#onTouchEvent(android.view.MotionEvent) 

      */ 

     @Override 

     public boolean onTouchEvent(MotionEvent event) { 

         float x = event.getX(); 

         float y = event.getY(); 

         switch (event.getAction()) { 

         case MotionEvent.ACTION_DOWN: 

             touchDown(x, y); 

             break; 

         case MotionEvent.ACTION_MOVE: 

             touchMove(x, y); 

             break; 

         case MotionEvent.ACTION_UP: 

             touchUp(); 

             break; 

         } 


         return true; 

     } 


     /** 

      * 按下的点 

      */ 

     private void touchDown(float x, float y) { 

         mPath = new Path(); 

         mDrawPath = new DrawPath(); 

         mPath.moveTo(x, y); 

         mDrawPath.paint = new Paint(mPaint); 

         mDrawPath.path = mPath; 

         posX = x; 

         posY = y; 

         /** 

          * Android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate, 

          * 其中前者是在UI线程自身中使用,而后者在非UI线程中使用。 

          * Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用 

          * ,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。 

          * 

          *   Android程序中可以使用的界面刷新方法有两种,分别是利用Handler和利用postInvalidate()来实现在线程中刷新界面 

          * 。 

          * 

          * @author1,利用invalidate()刷新界面 

          *                               实例化一个Handler对象,并重写handleMessage方法调用invalidate 

          *                             () 实现界面刷新;而在线程中通过sendMessage发送界面更新消息。 

          * 

          * @author 2,使用postInvalidate()刷新界面 

          *         使用postInvalidate则比较简单,不需要handler,直接在线程中调用postInvalidate即可。 

          */ 

         postInvalidate(); 

     } 


     /** 

      * 移动 

      */ 

     private void touchMove(float x, float y) { 

         float dx = Math.abs(x - posX); 

         float dy = Math.abs(y - posY); 

         if (dx >= TOUCH_TOLERANCE || dy > TOUCH_TOLERANCE) { 

             mPath.quadTo(posX, posY, (x + posX) / 2, (y + posY) / 2); 

             posX = x; 

             posY = y; 

         } 

         postInvalidate(); 

     } 


     /** 

      * 抬起 

      */ 

     private void touchUp() { 

         mPath.lineTo(posX, posY); 

         mPath.offset(0, -mBottomBitmapDrawHeight); 

         mCanvas.drawPath(mPath, mPaint); 

         mSavePath.add(mDrawPath); 

         postInvalidate(); 

     } 


     /** 

      * 背景图 

      * 

      * @return 

      * 

      */ 

     private boolean setBitmap(String imagePath) { 

         Bitmap bitmap = BitmapFactory.decodeFile(imagePath); 

         int width = bitmap.getWidth(); 

         int height = bitmap.getHeight(); 

         float nxScale = -1; 

         float nyScale = -1; 

         if (width != 0 && height != 0) { 

             nxScale = (float) width / mImageWidth; 

             nyScale = (float) height / mImageHeight; 

             if (nxScale >= 1 && nyScale >= 1 || nxScale < 1 && nyScale < 1) { 

                 if (nxScale > nyScale) { 

                     width = (int) (width / nxScale); 

                     height = (int) (height / nxScale); 

                 } else { 

                     width = (int) (width / nyScale); 

                     height = (int) (height / nyScale); 

                 } 


             } 

             if (nxScale >= 1 && nyScale < 1) { 

                 width = mImageWidth; 

             } 

             if (nxScale <= 1 && nyScale >= 1) { 

                 height = mImageHeight; 

             } 

             mBitmap = Bitmap.createScaledBitmap(bitmap, width, height, true); 

             mBottomBitmap = Bitmap.createBitmap(width, height, 

                     Bitmap.Config.ARGB_8888); 

             mSavePath.clear(); 

             mDeletePath.clear(); 

             mCanvas.setBitmap(mBottomBitmap); 

             mCanvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); 

             postInvalidate(); 


             return true; 

         } else 

             return false; 

     } 


     /** 

      * 背景颜色 

      */ 

     private void setBitmapColor(int color) { 

         mBottomBitmap.eraseColor(color); 

         mSavePath.clear(); 

         mDeletePath.clear(); 

         postInvalidate(); 

     } 


     /** 

      * 画笔 

      */ 

     private void setPaint(Paint paint) { 

         mPaint = paint; 

         postInvalidate(); 

     } 


     /** 

      * 保存图片 

      */ 

     private void saveImage(String imagePath) { 

         if (mImagePath == null || mBitmap == null) { 

             return; 

         } 

         String imageName = null; 

         int nStart = imagePath.lastIndexOf('/'); 

         int nEnd = imagePath.lastIndexOf('.'); 


         imageName = imagePath.substring(nStart, nEnd); 

         imageName += ".png"; 

         imageName = mImagePath + imageName; 

         File file = new File(imageName); 


         /** 

          * @author createNewFile 

          *         当且仅当不存在具有此抽象路径名指定的名称的文件时,原子地创建由此抽象路径名指定的一个新的空文件。检查文件是否存在 

          *         ,如果不存在则创建该文件,这是单个操作,对于其他所有可能影响该文件的文件系统活动来说,该操作是原子的。 

          * 

          * @author createTempFile File.createTempFile 

          *         的用途是你想要建立一个档案暂时使用,但是你不在乎其精确的档案名 

          *         ,只要不覆盖到已存在的档案时。可以制定临时文件的文件名前缀、后缀及文件所在的目录 

          *         ,如果不指定目录,则存放在系统的临时文件夹下。 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称 

          */ 

         try { 

             file.createNewFile(); 

             /** 创建一个向指定File对象表示的文件中写入数据的文件输出流 */ 

             FileOutputStream out = new FileOutputStream(file); 

             /** 压缩图片//100是压缩率,表示压缩率为0 即不压缩 ,如果是30 ,表示压缩70% */ 

             mBottomBitmap.compress(CompressFormat.PNG, 100, out); 

             /** 

              * flush() 是把缓冲区的数据强行输出, 

              * 主要用在IO中,即清空缓冲区数据,一般在读写流(stream)的时候,数据是先被读到了内存中 

              * ,再把数据写到文件中,当你数据读完的时候不代表你的数据已经写完了 

              * ,因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了close 

              * ()方法关闭了读写流,那么这部分数据就会丢失,所以应该在关闭读写流之前先flush()。 

              */ 

             out.flush(); 

             out.close(); 

         } catch (IOException e) { 

             e.printStackTrace(); 

         } 


     } 


     /** 

      * 清理图片 

      */ 

     private void clearImage() { 

         mSavePath.clear(); 

         mDeletePath.clear(); 


         if (mBitmap != null) { 

             int width = mBitmap.getWidth(); 

             int height = mBitmap.getHeight(); 

             mBottomBitmap = Bitmap.createBitmap(width, height, 

                     Bitmap.Config.ARGB_8888); 

             mCanvas.setBitmap(mBottomBitmap); 

             mCanvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); 

         } else { 

             int width = mCanvas.getWidth(); 

             int height = mCanvas.getHeight(); 

             mBottomBitmap = Bitmap.createBitmap(width, height, 

                     Bitmap.Config.ARGB_8888); 

             mCanvas.setBitmap(mBottomBitmap); 

         } 

         postInvalidate(); 

     } 


     /** 

      * 

      */ 

     private void undo() { 

         int nSize = mSavePath.size(); 

         if (nSize >= 1) { 

             mDeletePath.add(0, mSavePath.get(nSize - 1)); 

             mSavePath.remove(nSize - 1); 

         } else 

             return; 


         if (mBitmap != null) { 

             int width = mBitmap.getWidth(); 

             int height = mBitmap.getHeight(); 

             mBottomBitmap = Bitmap.createBitmap(width, height, 

                     Bitmap.Config.ARGB_8888); 

             mCanvas.setBitmap(mBottomBitmap); 

             mCanvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); 

         } else { 

             int width = mCanvas.getWidth(); 

             int height = mCanvas.getHeight(); 

             mBottomBitmap = Bitmap.createBitmap(width, height, 

                     Bitmap.Config.ARGB_8888); 

             mCanvas.setBitmap(mBottomBitmap); 

         } 

         /** 

          * 迭代器(Iterator) 

          * 

          *   迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象 

          * ,因为创建它的代价小。 

          * 

          *   Java中的Iterator功能比较简单,并且只能单向移动: 

          * 

          *   (1) 

          * 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素 

          * 。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。 

          * 

          *   (2) 使用next()获得序列中的下一个元素。 

          * 

          *   (3) 使用hasNext()检查序列中是否还有元素。 

          * 

          *   (4) 使用remove()将迭代器新返回的元素删除。 

          * 

          *   Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List, 

          * 也可以从List中插入和删除元素。 

          */ 

         Iterator<DrawPath> iter = mSavePath.iterator(); 

         DrawPath temp; 

         /** 

          * hasNext() 如果仍有元素可以迭代,则返回 true。 返回迭代的下一个元素。并把迭代输出的结果强制转换成Car对象 

          * hasNext()是判断是否有下一个元素 next() 得到下一个元素 iter.hasNext():判断集合中是否有下一个car 

          * iter.next():返回集合中的下一个car 

          * */ 

         while (iter.hasNext()) { 

             temp = iter.next(); 

             mCanvas.drawPath(temp.path, temp.paint); 

         } 

         postInvalidate(); 

     } 


     private void redo() { 

         int nSeize = mDeletePath.size(); 

         if (nSeize >= 1) { 

             mSavePath.add(mDeletePath.get(0)); 

             mDeletePath.remove(0); 


         } else { 

             return; 

         } 

         if (mBitmap != null) { 

             int width = mBitmap.getWidth(); 

             int height = mBitmap.getHeight(); 

             mBottomBitmap = Bitmap.createBitmap(width, height, 

                     Bitmap.Config.ARGB_8888); 

             mCanvas.setBitmap(mBottomBitmap); 

             mCanvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); 

         } else { 

             int width = mCanvas.getWidth(); 

             int height = mCanvas.getHeight(); 

             mBottomBitmap = Bitmap.createBitmap(width, height, 

                     Bitmap.Config.ARGB_8888); 

             mCanvas.setBitmap(mBottomBitmap); 

         } 

         Iterator<DrawPath> iter = mSavePath.iterator(); 

         DrawPath temp; 

         while (iter.hasNext()) { 

             temp = iter.next(); 

             mCanvas.drawPath(temp.path, temp.paint); 

         } 

         postInvalidate(); 

     } 

 }