您的位置:首页 > 移动开发 > Android开发

android涂鸦绘图功能部分实现

2014-03-22 11:32 218 查看






要看源码猛搓这里

/**

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

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: