38

I am working on android application. In my project I have a task regarding signature capture i.e the user should keep his/her signature on the screen of the mobile and once save button is clicked the signature has to stored in the database. I have searched and found some links but still I didn't find the exact solution. I also tried TouchPaint.java but there I didnt find the xml file for layout. Could you please suggest us with some sample code? I will be thankful to you....

hemanth kumar
  • 3,068
  • 10
  • 41
  • 64
  • 1
    Maybe this [related question][1] gives you some hints. [1]: http://stackoverflow.com/questions/4658703/signature-capture-in-phonegap-android-application – Ber Aug 29 '11 at 09:16
  • Possible duplicate: [Need to implement signature capture](http://stackoverflow.com/q/3752003/145173) – Edward Brey Aug 29 '13 at 09:16

4 Answers4

56

Here is the working Java version of Has AlTaiar's C# Signature View, Took me a while to get it to work 100% correctly

public class CaptureSignatureView extends View {

    private Bitmap _Bitmap;
    private Canvas _Canvas;
    private Path _Path;
    private Paint _BitmapPaint;
    private Paint _paint;
    private float _mX;
    private float _mY;
    private float TouchTolerance = 4;
    private float LineThickness = 4;

    public CaptureSignatureView(Context context, AttributeSet attr) {
        super(context, attr);
        _Path = new Path();
        _BitmapPaint = new Paint(Paint.DITHER_FLAG);
        _paint = new Paint();
        _paint.setAntiAlias(true);
        _paint.setDither(true);
        _paint.setColor(Color.argb(255, 0, 0, 0));
        _paint.setStyle(Paint.Style.STROKE);
        _paint.setStrokeJoin(Paint.Join.ROUND);
        _paint.setStrokeCap(Paint.Cap.ROUND);
        _paint.setStrokeWidth(LineThickness);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        _Bitmap = Bitmap.createBitmap(w, (h > 0 ? h : ((View) this.getParent()).getHeight()), Bitmap.Config.ARGB_8888);
        _Canvas = new Canvas(_Bitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        canvas.drawBitmap(_Bitmap, 0, 0, _BitmapPaint);
        canvas.drawPath(_Path, _paint);
    }

    private void TouchStart(float x, float y) {
        _Path.reset();
        _Path.moveTo(x, y);
        _mX = x;
        _mY = y;
    }

    private void TouchMove(float x, float y) {
        float dx = Math.abs(x - _mX);
        float dy = Math.abs(y - _mY);

        if (dx >= TouchTolerance || dy >= TouchTolerance) {
            _Path.quadTo(_mX, _mY, (x + _mX) / 2, (y + _mY) / 2);
            _mX = x;
            _mY = y;
        }
    }

    private void TouchUp() {
        if (!_Path.isEmpty()) {
            _Path.lineTo(_mX, _mY);
            _Canvas.drawPath(_Path, _paint);
        } else {
            _Canvas.drawPoint(_mX, _mY, _paint);
        }

        _Path.reset();
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        super.onTouchEvent(e);
        float x = e.getX();
        float y = e.getY();

        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                TouchStart(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                TouchMove(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                TouchUp();
                invalidate();
                break;
        }

        return true;
    }

    public void ClearCanvas() {
        _Canvas.drawColor(Color.WHITE);
        invalidate();
    }

    public byte[] getBytes() {
        Bitmap b = getBitmap();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        b.compress(Bitmap.CompressFormat.PNG, 100, baos);
        return baos.toByteArray();
    }

    public Bitmap getBitmap() {
        View v = (View) this.getParent();
        Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
        v.draw(c);

        return b;
    }
}

I tried Rob Croll's suggestion, which worked good, but it is straight liney, rendering the signature not human looking. If you know what I mean :P

Here is how you append the view on an empty linear layout

LinearLayout mContent = (LinearLayout) findViewById(R.id.linearLayout);
CaptureSignatureView mSig = new CaptureSignatureView(this, null);
mContent.addView(mSig, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);

Here is how to get the bytes or Bitmap of the Signature

byte[] signature = mSig.getBytes();
Bitmap signature = mSig.getBitmap();
Pierre
  • 8,397
  • 4
  • 64
  • 80
  • Is it possible to check if user has signed on it or not? – Tonespy Jan 24 '16 at 19:35
  • @ItuokeAjanlekoko You could add an event which checks the length of the line, if it is more than a dot, set bool flag to true – Pierre Jan 25 '16 at 08:09
  • 1
    Yeah. Did something like that, but I wasn't checking for line length. I added it in motion event up and motion event down. And if user clears, it becomes false. Thanks. – Tonespy Jan 25 '16 at 09:35
  • 1
    This is amazing! Works perfect! thank you!!! – Jus10 Mar 13 '17 at 06:55
  • 1
    works like charm! – Qin Jul 21 '18 at 06:04
  • 1
    work perfect!!! thank you so much buddy!!!! – Aleem Momin Jan 21 '19 at 10:45
  • wow! I say again, wow! simple and works perfect! The path is so smooth! Thanks a lot for this! – Ignacio Tomas Crespo Mar 30 '19 at 16:09
  • Sorry to wake up this thread, but how would you load a bitmap into your canvas ? For example, a signature has been captured, and you want to load it again ? – Jacks Jan 15 '20 at 10:04
  • 1
    @Jacks, you can use `_Canvas.drawBitmap(myBitmap, 0, 0);` – Pierre Jan 15 '20 at 15:28
  • 1
    @Pierre Indeed thank you. The problem I was facing is that I had to load my image after the call to `onSizeChanged`. What I did is that I put a boolean flag `mustLoad`, and in the call to `onSizeChanged`, if the flag is true, then I load my Bitmap. In case people have the same problem :) – Jacks Jan 16 '20 at 08:47
  • 1
    Great Answer. Best Answer I found in Stack Overflow. Simple and Clean. Thank You Very Much! – Dinith Rukshan Kumara Feb 09 '20 at 19:31
  • @Pierre How to validate the signature? is it empty or not – Askar May 20 '20 at 10:42
  • 1
    @Askar Create a global boolean `signed`, then in `TouchUp()`, in the if path not empty, set `signed = true;`. In `onSizeChanged()` and `ClearCanvas()` set `signed = false;` – Pierre May 20 '20 at 13:19
  • works great. but how to handle when layout is inside scrollview – Cleaton Pais Apr 01 '22 at 06:39
27

For anyone looking for a solution you will find one at http://www.mysamplecode.com/2011/11/android-capture-signature-using-canvas.html

It actually writes the signature to file but it's easy enough to change and write to a database instead.

RobCroll
  • 2,571
  • 1
  • 26
  • 36
4

I know this is an old question, but I needed to implement my own view to capture the signature because I am using MonoForAndroid (C# not Java). So I am adding my View code in here in case somebody needs it.

using System;
using Android.Content;
using Android.Graphics;
using Android.Views;

namespace MyApp.Views
{
    public class CaptureSignatureView : View
    {
        public CaptureSignatureView(Context c, SignatureData signatureData) : base(c)
        {
            SignatureData = signatureData;
            _Path = new Path();
            _BitmapPaint = new Paint(PaintFlags.Dither);
            _paint = new Paint
            {
                AntiAlias = true,
                Dither = true,
                Color = Color.Argb(255, 0, 0, 0)
            };
            _paint.SetStyle(Paint.Style.Stroke);
            _paint.StrokeJoin = Paint.Join.Round;
            _paint.StrokeCap = Paint.Cap.Round;
            _paint.StrokeWidth = 8;
        }

        protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
        {
            base.OnSizeChanged(w, h, oldw, oldh);
            _Bitmap = Bitmap.CreateBitmap(w, (h > 0 ? h : ((View)this.Parent).Height), Bitmap.Config.Argb8888);
            _Canvas = new Canvas(_Bitmap);
        }

        protected override void OnDraw(Canvas canvas)
        {
            canvas.DrawColor(Color.White);
            canvas.DrawBitmap(_Bitmap, 0, 0, _BitmapPaint);
            canvas.DrawPath(_Path, _paint);
        }

        private float _mX, _mY;
        private const float TouchTolerance = 4;

        private void TouchStart(float x, float y)
        {
            _Path.Reset();
            _Path.MoveTo(x, y);
            _mX = x;
            _mY = y;
            SignatureData.AddPoint(SignatureState.Start, (int)x, (int)y);
        }

        private void TouchMove(float x, float y)
        {
            float dx = Math.Abs(x - _mX);
            float dy = Math.Abs(y - _mY);

            if (dx >= TouchTolerance || dy >= TouchTolerance)
            {
                _Path.QuadTo(_mX, _mY, (x + _mX) / 2, (y + _mY) / 2);
                SignatureData.AddPoint(SignatureState.Move, (int)x, (int)y);
                _mX = x;
                _mY = y;
            }
        }

        private void TouchUp()
        {
            if (!_Path.IsEmpty)
            {
                _Path.LineTo(_mX, _mY);
                _Canvas.DrawPath(_Path, _paint);
            }
            else
            {
                _Canvas.DrawPoint(_mX, _mY, _paint);
            }
            SignatureData.AddPoint(SignatureState.End, (int)_mX, (int)_mY);

            _Path.Reset();
        }

        public override bool OnTouchEvent(MotionEvent e)
        {
            var x = e.GetX();
            var y = e.GetY();

            switch (e.Action)
            {
                case MotionEventActions.Down:
                    TouchStart(x, y);
                    Invalidate();
                    break;
                case MotionEventActions.Move:
                    TouchMove(x, y);
                    Invalidate();
                    break;
                case MotionEventActions.Up:
                    TouchUp();
                    Invalidate();
                    break;
            }
            return true;
        }

        public void ClearCanvas()
        {
            _Canvas.DrawColor(Color.White);
            Invalidate();
        }

        public Bitmap CanvasBitmap()
        {
            return _Bitmap;
        }

        public void Clear()
        {
            ClearCanvas();
            SignatureData = new SignatureData();
        }

        #region Implementation

        private Bitmap _Bitmap;
        private Canvas _Canvas;
        private readonly Path _Path;
        private readonly Paint _BitmapPaint;
        private readonly Paint _paint;
        public  SignatureData SignatureData;

        #endregion
    }

}

Also, I have a blog post here that goes through the details of how to capture the signature. Basically you can do it in two different ways. Either you capture the view with the signature as an image and you save that bitmap (and send it to the server maybe). Or you could just capture a two dimensional array of points and reconstruct the signature in any way you want it.

Has AlTaiar
  • 4,052
  • 2
  • 36
  • 37
4

you probably need gesture builder.

i think this link.

http://android-developers.blogspot.com/2009/10/gestures-on-android-16.html

will be usefull to you. if you need to check the signature again.

UPDATE

are you talking about this

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/TouchPaint.html

then this example does not use xml. it has the view as an inner class (MyView)

Samuel
  • 9,883
  • 5
  • 45
  • 57
  • Hi thanks for the response. I need the TouchPaint application only. But I am unable to view the output when I executed the application. Do I need any other new files to add to execute the application? I have already added two other classes GraphicsActivity and PictureLayout... – hemanth kumar Sep 02 '11 at 05:16
  • 2
    you can find the full working example of TouchPaing/FingerPaint in **com.example.android.apis.graphics** in the android sdk's **API Demos** – Samuel Sep 02 '11 at 05:56