android塗鴉實現
類似米聊、微信上的塗鴉和手寫文字功能
實現原理是自定義View,通過手勢識別獲取軌跡,然後通過畫筆畫圖
這裏添加了手勢記錄功能,並不難理解
代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
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.SQUARE); //
形狀
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() {
mBitmap
= Bitmap.createBitmap(screenWidth, screenHeight,
Bitmap.Config.ARGB_8888);
mCanvas.setBitmap(mBitmap); //
重新設置畫布,相當於清空畫布
//
清空畫布,但是如果圖片有背景的話,則使用上麵的重新初始化的方法,用該方法會將背景清空掉…
if
(savePath != null
&& savePath.size() > 0) {
//
移除最後一個path,相當於出棧操作
savePath.remove(savePath.size()
– 1);
Iterator<DrawPath>
iter = savePath.iterator();
while
(iter.hasNext()) {
DrawPath
drawPath = iter.next();
mCanvas.drawPath(drawPath.path,
drawPath.paint);
}
invalidate(); //
刷新
/*在這裏保存圖片純粹是為了方便,保存圖片進行驗證*/
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();
}
}
}
/**
*
重做的核心思想就是將撤銷的路徑保存到另外一個集合裏麵(棧),
*
然後從redo的集合裏麵取出最頂端對象,
*
畫在畫布上麵即可。
*/
public
void redo(){
//如果撤銷你懂了的話,那就試試重做吧。
}
@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
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 ;
}
return
super .onKeyDown(keyCode,
event);
}
}
|
最後更新:2017-04-03 12:53:36