项目里有时会有那些要签名啊,或者要手绘的要求,为此就会要求有这样的控件存在,这里介绍两个方法:
一、Android 自带的 GestureOverlayView,添加于API-4,主要是用于识别手势的。
对于签名也可以完成大部分工作,但是有个问题:多笔画的时候,两个笔画之间间隔太久会自动清空前面所有笔画。我还么有找解决方法。
使用很简单:
在你的xml文件里添加这个控件就可以(一个透明层):
<android.gesture.GestureOverlayView
android:id="@+id/gov"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:gestureColor="#ffffff"
android:fadeEnabled="false"
android:gestureStrokeType="multiple"
android:gestureStrokeLengthThreshold="0.1"
android:gestureStrokeSquarenessThreshold="0.1"
android:gestureStrokeAngleThreshold="0.1">
</android.gesture.GestureOverlayView>
有几个属性简单解释一下:fadeEnable,是手势的淡出效果,在做签名的时候就不用了;gestureStrokeType,有两个值,single和multiple,单笔画和多笔画,签名用多笔画;
gestureStrokeLengthThreshold、gestureStrokeSquarenessThreshold、gestureStrokeAngleThreshold 分别是长度、方形、角度的最小识别值,简单说就是对手势的识别精细度。在签名的要求中可能不是很重要。
然后可以把这个签名(手势)保存成Bitmap:
public Bitmap saveSignature() {
GestureOverlayView gestureOverlayView = (GestureOverlayView)findViewById(R.id.gov);
gestureOverlayView.setDrawingCacheEnabled(true);
return Bitmap.createBitmap(gestureOverlayView.getDrawingCache());
}
二、如果要求要可以多笔画间隔无限时,手动控制清空的情况,那么可以用自定义控件实现:
public class DrawBoard extends View {
private static final float STROKE_WIDTH = 5f; // 笔画的宽度
/*
* 笔画的一半宽度,用于在获取签名(手势)的矩形区域的时候将所有的笔画都框进去。
* 因为左上角和右下角的点是在笔画的那个点的中心的,所以左上角(x,y)->(x-w/2, y-w/2), 右下角(x,y)->(x+w/2, y+w/2)
*/
private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;
private Paint paint = new Paint();
private Path path = new Path();
private float lastTouchX;
private float lastTouchY;
private final RectF dirtyRect = new RectF(); // 用于框出签名(手势)的区域
public DrawBoard(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setAntiAlias(true); //反锯齿
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND); // 连接处圆润
paint.setStrokeWidth(STROKE_WIDTH);
}
public void clear() {
path.reset();
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(eventX, eventY);
lastTouchX = eventX;
lastTouchY = eventY;
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
resetDirtyRect(eventX, eventY); // 和最后一个坐标值比较更新图形矩形左上角和右下角
int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
expandDirtyRect(historicalX, historicalY); // 和历史坐标值比较更新图形矩形左上角和右下角
path.lineTo(historicalX, historicalY);
}
path.lineTo(eventX, eventY);
break;
default: return false;
}
invalidate((int) (dirtyRect.left - HALF_STROKE_WIDTH),
(int) (dirtyRect.top - HALF_STROKE_WIDTH),
(int) (dirtyRect.right + HALF_STROKE_WIDTH),
(int) (dirtyRect.bottom + HALF_STROKE_WIDTH)); //刷新整个图型区域
lastTouchX = eventX;
lastTouchY = eventY;
return true;
}
private void expandDirtyRect(float historicalX, float historicalY) {
if (historicalX < dirtyRect.left) {
dirtyRect.left = historicalX;
} else if (historicalX > dirtyRect.right) {
dirtyRect.right = historicalX;
}
if (historicalY < dirtyRect.top) {
dirtyRect.top = historicalY;
} else if (historicalY > dirtyRect.bottom) {
dirtyRect.bottom = historicalY;
}
}
private void resetDirtyRect(float eventX, float eventY) {
dirtyRect.left = Math.min(lastTouchX, eventX);
dirtyRect.right = Math.max(lastTouchX, eventX);
dirtyRect.top = Math.min(lastTouchY, eventY);
dirtyRect.bottom = Math.max(lastTouchY, eventY);
}
}