# Android手绘效果实现

### 代码实现

//获取离指定点最近的一个未绘制过的点
private Point getNearestPoint(Point p) {
if (p == null) return null;
//
int beginX = (p.x - add) >= 0 ? (p.x - add) : 0;
int endX = (p.x + add) < mSrcBmWidth ? (p.x + add) : mSrcBmWidth - 1;
int beginY = (p.y - add) >= 0 ? (p.y - add) : 0;
int endY = (p.y + add) < mSrcBmHeight ? (p.y + add) : mSrcBmHeight - 1;
//搜索正方形的上下边
for (int x = beginX; x <= endX; x++) {
if (mArray[x][beginY]) {
//标记当前点已经访问过
mArray[x][beginY] = false;
return new Point(x, beginY);
}
if (mArray[x][endY]) {
//标记当前点已经访问过
mArray[x][endY] = false;
return new Point(x, endY);
}
}
//搜索正方形的左右边
for (int y = beginY + 1; y <= endY - 1; y++) {
if (mArray[beginX][y]) {
//标记当前点已经访问过
mArray[beginX][beginY] = false;
return new Point(beginX, beginY);
}
if (mArray[endX][y]) {
//标记当前点已经访问过
mArray[endX][y] = false;
return new Point(endX, y);
}
}
}

return null;
}

   private Point mLastPoint = new Point(0, 0);
//获取下一个需要绘制的点
private Point getNextPoint() {
mLastPoint = getNearestPoint(mLastPoint);
return mLastPoint;
}

/**
* //绘制
* return :false 表示绘制完成，true表示还需要继续绘制
*/
private boolean draw() {

mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLACK);
//获取count个点后，一次性绘制到bitmap在把bitmap绘制到SurfaceView
int count = 100;
Point p = null;
while (count-- > 0) {
p = getNextPoint();
if (p == null) {//如果p为空，说明所有的点已经绘制完成
return false;
}
mTmpCanvas.drawPoint(p.x, p.y + offsetY, mPaint);
}
//将bitmap绘制到SurfaceView中
Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawBitmap(mTmpBm, 0, 0, mPaint);
if (p != null)
canvas.drawBitmap(mPaintBm, p.x, p.y - mPaintBm.getHeight() + offsetY, mPaint);
mSurfaceHolder.unlockCanvasAndPost(canvas);
return true;
}

package com.hc.myoutline;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
* Package com.hc.myoutline
* Created by HuaChao on 2016/5/27.
*/
public class DrawOutlineView extends SurfaceView implements SurfaceHolder.Callback {

private SurfaceHolder mSurfaceHolder;
private Bitmap mTmpBm;
private Canvas mTmpCanvas;
private int mWidth;
private int mHeight;
private Paint mPaint;
private int mSrcBmWidth;
private int mSrcBmHeight;
private boolean[][] mArray;
private int offsetY = 100;

private Bitmap mPaintBm;
private Point mLastPoint = new Point(0, 0);

public DrawOutlineView(Context context) {
super(context);
init();
}

public DrawOutlineView(Context context, AttributeSet attrs) {
super(context, attrs);

init();
}

private void init() {
mSurfaceHolder = getHolder();
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
}

//设置画笔图片
public void setPaintBm(Bitmap paintBm) {
mPaintBm = paintBm;
}

//获取离指定点最近的一个未绘制过的点
private Point getNearestPoint(Point p) {
if (p == null) return null;
//
int beginX = (p.x - add) >= 0 ? (p.x - add) : 0;
int endX = (p.x + add) < mSrcBmWidth ? (p.x + add) : mSrcBmWidth - 1;
int beginY = (p.y - add) >= 0 ? (p.y - add) : 0;
int endY = (p.y + add) < mSrcBmHeight ? (p.y + add) : mSrcBmHeight - 1;
//搜索正方形的上下边
for (int x = beginX; x <= endX; x++) {
if (mArray[x][beginY]) {
mArray[x][beginY] = false;
return new Point(x, beginY);
}
if (mArray[x][endY]) {
mArray[x][endY] = false;
return new Point(x, endY);
}
}
//搜索正方形的左右边
for (int y = beginY + 1; y <= endY - 1; y++) {
if (mArray[beginX][y]) {
mArray[beginX][beginY] = false;
return new Point(beginX, beginY);
}
if (mArray[endX][y]) {

mArray[endX][y] = false;
return new Point(endX, y);
}
}
}

return null;
}

//获取下一个需要绘制的点
private Point getNextPoint() {
mLastPoint = getNearestPoint(mLastPoint);
return mLastPoint;
}

/**
* //绘制
* return :false 表示绘制完成，true表示还需要继续绘制
*/
private boolean draw() {

mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLACK);
//获取count个点后，一次性绘制到bitmap在把bitmap绘制到SurfaceView
int count = 100;
Point p = null;
while (count-- > 0) {
p = getNextPoint();
if (p == null) {//如果p为空，说明所有的点已经绘制完成
return false;
}
mTmpCanvas.drawPoint(p.x, p.y + offsetY, mPaint);
}
//将bitmap绘制到SurfaceView中
Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawBitmap(mTmpBm, 0, 0, mPaint);
if (p != null)
canvas.drawBitmap(mPaintBm, p.x, p.y - mPaintBm.getHeight() + offsetY, mPaint);
mSurfaceHolder.unlockCanvasAndPost(canvas);
return true;
}
//重画
public void reDraw(boolean[][] array) {
if (isDrawing) return;
mTmpBm = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
mTmpCanvas = new Canvas(mTmpBm);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
mTmpCanvas.drawRect(0, 0, mWidth, mHeight, mPaint);
mLastPoint = new Point(0, 0);
beginDraw(array);
}

private boolean isDrawing = false;

public void beginDraw(boolean[][] array) {
if (isDrawing) return;
this.mArray = array;
mSrcBmWidth = array.length;
mSrcBmHeight = array[0].length;

@Override
public void run() {
while (true) {
isDrawing = true;
boolean rs = draw();
if (!rs) break;
try {
sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isDrawing = false;
}
}.start();
}

@Override
public void surfaceCreated(SurfaceHolder holder) {

}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
this.mWidth = width;
this.mHeight = height;
mTmpBm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mTmpCanvas = new Canvas(mTmpBm);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
mTmpCanvas.drawRect(0, 0, mWidth, mHeight, mPaint);
Canvas canvas = holder.lockCanvas();
canvas.drawBitmap(mTmpBm, 0, 0, mPaint);
holder.unlockCanvasAndPost(canvas);

mPaint.setStyle(Paint.Style.STROKE);
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}
}

package com.hc.myoutline;

import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;

public class MainActivity extends AppCompatActivity {
private DrawOutlineView drawOutlineView;
private Bitmap sobelBm;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
//将Bitmap压缩处理，防止OOM
Bitmap bm = CommenUtils.getRatioBitmap(this, R.drawable.test, 100, 100);
//返回的是处理过的Bitmap
sobelBm = SobelUtils.Sobel(bm);
drawOutlineView = (DrawOutlineView) findViewById(R.id.outline);

Bitmap paintBm = CommenUtils.getRatioBitmap(this, R.drawable.paint, 10, 20);
drawOutlineView.setPaintBm(paintBm);

}
//根据Bitmap信息，获取每个位置的像素点是否需要绘制
//使用boolean数组而不是int[][]主要是考虑到内存的消耗
private boolean[][] getArray(Bitmap bitmap) {
boolean[][] b = new boolean[bitmap.getWidth()][bitmap.getHeight()];

for (int i = 0; i < bitmap.getWidth(); i++) {
for (int j = 0; j < bitmap.getHeight(); j++) {
if (bitmap.getPixel(i, j) != Color.WHITE)
b[i][j] = true;
else
b[i][j] = false;
}
}
return b;
}

boolean first = true;

//点击时开始绘制
@Override
public boolean onTouchEvent(MotionEvent event) {
if (first) {
first = false;
drawOutlineView.beginDraw(getArray(sobelBm));
} else
drawOutlineView.reDraw(getArray(sobelBm));
return true;
}
}

### 参考链接

Android自动手绘，圆你儿时画家梦！ - huachao1001的专栏 - 博客频道 - CSDN.NET

#### Android手绘手写图DrawableView

2015-11-22 13:34:42

#### android实现手绘签名

2017年10月27日 26.28MB 下载

#### Android API DEMO:简单手绘游戏

2016-05-26 17:21:32

#### Android View与SurfaceView的手绘板制作

2016-03-21 21:53:59

#### 自定义View进阶-手绘地图（二）

2018-05-15 11:42:03

#### 基于mappwidget的手绘地图

2017-11-02 13:36:51

#### android自定义地图手绘地图景区导航

2014年01月26日 13.01MB 下载

#### 手绘地图的简单实例（mAppWidget）

2014年12月19日 3.77MB 下载

#### 基于mAppwidget实现的手绘地图demo

2014年07月02日 13.09MB 下载

#### Android编程之手绘

2012-06-18 17:10:41