Capturar firma manual (Android)

TEORÍA

Android no proporciona una View nativa para capturar un dibujo en un nodo de la pantalla y luego almacenarlo como imagen (Bitmap); será necesario crear una View personalizada que llamaremos CaptureBitmapView.

Para crear esta View personalizada hay que definir una clase que herede de View y luego insertarla en un LinearLayout desde código Android.

PRÁCTICA

Definir la clase CaptureBitmapView

Se crea una clase que hereda de View que detecta las pulsaciones y movimientos del dedo sobre la pantalla para dibujar una línea que siga su recorrido:

//https://stackoverflow.com/questions/7228191/android-signature-capture

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.io.ByteArrayOutputStream;

public class CaptureBitmapView 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 CaptureBitmapView(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.drawColor(Color.argb(255, 237, 239, 250));
        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);
        //_Canvas.drawColor(Color.argb(255, 237, 239, 250));
        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;
    }
}

Crear un nodo CaptureBitmapView

Para utilizar la clase anterior se crea un LinearLayout y se le añade un nuevo nodo dinámicamente desde código Android, esto es, una instancia del CaptureBitmapView:

Definir nodo LinearLayout
<!-- Definir el nodo en XML -->
<LinearLayout
    android:id="@+id/signLayout"
    android:layout_width="..."
    android:layout_height="..."
    android:orientation="...">
</LinearLayout>
Añadir CaptureBitmapView al LinearLayout
public class MyActivity ... {
  private CaptureBitmapView mSig;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    	//...
    	LinearLayout mContent = (LinearLayout) findViewById(R.id.signLayout);
        mSig = new CaptureBitmapView(this, null);
        mContent.addView(mSig, LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT);
    }
}

Almacenar el dibujo

Para almacenar el Bitmap del dibujo generado, sólo hay que usar el método «getBitmap» de la clase que hemos creado.

Bitmap signature = mSig.getBitmap();

Limpiar el dibujo

Para limpiar el dibujo que se ha creado, se puede utilizar el método «ClearCanvas» de la clase creada.

mSig.ClearCanvas();

 


Créditos de fuentes externas:

Referencias:

Deja un comentario