Android: digital signature using Bezier

随声附和 提交于 2019-12-02 20:35:20

Change draw method in Bezier class to:

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);
}

You need to see how many points are drawn in Bezier class and add them up.

From your code: You are collecting 4 points and draw a Bezier curve over them. This means that the algorithm - by design - will miss half of the sampled points.

Other options: 1. Create your own Bezier curve or lower order (say - order 3) - and then you'll miss only 1/3 of the points. This kinda renders the issue useless, as you want to conserve less data than sampled. 2. Create a high order Bezier curve that can approximate the whole signature.

You can, however, try to figure out what points you would like to select in order to get a Bezier curve which is closer to the signature : select the top of the letter 'l' as an end point of one Bezier curve and the start of another will yield better results.

You can use simple

code

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();
}

  }

You can use this,I have also attached the xml at the bottom:

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>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!