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

Java代码  
import java.io.File;  
import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.util.ArrayList;  
import java.util.Iterator;  
import java.util.List;  
  
import android.content.Context;  
import android.graphics.Bitmap;  
import android.graphics.Canvas;  
import android.graphics.Paint;  
import android.graphics.Path;  
import android.graphics.Bitmap.CompressFormat;  
import android.os.Environment;  
import android.view.MotionEvent;  
import android.view.View;  
  
/** 
 * View实现涂鸦、撤销以及重做功能 
 */  
  
public class TuyaView extends View {  
  
    private Bitmap mBitmap;  
    private Canvas mCanvas;  
    private Path mPath;  
    private Paint mBitmapPaint;// 画布的画笔  
    private Paint mPaint;// 真实的画笔  
    private float mX, mY;// 临时点坐标  
    private static final float TOUCH_TOLERANCE = 4;  
      
    // 保存Path路径的集合,用List集合来模拟栈  
    private static List<DrawPath> savePath;  
    // 记录Path路径的对象  
    private DrawPath dp;  
  
    private int screenWidth, screenHeight;  
  
    private class DrawPath {  
        public Path path;// 路径  
        public Paint paint;// 画笔  
    }  
  
    public TuyaView(Context context, int w, int h) {  
        super(context);  
        screenWidth = w;  
        screenHeight = h;  
  
        mBitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888);  
        // 保存一次一次绘制出来的图形  
        mCanvas = new Canvas(mBitmap);  
  
        mBitmapPaint = new Paint(Paint.DITHER_FLAG);  
        mPaint = new Paint();  
        mPaint.setAntiAlias(true);  
        mPaint.setStyle(Paint.Style.STROKE);  
        mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘  
        mPaint.setStrokeCap(Paint.Cap.ROUND);// 形状  
        mPaint.setStrokeWidth(5);// 画笔宽度  
  
        savePath = new ArrayList<DrawPath>();  
    }  
  
    @Override  
    public void onDraw(Canvas canvas) {  
        canvas.drawColor(0xFFAAAAAA);  
        // 将前面已经画过得显示出来  
        canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);  
        if (mPath != null) {  
            // 实时的显示  
            canvas.drawPath(mPath, mPaint);  
        }  
    }  
  
    private void touch_start(float x, float y) {  
        mPath.moveTo(x, y);  
        mX = x;  
        mY = y;  
    }  
  
    private void touch_move(float x, float y) {  
        float dx = Math.abs(x - mX);  
        float dy = Math.abs(mY - y);  
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {  
            // 从x1,y1到x2,y2画一条贝塞尔曲线,更平滑(直接用mPath.lineTo也是可以的)  
            mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);  
            mX = x;  
            mY = y;  
        }  
    }  
  
    private void touch_up() {  
        mPath.lineTo(mX, mY);  
        mCanvas.drawPath(mPath, mPaint);  
        //将一条完整的路径保存下来(相当于入栈操作)  
        savePath.add(dp);  
        mPath = null;// 重新置空  
    }  
    /** 
     * 撤销的核心思想就是将画布清空, 
     * 将保存下来的Path路径最后一个移除掉, 
     * 重新将路径画在画布上面。 
     */  
    public void undo() {  
        if (savePath != null && savePath.size() > 0) {  
            savePath.remove(savePath.size() - 1);  
            redrawOnBitmap();  
        }  
    }  
    /** 
     * 重做 
     */  
    public void redo(){  
        if (savePath != null && savePath.size() > 0) {  
            savePath.clear();  
            redrawOnBitmap();  
        }  
    }  
      
    private void redrawOnBitmap(){  
        mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,  
                Bitmap.Config.ARGB_8888);  
        mCanvas.setBitmap(mBitmap);// 重新设置画布,相当于清空画布   
        Iterator<DrawPath> iter = savePath.iterator();  
        while (iter.hasNext()) {  
            DrawPath drawPath = iter.next();  
            mCanvas.drawPath(drawPath.path, drawPath.paint);  
        }  
        invalidate();// 刷新  
    }  
  
    @Override  
    public boolean onTouchEvent(MotionEvent event) {  
        float x = event.getX();  
        float y = event.getY();  
  
        switch (event.getAction()) {  
        case MotionEvent.ACTION_DOWN:  
            // 每次down下去重新new一个Path  
            mPath = new Path();  
            //每一次记录的路径对象是不一样的  
            dp = new DrawPath();  
            dp.path = mPath;  
            dp.paint = mPaint;  
            touch_start(x, y);  
            invalidate();  
            break;  
        case MotionEvent.ACTION_MOVE:  
            touch_move(x, y);  
            invalidate();  
            break;  
        case MotionEvent.ACTION_UP:  
            touch_up();  
            invalidate();  
            break;  
        }  
        return true;  
    }  
  
    public void saveToSDCard(){  
        String fileUrl = Environment.getExternalStorageDirectory()  
                .toString() + "/android/data/test.png";  
        try {  
            FileOutputStream fos = new FileOutputStream(new File(fileUrl));  
            mBitmap.compress(CompressFormat.PNG, 100, fos);  
            fos.flush();  
            fos.close();  
        } catch (FileNotFoundException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}  
Java代码  
import android.app.Activity;  
import android.os.Bundle;  
import android.util.DisplayMetrics;  
import android.util.Log;  
import android.view.KeyEvent;  
  
public class TuyaActivity extends Activity {  
  
    private TuyaView tuyaView = null;  
  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
  
        DisplayMetrics dm = new DisplayMetrics();  
        getWindowManager().getDefaultDisplay().getMetrics(dm);  
  
        tuyaView = new TuyaView(this, dm.widthPixels, dm.heightPixels);  
        setContentView(tuyaView);  
    }  
  
    @Override  
    public boolean onKeyDown(int keyCode, KeyEvent event) {  
        if (keyCode == KeyEvent.KEYCODE_BACK) {// 返回键  
            tuyaView.undo();  
            return true;  
        }else if(keyCode == KeyEvent.KEYCODE_MENU){//MENU  
            tuyaView.redo();  
            return true;  
        }  
        return super.onKeyDown(keyCode, event);  
    }  
  
}  
posted @ 2012-04-09 22:12  与时俱进  阅读(1796)  评论(1编辑  收藏  举报
友情链接:同里老宅院民居客栈