Android实现签名并转成图片和打印的功能

Android实现签名的功能

自定义一个签名画布的面板

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class SignatureView extends View {
    
    
    private Paint paint = new Paint();
    private Path path = new Path();
    private Paint borderPaint = new Paint();

    public SignatureView(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(5f);

        borderPaint.setStyle(Paint.Style.STROKE);
        borderPaint.setStrokeWidth(10f);
        borderPaint.setColor(Color.RED);

        setBackgroundColor(Color.WHITE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
    
    
        canvas.drawPath(path, paint);
        canvas.drawRect(0, 0, getWidth(), getHeight(), borderPaint);
    }

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

        switch (event.getAction()) {
    
    
            case MotionEvent.ACTION_DOWN:
                path.moveTo(x, y);
                return true;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(x, y);
                break;
            case MotionEvent.ACTION_UP:
                // do nothing
                break;
            default:
                return false;
        }

        invalidate();
        return true;
    }

    public Bitmap getSignatureBitmap() {
    
    
        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        draw(canvas);
        return bitmap;
    }

    public void clearSignature() {
    
    
        path.reset();
        invalidate();
    }
}

写一个签名的页面布局

画布面板定义完成后,直接在layout文件中使用就行了:红色框中就是签名的地方。
签名的页面

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.util.SignatureActivity">

    <com.pft.mobilemr.customview.SignatureView
        android:id="@+id/signature_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/bottom_button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/bottom_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/signature_view">

        <Button
            android:id="@+id/cancel_signature"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_marginHorizontal="10dp"
            android:text="@string/cancel_signature"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/clear_signature"
            app:layout_constraintHorizontal_weight="1"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/clear_signature"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_marginHorizontal="10dp"
            android:text="@string/clear_signature"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/confirm_signature"
            app:layout_constraintHorizontal_weight="1"
            app:layout_constraintStart_toEndOf="@+id/cancel_signature"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/confirm_signature"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_marginHorizontal="10dp"
            android:text="@string/confirm_signature"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_weight="1"
            app:layout_constraintStart_toEndOf="@+id/clear_signature"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

activity中签名时需要的一些逻辑和功能实现

activity中直接写逻辑,并且设置三个按钮各自的逻辑,具体按钮方法实现的代码在自定义View页面都有写,直接调用就行,点击确认按钮后会将签名转成bitmap格式的图片,然后转成byte[]格式后放入Intent后传入其他页面。

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;

import com.pft.mobilemr.R;
import com.pft.mobilemr.customview.SignatureView;

import java.io.ByteArrayOutputStream;

/**
 * 签名页面
 */
public class SignatureActivity extends AppCompatActivity {
    
    

    private SignatureView signatureView;

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

    private void init() {
    
    
        signatureView = findViewById(R.id.signature_view);

        findViewById(R.id.clear_signature).setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                signatureView.clearSignature();
            }
        });

        findViewById(R.id.cancel_signature).setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                Intent intent = new Intent();
                setResult(RESULT_CANCELED, intent);
                finish();
            }
        });

        findViewById(R.id.confirm_signature).setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                Bitmap signatureBitmap = signatureView.getSignatureBitmap();
                Intent intent = new Intent();
                byte[] signature = Bitmap2Bytes(signatureBitmap);
                intent.putExtra("signature", signature);
                setResult(RESULT_OK, intent);
                finish();
            }
        });
    }

    private byte[] Bitmap2Bytes(Bitmap bm) {
    
    
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        return baos.toByteArray();
    }
}

整体来说逻辑并不是很混乱,代码的实现也并不是很难。
签名最好使用横屏,所以对这个activity设置打开方式为横屏。

<activity
    android:name=".activity.util.SignatureActivity"
    android:label="@string/activity_signature"
    android:screenOrientation="landscape" />

将签名的图片进行展示并打印

其他页面接收数据并展示出来

签名页面点击确认签名后,会跳转回前一个页面,并且将签名的内容一起传过去。获取到byte[]格式的图片后,转成bitmap后,在imageView控件中放入这个bitmap图片。

if (requestCode == REQUEST_CODE_SIGNATURE && resultCode == RESULT_OK && data != null) {
    
    
	byteArray = data.getByteArrayExtra("signature");
	if (byteArray == null) {
    
    
		toastMsg("签名数据为空");
		return;
	}

	// 处理签名数据
	ImageView imageView = findViewById(R.id.signature_img);
	imageBitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
	imageView.setImageBitmap(imageBitmap);
}

打印签名

打印签名之前,需要对这个签名的bitmap进行修整,使其大小符合我们的签名纸宽度,不然会出现只打印一半的情况。

开始打印:

try {
    
    
	p.reset();
	byte[] bytes = Printer.draw2PxPoint(p.bmpzoomTo58(imageBitmap));
	p.write(bytes);
	p.enter();
	p.feed();
	p.feed();
	p.close();
} catch (Exception e) {
    
    
	e.printStackTrace();
}

其中draw2PxPoint()方法就是对签名图片进行修整。

图片修整

/*************************************************************************
 * 假设一个240*240的图片,分辨率设为24, 共分10行打印
 * 每一行,是一个 240*24 的点阵, 每一列有24个点,存储在3个byte里面。
 * 每个byte存储8个像素点信息。因为只有黑白两色,所以对应为1的位是黑色,对应为0的位是白色
 **************************************************************************/
/**
 * 把一张Bitmap图片转化为打印机可以打印的字节流
 *
 * @param bmp
 * @return
 */
public static byte[] draw2PxPoint(Bitmap bmp) throws IOException {
    
    

	//用来存储转换后的 bitmap 数据。为什么要再加1000,这是为了应对当图片高度无法
	//整除24时的情况。比如bitmap 分辨率为 240 * 250,占用 7500 byte,
	//但是实际上要存储11行数据,每一行需要 24 * 240 / 8 =720byte 的空间。再加上一些指令存储的开销,
	//所以多申请 1000byte 的空间是稳妥的,不然运行时会抛出数组访问越界的异常。
	int size = bmp.getWidth() * bmp.getHeight() / 8 + 7500;
	byte[] data = new byte[size];
	int k = 0;
	//设置行距为0的指令
	data[k++] = 0x1B;
	data[k++] = 0x33;
	data[k++] = 0x01;
	// 逐行打印
	for (int j = 0; j < bmp.getHeight() / 24f; j++) {
    
    
		//打印图片的指令
		data[k++] = 0x1B;
		data[k++] = 0x2A;
		data[k++] = 33;
		data[k++] = (byte) (bmp.getWidth() % 256); //nL
		data[k++] = (byte) (bmp.getWidth() / 256); //nH
		//对于每一行,逐列打印
		for (int i = 0; i < bmp.getWidth(); i++) {
    
    
			//每一列24个像素点,分为3个字节存储
			for (int m = 0; m < 3; m++) {
    
    
				//每个字节表示8个像素点,0表示白色,1表示黑色
				for (int n = 0; n < 8; n++) {
    
    
					byte b = px2Byte(i, j * 24 + m * 8 + n, bmp);
					data[k] += data[k] + b;
				}
				k++;
			}
		}
		if (k < data.length) {
    
    
			data[k++] = 10;//换行
		}
	}
	return data;
}

/**
 * 灰度图片黑白化,黑色是1,白色是0
 *
 * @param x   横坐标
 * @param y   纵坐标
 * @param bit 位图
 * @return
 *
 */
public static byte px2Byte(int x, int y, Bitmap bit) {
    
    
	if (x < bit.getWidth() && y < bit.getHeight()) {
    
    
		byte b;
		int pixel = bit.getPixel(x, y);
		int red = (pixel & 0x00ff0000) >> 16; // 取高两位
		int green = (pixel & 0x0000ff00) >> 8; // 取中两位
		int blue = pixel & 0x000000ff; // 取低两位
		int gray = RGB2Gray(red, green, blue);
		if (gray < 128) {
    
    
			b = 1;
		} else {
    
    
			b = 0;
		}
		return b;
	}
	return 0;
}

/**
 * 图片灰度的转化
 *
 */
private static int RGB2Gray(int r, int g, int b) {
    
    
	int gray = (int) (0.29900 * r + 0.58700 * g + 0.11400 * b); //灰度转化公式
	return gray;
}

DecimalFormat df = new DecimalFormat("0.00");

public Bitmap bmpzoomTo58(Bitmap mBitmap) {
    
    
	if (mBitmap.getWidth() >= 380) {
    
    
		float c = Float.valueOf(df.format((float) mBitmap.getWidth() / 380));
		int newHight = Integer.parseInt(new java.text.DecimalFormat("0").format(mBitmap.getHeight() / c));
		mBitmap = zoomImg(mBitmap, 380, newHight);
	} else {
    
    
		float c = Float.valueOf(df.format((float) 380 / mBitmap.getWidth()));

		Log.e("", "c:" + c);
		int newHight1 = Integer.parseInt(new java.text.DecimalFormat("0").format(c * mBitmap.getHeight()));
		mBitmap = zoomImg(mBitmap, 380, newHight1);
	}
	return mBitmap;
}

// 缩放图片
private static Bitmap zoomImg(Bitmap bm, int newWidth, int newHeight) {
    
    
	// 获得图片的宽高
	int width = bm.getWidth();
	int height = bm.getHeight();
	// 计算缩放比例
	float scaleWidth = ((float) newWidth) / width;
	float scaleHeight = ((float) newHeight) / height;
	// 取得想要缩放的matrix参数
	Matrix matrix = new Matrix();
	matrix.postScale(scaleWidth, scaleHeight);
	// 得到新的图片
	Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
	return newbm;
}

示例图片:
签名面板签名
签名打印结果

猜你喜欢

转载自blog.csdn.net/jxj960417/article/details/132879671