安卓:使用贝塞尔曲线进行数字签名

19

输入图像描述
我试图使用贝塞尔曲线绘制数字签名,如上所示的图片。当我触摸并尝试绘制线条时,结果是点线而不是连续的线条。使用简单签名可以实现简单的签名,但我想使用贝塞尔曲线和触摸压力创建更平滑的曲线。尝试了这个链接

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

}

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

贝塞尔曲线.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;
}


+1 我之前尝试过,有一些成功,但并不完美。让我搜索一下那段代码。 - Arun C
@ArunCThomas 上面的代码没有任何错误,但是无法在触摸屏上绘制线条。我错在哪里了? - kyogs
3
你想出了一些解决方案吗? - syntagma
嗨,我想知道你找到解决方案了吗? - Ehsan
@REACHUS 你能否请评论一下你是否找到了任何好的解决方案?这会对很多人有所帮助。谢谢。 - Shobhit Puri
显示剩余2条评论
4个回答

2

将贝塞尔类中的绘制方法更改为:

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

谢谢,但是你从http://corner.squareup.com/2012/07/smoother-signatures.html得到了这个方法,我也尝试过了。:) - kyogs

1
你可以使用简单的代码。
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();
}

  }

1
你可以使用它,我也在底部附上了xml。
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>

有趣的是,我也看到过一个使用这种方法制作签名的教程,但实际上使用贝塞尔曲线的优点在于当笔画变大时,您可以定义更好的签名,当您用手指移动得更慢时,笔画会变得更细,就像真正的笔一样。我想这实际上并没有回答问题。 - Pedro Teran

1
你需要查看Bezier类中绘制了多少点,然后将它们加起来。
从你的代码来看:你正在收集4个点并在它们之上绘制Bezier曲线。这意味着算法-按设计-将错过一半的采样点。
其他选项: 1. 创建自己的Bezier曲线或较低阶数(例如-3阶)-那么你只会错过1/3的点。这有点使问题无用,因为你想要保留比采样数据更少的数据。 2. 创建一个高阶Bezier曲线,可以近似整个签名。
但你可以尝试找出想要选择哪些点,以便获得更接近签名的Bezier曲线:选择字母'l'的顶部作为一个Bezier曲线的端点,另一个Bezier曲线的开始点会产生更好的结果。

谢谢 :). 但在上面的代码中,我哪里错了?你有使用贝塞尔曲线的演示视图吗?.. :) - kyogs
我使用了与你相同的类,但进行了更改以添加Bezier(localBezier, this.m_LastWidth * 2, f)。 - Henry Aloni
我按照您的要求更改了这行代码,但仍然只能得到点,无法画出连续的线条,就像上面的图片所示。 - kyogs
基本上,绘制贝塞尔曲线的算法只是放置许多线条。您可以将其更改为在点p(n)和p(n+1)之间绘制线条。这是绘制贝塞尔曲线的经典方法:您假设在屏幕视图内绘制贝塞尔曲线所需的像素不超过max(width,height)。 - Henry Aloni
你说我也改了,但是没有成功。你能告诉我在我的上面的代码中哪里出错了吗?为什么我没有得到像图片中显示的完美符号呢? - kyogs
我看不出哪里有问题,因为我没有清晰的图片来展示你所看到的实际结果。我使用了下面的代码和bezier类 - 它可以正常工作 - 画出的线看起来像是一条实线(尽管它只是一组点)。 - Henry Aloni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接