java android paint bezier handwriting

java - canvas signature js



Android: firma digital utilizando Bezier (4)

Cambiar el método de dibujo en la clase Bezier para:

public void draw(Canvas canvas, Paint paint, float startWidth, float endWidth) { float originalWidth = paint.getStrokeWidth(); float widthDelta = endWidth - startWidth; for (int i = 0; i < drawSteps; i++) { float t = ((float) i) / drawSteps; float tt = t * t; float ttt = tt * t; float u = 1 - t; float uu = u * u; float uuu = uu * u; float x = uuu * startPoint.x; x += 3 * uu * t * getControlPointOne().x; x += 3 * u * tt * getControlPointTwo().x; x += ttt * endPoint.x; float y = uuu * startPoint.y; y += 3 * uu * t * getControlPointOne().y; y += 3 * u * tt * getControlPointTwo().y; y += ttt * endPoint.y; paint.setStrokeWidth(startWidth + ttt * widthDelta); canvas.drawPoint(x, y, paint); } paint.setStrokeWidth(originalWidth); }


Estoy probando una firma digital de dos sorteos utilizando Bezier como se muestra en la imagen de arriba. Cuando toco e intento dibujar una línea, el resultado es una línea de puntos pero no una línea continua. Firma simple hecha usando firma simple pero quiero crear más una curva suave usando Bezier con presión táctil. intentado con este enlace

SignatureViewDemo.java

public class SignatureViewDemo extends View { private int color = Color.BLACK; private Bitmap m_Bitmap; private final Paint m_BorderPaint; private Canvas m_Canvas; private Point m_CropBotRight; private Point m_CropTopLeft; private float m_CurrentX; private float m_CurrentY; private final float m_DesiredDash; private float m_LastWidth = 6.5F; private Paint m_PenPaint; private int m_PointIndex = 0; private ArrayList<Point> m_Points = new ArrayList<Point>(); private final float m_StrokeWidth; boolean m_Empty; public SignatureViewDemo(Context paramContext) { this(paramContext, null); } public SignatureViewDemo(Context paramContext, AttributeSet paramAttributeSet) { this(paramContext, paramAttributeSet, 0); } public SignatureViewDemo(Context paramContext, AttributeSet paramAttributeSet, int paramInt) { super(paramContext, paramAttributeSet, paramInt); setFocusable(true); this.m_PenPaint = new Paint(); this.m_PenPaint.setAntiAlias(true); this.m_PenPaint.setColor(Color.BLACK); this.m_PenPaint.setStrokeWidth(5.0F); this.m_PenPaint.setStrokeJoin(Paint.Join.ROUND); this.m_PenPaint.setStrokeCap(Paint.Cap.ROUND); this.m_CurrentY = (0.0F / 0.0F); this.m_CurrentX = (0.0F / 0.0F); this.m_StrokeWidth = 5.0F; this.m_DesiredDash = 10.0F; this.m_BorderPaint = new Paint(); this.m_BorderPaint.setColor(Color.BLACK); this.m_BorderPaint.setStyle(Paint.Style.STROKE); this.m_BorderPaint.setStrokeWidth(this.m_StrokeWidth); } public void addBezier(Bezier paramBezier, float paramFloat1, float paramFloat2) { if (this.m_Bitmap == null) { this.m_Bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); this.m_Canvas = new Canvas(this.m_Bitmap); } paramBezier.draw(this.m_Canvas, this.m_PenPaint, paramFloat1, paramFloat2); } public void addPoint(Point paramPoint) { if ((paramPoint.getX() < this.m_CropTopLeft.getX()) && (paramPoint.getX() >= 0.0F)) this.m_CropTopLeft.setX(paramPoint.getX()); if ((paramPoint.getY() < this.m_CropTopLeft.getY()) && (paramPoint.getY() >= 0.0F)) this.m_CropTopLeft.setY(paramPoint.getY()); if ((paramPoint.getX() > this.m_CropBotRight.getX()) && (paramPoint.getX() <= this.m_Canvas.getWidth())) this.m_CropBotRight.setX(paramPoint.getX()); if ((paramPoint.getY() > this.m_CropBotRight.getY()) && (paramPoint.getY() <= this.m_Canvas.getHeight())) this.m_CropBotRight.setY(paramPoint.getY()); this.m_Points.add(paramPoint); drawPoints(); } // public void clear() { // if (this.m_Canvas == null) // this.m_Canvas.drawColor(0, PorterDuff.Mode.CLEAR); // invalidate(); // // return; // } public void clear() { if (this.m_Canvas == null) return; while (m_Empty) { m_Canvas.drawColor(0, PorterDuff.Mode.CLEAR); m_Empty = true; invalidate(); return; } } public void drawBitmap(Bitmap paramBitmap) { clear(); if ((paramBitmap != null) && (this.m_Canvas != null) && (this.m_Canvas.getWidth() != 0) && (this.m_Canvas.getHeight() != 0)) { Matrix localMatrix = new Matrix(); localMatrix.setRectToRect( new RectF(0.0F, 0.0F, paramBitmap.getWidth(), paramBitmap .getHeight()), new RectF(0.0F, 0.0F, this.m_Canvas.getWidth(), this.m_Canvas.getHeight()), Matrix.ScaleToFit.CENTER); this.m_Canvas.drawBitmap(paramBitmap, localMatrix, null); m_Empty = false; } invalidate(); } public void drawPoints() { if ((m_Points.size() >= 4) && (4 + this.m_PointIndex <= this.m_Points.size())) { Point localPoint1 = (Point) this.m_Points.get(this.m_PointIndex); Point localPoint2 = (Point) this.m_Points .get(1 + this.m_PointIndex); Point localPoint3 = (Point) this.m_Points .get(2 + this.m_PointIndex); Point localPoint4 = (Point) this.m_Points .get(3 + this.m_PointIndex); Bezier localBezier = new Bezier(localPoint1, localPoint2, localPoint3, localPoint4); localBezier.setColor(Color.GREEN); float f = strokeWidth(8.0F / localPoint4.velocityFrom(localPoint1)); addBezier(localBezier, this.m_LastWidth, f); invalidate(); this.m_LastWidth = f; this.m_PointIndex = (3 + this.m_PointIndex); m_Empty = false; } } public boolean isEmpty() { return m_Empty; } public Bitmap getBitmap() { return this.m_Bitmap; } public int getColor() { return this.color; } protected void onDraw(Canvas paramCanvas) { if (this.m_Bitmap != null) paramCanvas.drawBitmap(this.m_Bitmap, 0.0F, 0.0F, null); } protected void onMeasure(int paramInt1, int paramInt2) { int i = View.MeasureSpec.getSize(paramInt1); int j = View.MeasureSpec.getSize(paramInt2); this.m_CropTopLeft = new Point(i, j); this.m_CropBotRight = new Point(0.0F, 0.0F); setMeasuredDimension(i, j); } protected void onSizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) { Bitmap localBitmap = Bitmap.createBitmap(paramInt1, paramInt2, Bitmap.Config.ARGB_8888); this.m_Canvas = new Canvas(localBitmap); float f1 = 2.0F * (this.m_Canvas.getWidth() + this.m_Canvas.getHeight() - 2.0F * this.m_StrokeWidth); float f2 = f1 * this.m_DesiredDash / (Math.round(f1 / (4.0F * this.m_DesiredDash)) * (4.0F * this.m_DesiredDash)); Paint localPaint = this.m_BorderPaint; float[] arrayOfFloat = new float[2]; arrayOfFloat[0] = f2; arrayOfFloat[1] = f2; localPaint.setPathEffect(new DashPathEffect(arrayOfFloat, f2 / 2.0F)); clear(); if (this.m_Bitmap != null) { Rect localRect = new Rect(0, 0, this.m_Canvas.getWidth(), this.m_Canvas.getHeight()); this.m_Canvas.drawBitmap(this.m_Bitmap, null, localRect, null); m_Empty = false; } this.m_Bitmap = localBitmap; } public boolean onTouchEvent(MotionEvent paramMotionEvent) { int i = 0xFF & paramMotionEvent.getAction(); if (i == 0) { this.m_CurrentX = paramMotionEvent.getX(); this.m_CurrentY = paramMotionEvent.getY(); addPoint(new Point(this.m_CurrentX, this.m_CurrentY, paramMotionEvent.getEventTime())); getParent().requestDisallowInterceptTouchEvent(true); } // while (m_Empty) { if ((i == 1) || (i == 3)) { this.m_CurrentY = (0.0F / 0.0F); this.m_CurrentX = (0.0F / 0.0F); this.m_Points.clear(); this.m_PointIndex = 0; getParent().requestDisallowInterceptTouchEvent(false); } // if ((this.m_Points.size() < 4) || (4 + this.m_PointIndex > // this.m_Points.size())) // while (1 + this.m_PointIndex <= this.m_Points.size()) drawPoints(); if ((i == 2) || (i == 1)) { for (int j = 0; j < paramMotionEvent.getHistorySize(); j++) addPoint(new Point(paramMotionEvent.getHistoricalX(j), paramMotionEvent.getHistoricalY(j), paramMotionEvent.getHistoricalEventTime(j))); addPoint(new Point(paramMotionEvent.getX(), paramMotionEvent.getY(), paramMotionEvent.getEventTime())); } // } return true; } public void setColor(int paramInt) { this.color = Color.BLACK; } public Point getCropBotRight() { return this.m_CropBotRight; } public Point getCropTopLeft() { return this.m_CropTopLeft; } // public float strokeWidth(float paramFloat) { // if (paramFloat > 11.0F) // paramFloat = 10.0F; // if (paramFloat < 5.0F) // paramFloat = 6.0F; // return paramFloat; // } public float strokeWidth(float paramFloat) { if (paramFloat > 11.0F) paramFloat = 10.0F; while (m_Empty) { if (paramFloat < 5.0F) paramFloat = 6.0F; return paramFloat; } return paramFloat; } }

Punto.java

private long time; private float x; private float y; public Point(float paramFloat1, float paramFloat2) { this.x = paramFloat1; this.y = paramFloat2; } public Point(float paramFloat1, float paramFloat2, long paramLong) { this.x = paramFloat1; this.y = paramFloat2; this.time = paramLong; } protected float distanceTo(Point paramPoint) { float f1 = this.x - paramPoint.getX(); float f2 = this.y - paramPoint.getY(); return FloatMath.sqrt(f1 * f1 + f2 * f2); } public long getTime() { return this.time; } public float getX() { return this.x; } public float getY() { return this.y; } public void setX(float paramFloat) { this.x = paramFloat; } public void setY(float paramFloat) { this.y = paramFloat; } public float velocityFrom(Point paramPoint) { return distanceTo(paramPoint) / (this.time - paramPoint.time); } }

Bezier.java

public class Bezier { private Point controlPointOne; private Point controlPointTwo; private int drawSteps; private Point endPoint; private int mColor; private Point startPoint; public Bezier() { } public Bezier(Point paramPoint1, Point paramPoint2, Point paramPoint3, Point paramPoint4) { this.startPoint = paramPoint1; this.controlPointOne = paramPoint2; this.controlPointTwo = paramPoint3; this.endPoint = paramPoint4; this.drawSteps = ((int) (paramPoint1.distanceTo(paramPoint2) + paramPoint2.distanceTo(paramPoint3) + paramPoint3 .distanceTo(paramPoint4))); } public void draw(Canvas paramCanvas, Paint paramPaint, float paramFloat1, float paramFloat2) { float f1 = paramFloat2 - paramFloat1; for (int i = 0; i < this.drawSteps; i++) { float f2 = i / this.drawSteps; float f3 = f2 * f2; float f4 = f3 * f2; float f5 = 1.0F - f2; float f6 = f5 * f5; float f7 = f6 * f5; float f8 = f7 * this.startPoint.getX() + f2 * (3.0F * f6) * this.controlPointOne.getX() + f3 * (3.0F * f5) * this.controlPointTwo.getX() + f4 * this.endPoint.getX(); float f9 = f7 * this.startPoint.getY() + f2 * (3.0F * f6) * this.controlPointOne.getY() + f3 * (3.0F * f5) * this.controlPointTwo.getY() + f4 * this.endPoint.getY(); paramPaint.setColor(getColor()); paramPaint.setStrokeWidth(paramFloat1 + f4 * f1); paramCanvas.drawPoint(f8, f9, paramPaint); } } public int getColor() { return this.mColor; } public Point getControlPointOne() { return this.controlPointOne; } public Point getControlPointTwo() { return this.controlPointTwo; } public int getDrawSteps() { return this.drawSteps; } public Point getEndPoint() { return this.endPoint; } public Point getStartPoint() { return this.startPoint; } public void setColor(int paramInt) { this.mColor = Color.BLACK; } public void setControlPointOne(Point paramPoint) { this.controlPointOne = paramPoint; } public void setControlPointTwo(Point paramPoint) { this.controlPointTwo = paramPoint; } public void setDrawSteps(int paramInt) { this.drawSteps = paramInt; } public void setEndPoint(Point paramPoint) { this.endPoint = paramPoint; } public void setStartPoint(Point paramPoint) { this.startPoint = paramPoint; }

}


Debe ver cuántos puntos se dibujan en la clase Bezier y sumarlos.

De su código: está acumulando 4 puntos y dibuja una curva Bezier sobre ellos. Esto significa que el algoritmo, por diseño, perderá la mitad de los puntos muestreados.

Otras opciones: 1. Cree su propia curva Bezier o un orden inferior (por ejemplo, orden 3), y luego perderá solo 1/3 de los puntos. Esto hace que el problema sea inútil, ya que desea conservar menos datos que los muestreados. 2. Cree una curva Bezier de orden superior que pueda aproximarse a toda la firma.

Sin embargo, puede tratar de averiguar qué puntos desea seleccionar para obtener una curva Bezier que esté más cerca de la firma: seleccione la parte superior de la letra ''l'' como punto final de una curva Bezier y el comienzo De otro dará mejores resultados.


Puedes usar esto, también he adjuntado el xml en la parte inferior:

import java.io.File; import java.io.FileOutputStream; import java.util.Calendar; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.ContextWrapper; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore.Images; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.Toast; public class SignatureCapture extends Activity { public LinearLayout mContent; public signature mSignature; public Button mClear, mGetSign, mCancel; public static String tempDir; public int count = 1; public String current = null; public static Bitmap mBitmap; public View mView; public File mypath; private String uniqueId; private EditText yourName; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.signature); // tempDir = Environment.getExternalStorageDirectory() + "/" + // getResources().getString(R.string.external_dir) + "/"; ContextWrapper cw = new ContextWrapper(getApplicationContext()); // File directory = // cw.getDir(getResources().getString(R.string.external_dir), // Context.MODE_PRIVATE); File directory = new File(Environment.getExternalStorageDirectory() + "/GLP_Images"); if (!directory.exists()) directory.mkdir(); // directory is created; // prepareDirectory(); uniqueId = getTodaysDate() + "_" + getCurrentTime(); current = uniqueId + ".png"; mypath = new File(directory, current); mContent = (LinearLayout) findViewById(R.id.linearLayout); mSignature = new signature(this, null); mSignature.setBackgroundColor(Color.WHITE); mContent.addView(mSignature, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); mClear = (Button) findViewById(R.id.clear); mGetSign = (Button) findViewById(R.id.getsign); mGetSign.setEnabled(false); mCancel = (Button) findViewById(R.id.cancel); mView = mContent; // yourName = (EditText) findViewById(R.id.yourName); mClear.setOnClickListener(new OnClickListener() { public void onClick(View v) { Log.v("log_tag", "Panel Cleared"); mSignature.clear(); mGetSign.setEnabled(false); } }); mGetSign.setOnClickListener(new OnClickListener() { public void onClick(View v) { Log.v("log_tag", "Panel Saved"); // boolean error = captureSignature(); // if(error){ mView.setDrawingCacheEnabled(true); mSignature.save(mView); // Bundle b = new Bundle(); // b.putString("status", "done"); /* * Intent intent = new * Intent(SignatureCapture.this,SigntuareCaptureActivity.class); * // intent.putExtra("imagePath",mypath); * startActivity(intent); //intent.putExtra("status", "done"); * //setResult(RESULT_OK,intent); finish(); // } */ AlertDialog.Builder dialog = new AlertDialog.Builder( SignatureCapture.this); dialog.setMessage("Details saved"); dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); dialog.show(); } }); mCancel.setOnClickListener(new OnClickListener() { public void onClick(View v) { Log.v("log_tag", "Panel Canceled"); Bundle b = new Bundle(); b.putString("status", "cancel"); Intent intent = new Intent(); intent.putExtras(b); setResult(RESULT_OK, intent); finish(); } }); } @Override protected void onDestroy() { Log.w("GetSignature", "onDestory"); super.onDestroy(); } // private boolean captureSignature() // { // // boolean error = false; // String errorMessage = ""; // // // if(yourName.getText().toString().equalsIgnoreCase("")) // { // errorMessage = errorMessage + "Please enter your Name/n"; // error = true; // } // // if(error) // { // Toast toast = Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT); // toast.setGravity(Gravity.TOP, 105, 50); // toast.show(); // } // // return error; // } private String getTodaysDate() { final Calendar c = Calendar.getInstance(); int todaysDate = (c.get(Calendar.YEAR) * 10000) + ((c.get(Calendar.MONTH) + 1) * 100) + (c.get(Calendar.DAY_OF_MONTH)); Log.w("DATE:", String.valueOf(todaysDate)); return (String.valueOf(todaysDate)); } private String getCurrentTime() { final Calendar c = Calendar.getInstance(); int currentTime = (c.get(Calendar.HOUR_OF_DAY) * 10000) + (c.get(Calendar.MINUTE) * 100) + (c.get(Calendar.SECOND)); Log.w("TIME:", String.valueOf(currentTime)); return (String.valueOf(currentTime)); } private boolean prepareDirectory() { try { if (makedirs()) { return true; } else { return false; } } catch (Exception e) { e.printStackTrace(); Toast.makeText( this, "Could not initiate File System.. Is Sdcard mounted properly?", 1000).show(); return false; } } private boolean makedirs() { File tempdir = new File(tempDir); if (!tempdir.exists()) tempdir.mkdirs(); if (tempdir.isDirectory()) { File[] files = tempdir.listFiles(); for (File file : files) { if (!file.delete()) { System.out.println("Failed to delete " + file); } } } return (tempdir.isDirectory()); } public class signature extends View { private static final float STROKE_WIDTH = 5f; 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 signature(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 save(View v) { Log.v("log_tag", "Width: " + v.getWidth()); Log.v("log_tag", "Height: " + v.getHeight()); if (mBitmap == null) { mBitmap = Bitmap.createBitmap(mContent.getWidth(), mContent.getHeight(), Bitmap.Config.RGB_565); } Canvas canvas = new Canvas(mBitmap); try { FileOutputStream mFileOutStream = new FileOutputStream(mypath); v.draw(canvas); mBitmap.compress(Bitmap.CompressFormat.PNG, 90, mFileOutStream); mFileOutStream.flush(); mFileOutStream.close(); String url = Images.Media.insertImage(getContentResolver(), mBitmap, "title", null); // Log.v("log_tag","url: " + url); // //In case you want to delete the file // boolean deleted = mypath.delete(); // Log.v("log_tag","deleted: " + mypath.toString() + deleted); // //If you want to convert the image to string use base64 // converter } catch (Exception e) { Log.v("log_tag", e.toString()); } } 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(); mGetSign.setEnabled(true); 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: debug("Ignored touch event: " + event.toString()); 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 debug(String string) { } 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); } } }

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/linearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/header_bg" android:gravity="center" android:text="Signature" android:textColor="@android:color/white" android:textSize="20dp" android:textStyle="bold" /> <LinearLayout android:id="@+id/linearLayout" android:layout_width="match_parent" android:layout_weight="1" android:layout_height="wrap_content" android:orientation="vertical" /> <LinearLayout android:id="@+id/linearLayout2" android:layout_width="match_parent" android:layout_height="wrap_content" > <Button android:id="@+id/cancel" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight=".30" android:text="Cancel" android:visibility="gone" /> <Button android:id="@+id/clear" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Clear" /> <Button android:id="@+id/getsign" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Save" /> </LinearLayout> </LinearLayout>


Puedes usar simple

código

public class MySignatureView extends View { private Path mPath; private Paint mPaint; private Bitmap mBitmap; private Canvas mCanvas; private int bgColor; private float curX, curY; private boolean isDragged = false; private static final int TOUCH_TOLERANCE = 4; private static final int STROKE_WIDTH = 2; public MySignatureView(Context context) { super(context); init(); } public MySignatureView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public MySignatureView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { setFocusable(true); bgColor = Color.WHITE; mPath = new Path(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(bgColor ^ 0x00FFFFFF); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(STROKE_WIDTH); } public void setSigColor(int color) { mPaint.setColor(color); } public void setSigColor(int a, int r, int g, int b) { mPaint.setARGB(a, r, g, b); } public void clearSig() { if (mCanvas != null) { mCanvas.drawColor(bgColor); mCanvas.drawPaint(mPaint); mPath.reset(); invalidate(); } } public Bitmap getImage() { return this.mBitmap; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { int bitW = mBitmap != null ? mBitmap.getWidth() : 0; int bitH = mBitmap != null ? mBitmap.getWidth() : 0; if (bitW >= w && bitH >= h) { return; } if (bitW < w) bitW = w; if (bitH < h) bitH = h; Bitmap newBitmap = Bitmap.createBitmap(bitW, bitH, Bitmap.Config.ARGB_8888); Canvas newCanvas = new Canvas(); newCanvas.setBitmap(newBitmap); if (mBitmap != null) { newCanvas.drawBitmap(mBitmap, 0, 0, null); } mBitmap = newBitmap; mCanvas = newCanvas; } @Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(mBitmap, 0, 0, mPaint); canvas.drawPath(mPath, mPaint); } @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; } invalidate(); return true; } private void touchDown(float x, float y) { mPath.reset(); mPath.moveTo(x, y); curX = x; curY = y; isDragged = false; } private void touchMove(float x, float y) { float dx = Math.abs(x - curX); float dy = Math.abs(y - curY); if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { mPath.quadTo(curX, curY, (x + curX) / 2, (y + curY) / 2); curX = x; curY = y; isDragged = true; } } private void touchUp() { if (isDragged) { mPath.lineTo(curX, curY); } else { mPath.lineTo(curX+2, curY+2); } mCanvas.drawPath(mPath, mPaint); mPath.reset(); } }