最近需要实现一个手写签名控件,在搜索多轮后,终于实现了效果,但是一个手写签名,涉及到了自定义View,画板,path等知识发现自己还是有挺多地方不清楚的,下面从零开始实现一个自定义画板,也一个个解释用到的知识点,算是对手写签名用到所需知识的一个总结。
手写顾名思义是在屏幕中获取若干不连续的点,然后把这些点连成一条线。
public class PaintView extends View {
private Paint mPaint;
private Canvas cacheCanvas;
private Path mPath;
private Bitmap mCachebBitmap;
public PaintView(Context context) {
super(context);
}
public PaintView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(4.0f);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLACK);
mPath = new Path();
}
}
Paint
我们知道在画板上画画,需要一直画笔,Paint 类你可以想象它就是一支画笔,通过Paint ,你可以设置画笔的大小,颜色等等。
Path
Path 封装了直线,二次曲线和三次曲线组成的复合路径,在手写签名中,我们每在屏幕中移动,就会产生一个路径,我们需要把这个路径通过Path记录下来,在配合Canvas .drawPath(Path path,Paint paint)绘制在画板上。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mCachebBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
cacheCanvas = new Canvas(mCachebBitmap); //canvas绘制的内容,将会在这个mCachebBitmap内
}
在onMeasure中,等控件初始化完成,可以获取宽高时,初始化缓存的Bitmap和画板。
下面附上完整的代码
public class PaintView extends View {
AttributeSet attrs;
private Paint mPaint;
private Canvas cacheCanvas;
private Path mPath;
private Bitmap mCachebBitmap;
public PaintView(Context context) {
super(context);
}
public PaintView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(4.0f);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLACK);
mPath = new Path();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mCachebBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
//canvas绘制的内容,将会在这个mBufferBitmap内
cacheCanvas = new Canvas(mCachebBitmap);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mCachebBitmap, 0, 0, null);
}
private float mLastTouchX, mLastTouchY;
@Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mPath.moveTo(eventX, eventY);
mLastTouchX = eventX;
mLastTouchY = eventY;
break;
}
case MotionEvent.ACTION_MOVE:
mPath.quadTo(mLastTouchX, mLastTouchY, eventX, eventY);
//在缓存里面绘制
cacheCanvas.drawPath(mPath, mPaint);
//重新绘制,会调用onDraw方法
invalidate();
mLastTouchX = eventX;
mLastTouchY = eventY;
break;
case MotionEvent.ACTION_UP: {
mPath.reset();
break;
}
}
return true;
}
}
现在一个最简单手写画板就实现了
可以发现实现手写的重点在于onTouchEvent方法中,那么我们来看看该方法。
当我们手指落下屏幕时,触发onTouchEvent的MotionEvent.ACTION_DOWN回调,调用了mPath.moveTo(eventX, eventY),这里可以把path的起始坐标移动到eventX,eventY,即我们手指落下的位置。
当我们手指在屏幕中移动时,触发MotionEvent.ACTION_MOVE,调用了mPath.quadTo(mLastTouchX, mLastTouchY, eventX, eventY); 把我们手指落下屏幕的坐标mLastTouchX,mLastTouchY作为起点,手指移动的终点坐标eventX,eventY,用来绘制一个曲线。
cacheCanvas.drawPath(mPath, mPaint);
后需要把这个路径存储起来,并调用invalidate();方法,这时会回调
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mCachebBitmap, 0, 0, null);
}
这时才真正的把手写线画到了界面上。
当然后续还有刷新优化,导出图片,擦除画笔等内容,这些我们后边继续。