Implémentation d'une signature manuscrite personnalisée sous Android

Introduction technique

La transition vers des processus numériques nécessite souvent la capacité de capturer des signataries sur des appareils mobiles. Dans le cadre d'une application de gestion de sanctions ou d'inspection de terrain, la capture d'une signature électronique devient un impératif fonctionnel. Cette section détaille l'intégration d'un composant de dessin personnalisé pour gérer les entrées tactiles et renvoyer le résultat binaire à l'activité appelante.

Configuration du composant graphique

Pour dessiner librement à l'écran, il est nécessaire de développer ou d'utiliser une CustomView capable de suivre le tracé du doigt. Ce composant gère les événements de contact (onTouchEvent) et stocke les chemins vectoriels (Path) associés aux mouvements de l'utilisateur. Lors de la soumission, ces données sont converties en une image raster.

Création de l'activité dédiée

L'activité responsable de la collecte de la signature doit être légère et isolée. Elle contient exclusivement le canvas de dessin et les actions de contrôle nécessaires. Contrairement aux implémentations lourdes incluant la persistance locale immédiate, ici l'objectif est de renvoyer le flux de données directement via le résultat de l'intent.

Logique de l'activité (Java)

Le code ci-dessous montre comment initialiser les composants, gérer les clics via des écouteurs anonymes et préparer le retour des données sous forme de tableau d'octets pour minimiser la charge mémoire lors du transfert inter-processus.

package com.app.inspection.signature;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import com.app.inspection.custom.SignatureCanvas;

import java.io.ByteArrayOutputStream;

public class ActivitySignatureManuelle extends AppCompatActivity {

    private static final String EXTRA_SIGNATURE_DATA = "data_signature_bytes";
    private SignatureCanvas canvassignature;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sign_capture);

        // Initialisation des vues
        canvassignature = findViewById(R.id.canvas_draw_area);
        canvassignature.setDrawingCacheEnabled(true);

        // Gestionnaire d'annulation (Retour)
        findViewById(R.id.action_exit).setOnClickListener(v -> finish());

        // Gestionnaire d'effacement
        findViewById(R.id.btn_clear_trace).setOnClickListener(v -> {
            if (canvassignature != null) {
                canvassignature.clearPath();
            }
        });

        // Gestionnaire de validation
        findViewById(R.id.boutonValider).setOnClickListener(this::traiterSubmission);
    }

    private void traiterSubmission(View vueSubmit) {
        // Vérification de présence de données
        if (canvassignature.getPathCount() == 0) {
            Toast.makeText(this, "Trace manquante", Toast.LENGTH_SHORT).show();
            return;
        }

        // Récupération de l'image depuis le cache
        Bitmap imageSigne = canvassignature.getDrawingCache();
        
        if (imageSigne == null || imageSigne.isRecycled()) {
            Toast.makeText(this, "Échec de la création de l'image", Toast.LENGTH_SHORT).show();
            return;
        }

        // Conversion en tableau d'octets pour le passage par Intent
        ByteArrayOutputStream streamData = new ByteArrayOutputStream();
        imageSigne.compress(Bitmap.CompressFormat.JPEG, 90, streamData);
        byte[] donneesBinaires = streamData.toByteArray();

        // Construction du résultat
        Intent resultatIntent = new Intent();
        resultatIntent.putExtra(EXTRA_SIGNATURE_DATA, donneesBinaires);
        setResult(Activity.RESULT_OK, resultatIntent);
        finish();
    }
}

Disposition de l'niterface (XML)

Le layout se concentre sur la zone de dessin principale. Les attributs de couleur et d'épaisseur sont définis dans les ressources pour permettre une personnalisation simple sans toucher au code compilé.

<?xml version="1.0" encoding="utf-8"??>
<linearlayout android:background="@color/fond_application" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">

    
    <include layout="@layout/header_inclure"></include>

    <textview android:gravity="center" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="16dp" android:text="Veuillez signer ici numériquement" android:textcolor="@color/text_standard" android:textstyle="bold"></textview>

    <com.app.inspection.custom.signaturecanvas android:background="@color/blanc_clean" android:id="@+id/canvas_draw_area" android:layout_height="240dp" android:layout_width="match_parent" app:border_color="#CCCCCC" app:stroke_width="8"></com.app.inspection.custom.signaturecanvas>

    <linearlayout android:layout_height="wrap_content" android:layout_margintop="24dp" android:layout_width="match_parent" android:orientation="horizontal" android:padding="12dp">

        <button android:id="@+id/btn_clear_trace" android:layout_height="50dp" android:layout_weight="1" android:layout_width="0dp" android:text="Effacer" android:textallcaps="false"></button>

        <button android:backgroundtint="@color/action_bleu" android:id="@+id/boutonValider" android:layout_height="50dp" android:layout_marginstart="16dp" android:layout_weight="1" android:layout_width="0dp" android:text="Enregistrer" android:textcolor="@color/blanc_clean"></button>

    </linearlayout>

</linearlayout>

Intégration dans l'activité principale

L'activité principale, responsable de la saisie du formulaire de sanction, doit lancer la capture de signature et récupérer les données retournées. Il est important de distinguer plusieurs zones de signature (ex : agent inspecteur vs contrevenant) en utilisant des codes de requête différents.

Mise en œuvre du lancement et réception

Voici comment gérer la navigation vers le module de signature et décoder le tableau d'octets reçu pour l'affichage immédiat dans une ImageView.

public class FormulaireSanction extends AppCompatActivity {

    private static final int REQ_CODE_CLIENT = 101;
    private static final int REQ_CODE_AGENT = 102;
    private ImageView imageClient, imageAgent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_form_sanction);

        imageClient = findViewById(R.id.img_zone_client);
        imageAgent = findViewById(R.id.img_zone_agent);

        // Attachement des listeners pour déclencher la signature
        imageClient.setOnClickListener(v -> 
            lancerSignature(REQ_CODE_CLIENT));
            
        imageAgent.setOnClickListener(v -> 
            lancerSignature(REQ_CODE_AGENT));
    }

    private void lancerSignature(int requestCode) {
        Intent intentionLancer = new Intent(this, ActivitySignatureManuelle.class);
        startActivityForResult(intentionLancer, requestCode);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK && data != null) {
            byte[] fluxImage = data.getByteArrayExtra(
                "data_signature_bytes");
            
            if (fluxImage != null && fluxImage.length > 0) {
                Bitmap bitmapRegener = BitmapFactory.decodeByteArray(
                    fluxImage, 0, fluxImage.length);
                    
                if (requestCode == REQ_CODE_CLIENT) {
                    imageClient.setImageBitmap(bitmapRegener);
                } else if (requestCode == REQ_CODE_AGENT) {
                    imageAgent.setImageBitmap(bitmapRegener);
                }
            }
        }
    }
}

Étiquettes: Android Java signature-numérique customview intent

Publié le 13 août à 22h51