android涂鸦绘图功能部分实现


要看源码猛搓这里

/**  

 * @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();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值