最近在做打印小票的功能,需要通过USB串口连接打印机打印出订单的小票,自己也是在网上查了一些相关的资料,也踩了一些坑,最后把总结的代码分享出来,给有需要的朋友做个参考,废话不多说了,先看打印出来的效果,使用的是佳博打印机,58mm和80mm的纸张排版都有。
接下来上代码,由于项目是使用java+kotlin混合着写的,所以说会有这两种语言的代码。
一、连接USB打印机
public class UsbPrinter {
private static final String TAG = "UsbPrinter";
public static final String ACTION_USB_PERMISSION = "com.usb.printer.USB_PERMISSION";
@SuppressLint("StaticFieldLeak")
private static UsbPrinter mInstance;
private Context mContext;
private PendingIntent mPermissionIntent;
private UsbManager mUsbManager;
private UsbDeviceConnection mUsbDeviceConnection;
private UsbEndpoint ep, printerEp;
private UsbInterface usbInterface;
private static final int TIME_OUT = 100000;
private UsbPrinter() {
}
public static UsbPrinter getInstance() {
if (mInstance == null) {
synchronized (UsbPrinter.class) {
if (mInstance == null) {
mInstance = new UsbPrinter();
}
}
}
return mInstance;
}
/**
* 初始化打印机,需要与destroy对应
*
* @param context 上下文
*/
public void initPrinter(Context context) {
init(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void init(Context context) {
mContext = context;
mUsbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
// 注册广播监听usb设备
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
mContext.registerReceiver(mUsbDeviceReceiver, filter);
// 列出所有的USB设备,并且都请求获取USB权限
HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
for (UsbDevice device : deviceList.values()) {
// 得到此设备的一个接口
usbInterface = device.getInterface(0);
Logger.e(TAG, usbInterface.getInterfaceClass() + "");
// 获取接口的类别 7代表连接的是打印机
if (usbInterface.getInterfaceClass() == 7) {
Logger.e(TAG, device.getProductName() + " " + device.getManufacturerName());
// 1137 85 1027
// 26728 1280 1045+2
Logger.e(TAG, device.getVendorId() + " " + device.getProductId() + " " + device.getDeviceId());
if (!mUsbManager.hasPermission(device)) {
mUsbManager.requestPermission(device, mPermissionIntent);
} else {
connectUsbPrinter(device);
}
}
}
}
private final BroadcastReceiver mUsbDeviceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Logger.e("action", action);
UsbDevice mUsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) && mUsbDevice != null) {
Logger.e("receiver", action);
connectUsbPrinter(mUsbDevice);
} else {
Toast.makeText(context, "USB设备请求被拒绝", Toast.LENGTH_SHORT).show();
}
}
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
if (mUsbDevice != null) {
Toast.makeText(context, "有设备拔出", Toast.LENGTH_SHORT).show();
}
} else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
Toast.makeText(context, "有设备插入", Toast.LENGTH_SHORT).show();
if (mUsbDevice != null) {
if (!mUsbManager.hasPermission(mUsbDevice)) {
mUsbManager.requestPermission(mUsbDevice, mPermissionIntent);
}
}
}
}
};
/**
* 关闭usb的端口及取消注册的广播监听
*/
public void close() {
if (mUsbDeviceConnection != null) {
mUsbDeviceConnection.close();
mUsbDeviceConnection = null;
}
mContext.unregisterReceiver(mUsbDeviceReceiver);
mContext = null;
mUsbManager = null;
}
/**
* 连接打印机设备
*
* @param mUsbDevice 识别到的打印机设备
*/
private void connectUsbPrinter(UsbDevice mUsbDevice) {
if (mUsbDevice != null) {
for (int i = 0; i < usbInterface.getEndpointCount(); i++) {
// 获取指定的index 获得此接口的一个节点 返回一个UsbEndpoint
ep = usbInterface.getEndpoint(i);
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
int pid = mUsbDevice.getProductId();
Logger.e(TAG, "connectUsbPrinter: " + pid);
int vid = mUsbDevice.getVendorId();
Logger.e(TAG, "connectUsbPrinter: " + vid);
mUsbDeviceConnection = mUsbManager.openDevice(mUsbDevice);
printerEp = ep;
if (mUsbDeviceConnection != null) {
Toast.makeText(mContext, "设备已连接", Toast.LENGTH_SHORT).show();
// 在使用UsbInterface进行数据的写入写出之前,要申明对其的专有访问权限,防止通信混乱
mUsbDeviceConnection.claimInterface(usbInterface, true);
}
}
}
}
} else {
Toast.makeText(mContext, "未发现可用的打印机", Toast.LENGTH_SHORT).show();
}
}
/**
* 安卓9.0之前
* 只要你传送的数据不大于16384 bytes,传送不会出问题,一旦数据大于16384 bytes,也可以传送,
* 只是大于16384后面的数据就会丢失,获取到的数据永远都是前面的16384 bytes,
* 所以,android USB Host 模式与HID使用bulkTransfer(endpoint,buffer,length,timeout)通讯时
* buffer的长度不能超过16384。
* <p>
* controlTransfer( int requestType, int request , int value , int index , byte[] buffer , int length , int timeout)
* 该方法通过0节点向此设备传输数据,传输的方向取决于请求的类别,如果requestType 为 USB_DIR_OUT 则为写数据 , USB _DIR_IN ,则为读数据
*/
private void write(byte[] bytes) {
if (mUsbDeviceConnection != null) {
// 通过给定的endpoint 来进行大量的数据传输,传输的方向取决于该节点的方向,
// buffer是要发送或接收的字节数组,length是该字节数组的长度,失败则返回负数
// 下行端点,字节数组消息,消息长度,响应时间
int len = mUsbDeviceConnection.bulkTransfer(printerEp, bytes, bytes.length, TIME_OUT);
Logger.e(TAG, "返回数据的长度为:len=" + len);
} else {
Looper.prepare();
handler.sendEmptyMessage(0);
Looper.loop();
}
}
@SuppressLint("HandlerLeak")
private final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Toast.makeText(mContext, "未发现可用的打印机", Toast.LENGTH_SHORT).show();
}
};
/**
* 初始化
*/
public void init() {
write(EscUtil.initPrinter());
}
/**
* 打印文字
*
* @param msg 打印的内容
*/
public void printText(String msg) {
byte[] bytes = new byte[0];
try {
bytes = msg.getBytes("gbk");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
write(bytes);
}
/**
* 换行打印文字
*
* @param msg 打印的内容
*/
public void printTextNewLine(String msg) {
byte[] bytes = new byte[0];
try {
bytes = msg.getBytes("gbk");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
write(new String("\n").getBytes());
write(bytes);
}
/**
* 换行
*/
public void printLine() {
// write(new byte[]{10});
write(new String("\n").getBytes());
}
/**
* 打印空行
*
* @param size 几行
*/
public void printLine(int size) {
for (int i = 0; i < size; i++) {
printText("\n");
}
}
/**
* 设置字体大小
*
* @param size 0:正常大小 1:两倍高 2:两倍宽 3:两倍大小 4:三倍高 5:三倍宽 6:三倍大 7:四倍高 8:四倍宽 9:四倍大小 10:五倍高 11:五倍宽 12:五倍大小
*/
public void setTextSize(int size) {
write(EscUtil.setTextSize(size));
}
/**
* 字体加粗
*
* @param isBold true/false
*/
public void bold(boolean isBold) {
if (isBold) {
write(EscUtil.boldOn());
} else {
write(EscUtil.boldOff());
}
}
/**
* 打印一维条形码
*
* @param data 条码
*/
public void printBarCode(String data) {
// write(EscUtil.getPrintBarCode(data, 5, 90, 5, 2));
write(EscUtil.getPrintBarCode(data, 5, 90, 4, 2));
}
/**
* 打印二维码
*
* @param data 打印的内容
*/
public void printQrCode(String data) {
write(EscUtil.getPrintQrCode(data, 250));
}
/**
* 设置对齐方式
*
* @param position 0居左 1居中 2居右
*/
public void setAlign(int position) {
byte[] bytes = null;
switch (position) {
case 0:
bytes = EscUtil.alignLeft();
// bytes = new byte[]{ 0x1b, 0x61, 0x30 };
// bytes = new byte[]{27, 97, (byte) 0};
break;
case 1:
bytes = EscUtil.alignCenter();
// bytes = new byte[]{ 0x1b, 0x61, 0x31 };
// bytes = new byte[]{27, 97, (byte) 1};
break;
case 2:
bytes = EscUtil.alignRight();
// bytes = new byte[]{ 0x1b, 0x61, 0x32 };
// bytes = new byte[]{27, 97, (byte) 2};
break;
default:
break;
}
write(bytes);
}
/**
* 获取字符串的宽度
*
* @param str 取字符
* @return 宽度
*/
public int getStringWidth(String str) {
int width = 0;
for (char c : str.toCharArray()) {
width += isChinese(c) ? 2 : 1;
}
return width;
}
/**
* 判断是否中文
* GENERAL_PUNCTUATION 判断中文的“号
* CJK_SYMBOLS_AND_PUNCTUATION 判断中文的。号
* HALFWIDTH_AND_FULLWIDTH_FORMS 判断中文的,号
*
* @param c 字符
* @return 是否中文
*/
public static boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION;
}
/**
* 切纸
*/
public void cutPager() {
write(EscUtil.cutter());
}
}
打印指令相关:
public class EscUtil {
/**
* Escape
*/
public static final byte ESC = 0x1B;
/**
* Text delimiter 文本分隔符
*/
public static final byte FS = 0x1C;
/**
* Group separator 组分离器
*/
public static final byte GS = 0x1D;
/**
* data link escape
*/
public static final byte DLE = 0x10;
/**
* End of transmission
*/
public static final byte EOT = 0x04;
/**
* Enquiry character
*/
public static final byte ENQ = 0x05;
/**
* Spaces
*/
public static final byte SP = 0x20;
/**
* Horizontal list
*/
public static final byte HT = 0x09;
/**
* 打印和包装(水平方向)
*/
public static final byte LF = 0x0A;
/**
* Home key
*/
public static final byte CR = 0x0D;
/**
* Carriage control (print and return to the standard mode (in page mode))
*/
public static final byte FF = 0x0C;
/**
* Canceled (cancel print data in page mode)
*/
public static final byte CAN = 0x18;
/**
* 初始化打印机
*/
public static byte[] initPrinter() {
byte[] result = new byte[2];
result[0] = ESC;
result[1] = 0x40;
return result;
}
/**
* 打印浓度指令
*/
public static byte[] setPrinterDarkness(int value) {
byte[] result = new byte[9];
result[0] = GS;
result[1] = 40;
result[2] = 69;
result[3] = 4;
result[4] = 0;
result[5] = 5;
result[6] = 5;
result[7] = (byte) (value >> 8);
result[8] = (byte) value;
return result;
}
/**
* 打印单个二维码 sunmi自定义指令
*
* @param code: 二维码数据
* @param modulesize: 二维码块大小(单位:点, 取值 1 至 16 )
* @param errorlevel: 二维码纠错等级(0 至 3)
* 0 -- 纠错级别L ( 7%)
* 1 -- 纠错级别M (15%)
* 2 -- 纠错级别Q (25%)
* 3 -- 纠错级别H (30%)
*/
public static byte[] getPrintQrCode(String code, int moduleSize, int errorLevel) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
buffer.write(setQrCodeSize(moduleSize));
buffer.write(setQrCodeErrorLevel(errorLevel));
buffer.write(getQrCodeBytes(code));
buffer.write(getBytesForPrintQrCode(true));
} catch (Exception e) {
e.printStackTrace();
}
return buffer.toByteArray();
}
/**
* 横向两个二维码 sunmi自定义指令
*
* @param code1: 二维码数据
* @param code2: 二维码数据
* @param modulesize: 二维码块大小(单位:点, 取值 1 至 16 )
* @param errorlevel: 二维码纠错等级(0 至 3)
* 0 -- 纠错级别L ( 7%)
* 1 -- 纠错级别M (15%)
* 2 -- 纠错级别Q (25%)
* 3 -- 纠错级别H (30%)
*/
public static byte[] getPrintDoubleQrCode(String code1, String code2, int moduleSize, int errorLevel) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
buffer.write(setQrCodeSize(moduleSize));
buffer.write(setQrCodeErrorLevel(errorLevel));
buffer.write(getQrCodeBytes(code1));
buffer.write(getBytesForPrintQrCode(false));
buffer.write(getQrCodeBytes(code2));
// 加入横向间隔
buffer.write(new byte[]{0x1B, 0x5C, 0x18, 0x00});
buffer.write(getBytesForPrintQrCode(true));
} catch (Exception e) {
e.printStackTrace();
}
return buffer.toByteArray();
}
/**
* 光栅打印二维码
*/
public static byte[] getPrintQrCode(String data, int size) {
byte[] bytes1 = new byte[4];
bytes1[0] = GS;
bytes1[1] = 0x76;
bytes1[2] = 0x30;
bytes1[3] = 0x00;
byte[] bytes2 = BytesUtil.getZxingQrCode(data, size);
return bytes2 == null ? new byte[]{} : BytesUtil.byteMerger(bytes1, bytes2);
}
/**
* 打印一维条形码
*/
public static byte[] getPrintBarCode(String data, int logy, int height, int width, int textPosition) {
if (logy < 0 || logy > 10) {
return new byte[]{LF};
}
if (width < 2 || width > 6) {
width = 2;
}
if (textPosition < 0 || textPosition > 3) {
textPosition = 0;
}
if (height < 1 || height > 255) {
height = 162;
}
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
buffer.write(new byte[]{0x1D, 0x66, 0x01, 0x1D, 0x48, (byte) textPosition,
0x1D, 0x77, (byte) width, 0x1D, 0x68, (byte) height, 0x0A});
byte[] barcode;
if (logy == 10) {
barcode = BytesUtil.getBytesFromDecString(data);
} else {
barcode = data.getBytes("GB18030");
}
if (logy > 7) {
buffer.write(new byte[]{0x1D, 0x6B, 0x49, (byte) (barcode.length + 2), 0x7B, (byte) (0x41 + logy - 8)});
} else {
buffer.write(new byte[]{0x1D, 0x6B, (byte) (logy + 0x41), (byte) barcode.length});
}
buffer.write(barcode);
} catch (Exception e) {
e.printStackTrace();
}
return buffer.toByteArray();
}
/**
* 光栅位图打印
*/
public static byte[] printBitmap(Bitmap bitmap) {
byte[] bytes1 = new byte[4];
bytes1[0] = GS;
bytes1[1] = 0x76;
bytes1[2] = 0x30;
bytes1[3] = 0x00;
byte[] bytes2 = BytesUtil.getBytesFromBitMap(bitmap);
return BytesUtil.byteMerger(bytes1, bytes2);
}
/**
* 光栅位图打印 设置mode
*/
public static byte[] printBitmap(Bitmap bitmap, int mode) {
byte[] bytes1 = new byte[4];
bytes1[0] = GS;
bytes1[1] = 0x76;
bytes1[2] = 0x30;
bytes1[3] = (byte) mode;
byte[] bytes2 = BytesUtil.getBytesFromBitMap(bitmap);
return BytesUtil.byteMerger(bytes1, bytes2);
}
/**
* 光栅位图打印
*/
public static byte[] printBitmap(byte[] bytes) {
byte[] bytes1 = new byte[4];
bytes1[0] = GS;
bytes1[1] = 0x76;
bytes1[2] = 0x30;
bytes1[3] = 0x00;
return BytesUtil.byteMerger(bytes1, bytes);
}
/**
* 选择位图指令 设置mode
* 需要设置1B 33 00将行间距设为0
*/
public static byte[] selectBitmap(Bitmap bitmap, int mode) {
return BytesUtil.byteMerger(BytesUtil.byteMerger(new byte[]{0x1B, 0x33, 0x00}, BytesUtil.getBytesFromBitMap(bitmap, mode)), new byte[]{0x0A, 0x1B, 0x32});
}
/**
* 跳指定行数
*/
public static byte[] nextLine(int lineNum) {
byte[] result = new byte[lineNum];
for (int i = 0; i < lineNum; i++) {
result[i] = LF;
}
return result;
}
/**
* 设置下划线1点
*/
public static byte[] underlineWithOneDotWidthOn() {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 45;
result[2] = 1;
return result;
}
/**
* 设置下划线2点
*/
public static byte[] underlineWithTwoDotWidthOn() {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 45;
result[2] = 2;
return result;
}
/**
* 取消下划线
*/
public static byte[] underlineOff() {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 45;
result[2] = 0;
return result;
}
// ------------------------bold-----------------------------
/**
* 字体加粗
*/
public static byte[] boldOn() {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 69;
result[2] = 0xF;
return result;
}
/**
* 取消字体加粗
*/
public static byte[] boldOff() {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 69;
result[2] = 0;
return result;
}
// ------------------------character-----------------------------
/**
* 单字节模式开启
*/
public static byte[] singleByte() {
byte[] result = new byte[2];
result[0] = FS;
result[1] = 0x2E;
return result;
}
/**
* 单字节模式关闭
*/
public static byte[] singleByteOff() {
byte[] result = new byte[2];
result[0] = FS;
result[1] = 0x26;
return result;
}
/**
* 设置单字节字符集
*/
public static byte[] setCodeSystemSingle(byte charset) {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 0x74;
result[2] = charset;
return result;
}
/**
* 设置多字节字符集
*/
public static byte[] setCodeSystem(byte charset) {
byte[] result = new byte[3];
result[0] = FS;
result[1] = 0x43;
result[2] = charset;
return result;
}
// ------------------------Align-----------------------------
/**
* 居左对齐
*/
public static byte[] alignLeft() {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 97;
result[2] = 0;
return result;
}
/**
* 居中对齐
*/
public static byte[] alignCenter() {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 97;
result[2] = 1;
return result;
}
/**
* 居右对齐
*/
public static byte[] alignRight() {
byte[] result = new byte[3];
result[0] = ESC;
result[1] = 97;
result[2] = 2;
return result;
}
/**
* 切刀
*/
public static byte[] cutter() {
// byte[] data = new byte[]{0x1d, 0x56, 0x01};
// byte[] data = new byte[]{29, 86, (byte) 1};
return new byte[]{GS, 'V', 0x00};
}
/**
* 走纸到黑标
*/
public static byte[] go() {
return new byte[]{0x1C, 0x28, 0x4C, 0x02, 0x00, 0x42, 0x31};
}
/**
* 二维码块大小设置指令
*/
private static byte[] setQrCodeSize(int moduleSize) {
byte[] dtmp = new byte[8];
dtmp[0] = GS;
dtmp[1] = 0x28;
dtmp[2] = 0x6B;
dtmp[3] = 0x03;
dtmp[4] = 0x00;
dtmp[5] = 0x31;
dtmp[6] = 0x43;
dtmp[7] = (byte) moduleSize;
return dtmp;
}
/**
* 二维码纠错等级设置指令
*/
private static byte[] setQrCodeErrorLevel(int errorLevel) {
byte[] dtmp = new byte[8];
dtmp[0] = GS;
dtmp[1] = 0x28;
dtmp[2] = 0x6B;
dtmp[3] = 0x03;
dtmp[4] = 0x00;
dtmp[5] = 0x31;
dtmp[6] = 0x45;
dtmp[7] = (byte) (48 + errorLevel);
return dtmp;
}
/**
* 打印已存入数据的二维码
*/
private static byte[] getBytesForPrintQrCode(boolean single) {
byte[] dtmp;
// 同一行只打印一个QRCode, 后面加换行
if (single) {
dtmp = new byte[9];
dtmp[8] = 0x0A;
} else {
dtmp = new byte[8];
}
dtmp[0] = 0x1D;
dtmp[1] = 0x28;
dtmp[2] = 0x6B;
dtmp[3] = 0x03;
dtmp[4] = 0x00;
dtmp[5] = 0x31;
dtmp[6] = 0x51;
dtmp[7] = 0x30;
return dtmp;
}
/**
* 二维码存入指令
*/
private static byte[] getQrCodeBytes(String code) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
byte[] d = code.getBytes("GB18030");
int len = d.length + 3;
if (len > 7092) {
len = 7092;
}
buffer.write((byte) 0x1D);
buffer.write((byte) 0x28);
buffer.write((byte) 0x6B);
buffer.write((byte) len);
buffer.write((byte) (len >> 8));
buffer.write((byte) 0x31);
buffer.write((byte) 0x50);
buffer.write((byte) 0x30);
for (int i = 0; i < d.length && i < len; i++) {
buffer.write(d[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
return buffer.toByteArray();
}
/**
* 设置字体大小
*/
public static byte[] setTextSize(int size) {
return fontSize(size).getBytes();
}
/**
* 字体的大小
*
* @param fontSize 0:正常大小 1:两倍高 2:两倍宽 3:两倍大小 4:三倍高 5:三倍宽 6:三倍大小
* 7:四倍高 8:四倍宽 9:四倍大小 10:五倍高 11:五倍宽 12:五倍大小
*/
public static String fontSize(int fontSize) {
String cmdstr = "";
// 设置字体大小
switch (fontSize) {
case -1:
// // 29 33
// cmdstr = new StringBuffer().append((char) 29).append((char) 33)
// .append((char) 0).toString();
// break;
case 0:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 0).toString();
break;
case 1:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 1).toString();
break;
case 2:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 16).toString();
break;
case 3:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 17).toString();
break;
case 4:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 2).toString();
break;
case 5:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 32).toString();
break;
case 6:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 34).toString();
break;
case 7:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 3).toString();
break;
case 8:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 48).toString();
break;
case 9:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 51).toString();
break;
case 10:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 4).toString();
break;
case 11:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 64).toString();
break;
case 12:
cmdstr = new StringBuffer().append((char) 29).append((char) 33)
.append((char) 68).toString();
break;
default:
break;
}
Logger.e("textSize", cmdstr);
return cmdstr;
}
}
public class BytesUtil {
/**
* 字节流转16进制字符串
*/
public static String getHexStringFromBytes(byte[] data) {
if (data == null || data.length <= 0) {
return null;
}
String hexString = "0123456789ABCDEF";
int size = data.length * 2;
StringBuilder sb = new StringBuilder(size);
for (int i = 0; i < data.length; i++) {
sb.append(hexString.charAt((data[i] & 0xF0) >> 4));
sb.append(hexString.charAt((data[i] & 0x0F) >> 0));
}
return sb.toString();
}
/**
* 单字符转字节
*/
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
/**
* 16进制字符串转字节数组
*/
@SuppressLint("DefaultLocale")
public static byte[] getBytesFromHexString(String hexString) {
if (hexString == null || "".equals(hexString)) {
return null;
}
hexString = hexString.replace(" ", "");
hexString = hexString.toUpperCase();
int size = hexString.length() / 2;
char[] hexArray = hexString.toCharArray();
byte[] rv = new byte[size];
for (int i = 0; i < size; i++) {
int pos = i * 2;
rv[i] = (byte) (charToByte(hexArray[pos]) << 4 | charToByte(hexArray[pos + 1]));
}
return rv;
}
/**
* 十进制字符串转字节数组
*/
@SuppressLint("DefaultLocale")
public static byte[] getBytesFromDecString(String decString) {
if (decString == null || "".equals(decString)) {
return null;
}
decString = decString.replace(" ", "");
int size = decString.length() / 2;
char[] decarray = decString.toCharArray();
byte[] rv = new byte[size];
for (int i = 0; i < size; i++) {
int pos = i * 2;
rv[i] = (byte) (charToByte(decarray[pos]) * 10 + charToByte(decarray[pos + 1]));
}
return rv;
}
/**
* 字节数组组合操作1
*/
public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) {
byte[] byte_3 = new byte[byte_1.length + byte_2.length];
System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
return byte_3;
}
/**
* 字节数组组合操作2
*/
public static byte[] byteMerger(byte[][] byteList) {
int length = 0;
// for (int i = 0; i < byteList.length; i++) {
// length += byteList[i].length;
// }
for (byte[] bytes : byteList) {
length += bytes.length;
}
byte[] result = new byte[length];
int index = 0;
for (int i = 0; i < byteList.length; i++) {
byte[] nowByte = byteList[i];
for (int k = 0; k < byteList[i].length; k++) {
result[index] = nowByte[k];
index++;
}
}
// for (int i = 0; i < index; i++) {
// // CommonUtils.LogWuwei("", "result[" + i + "] is " + result[i]);
// }
return result;
}
/**
* 生成表格字节流
*/
public static byte[] initTable(int h, int w) {
int hh = h * 32;
int ww = w * 4;
byte[] data = new byte[hh * ww + 5];
// xL
data[0] = (byte) ww;
// xH
data[1] = (byte) (ww >> 8);
data[2] = (byte) hh;
data[3] = (byte) (hh >> 8);
int k = 4;
int m = 31;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
data[k++] = (byte) 0xFF;
data[k++] = (byte) 0xFF;
data[k++] = (byte) 0xFF;
data[k++] = (byte) 0xFF;
}
if (i == h - 1) m = 30;
for (int t = 0; t < m; t++) {
for (int j = 0; j < w - 1; j++) {
data[k++] = (byte) 0x80;
data[k++] = (byte) 0;
data[k++] = (byte) 0;
data[k++] = (byte) 0;
}
data[k++] = (byte) 0x80;
data[k++] = (byte) 0;
data[k++] = (byte) 0;
data[k++] = (byte) 0x01;
}
}
for (int j = 0; j < w; j++) {
data[k++] = (byte) 0xFF;
data[k++] = (byte) 0xFF;
data[k++] = (byte) 0xFF;
data[k++] = (byte) 0xFF;
}
data[k++] = 0x0A;
return data;
}
/**
* 生成二维码字节流
*/
public static byte[] getZxingQrCode(String data, int size) {
try {
Hashtable<EncodeHintType, String> hints = new Hashtable<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(data, BarcodeFormat.QR_CODE, size, size, hints);
return getBytesFromBitMatrix(bitMatrix);
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
public static byte[] getBytesFromBitMatrix(BitMatrix bits) {
if (bits == null) {
return null;
}
int h = bits.getHeight();
int w = (bits.getWidth() + 7) / 8;
byte[] rv = new byte[h * w + 4];
// xL
rv[0] = (byte) w;
// xH
rv[1] = (byte) (w >> 8);
rv[2] = (byte) h;
rv[3] = (byte) (h >> 8);
int k = 4;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
for (int n = 0; n < 8; n++) {
byte b = getBitMatrixColor(bits, j * 8 + n, i);
rv[k] += rv[k] + b;
}
k++;
}
}
return rv;
}
private static byte getBitMatrixColor(BitMatrix bits, int x, int y) {
int width = bits.getWidth();
int height = bits.getHeight();
if (x >= width || y >= height || x < 0 || y < 0) return 0;
if (bits.get(x, y)) {
return 1;
} else {
return 0;
}
}
/**
* 将bitmap图转换为头四位有宽高的光栅位图
*/
public static byte[] getBytesFromBitMap(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int bw = (width - 1) / 8 + 1;
byte[] rv = new byte[height * bw + 4];
// xL
rv[0] = (byte) bw;
// xH
rv[1] = (byte) (bw >> 8);
rv[2] = (byte) height;
rv[3] = (byte) (height >> 8);
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int clr = pixels[width * i + j];
int red = (clr & 0x00ff0000) >> 16;
int green = (clr & 0x0000ff00) >> 8;
int blue = clr & 0x000000ff;
byte gray = (RGB2Gray(red, green, blue));
rv[(width * i + j) / 8 + 4] = (byte) (rv[(width * i + j) / 8 + 4] | (gray << (7 - ((width * i + j) % 8))));
}
}
return rv;
}
/**
* 将bitmap转成按mode指定的N点行数据
*/
public static byte[] getBytesFromBitMap(Bitmap bitmap, int mode) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
if (mode == 0 || mode == 1) {
byte[] res = new byte[width * height / 8 + 5 * height / 8];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 0; i < height / 8; i++) {
res[0 + i * (width + 5)] = 0x1b;
res[1 + i * (width + 5)] = 0x2a;
res[2 + i * (width + 5)] = (byte) mode;
res[3 + i * (width + 5)] = (byte) (width % 256);
res[4 + i * (width + 5)] = (byte) (width / 256);
for (int j = 0; j < width; j++) {
byte gray = 0;
for (int m = 0; m < 8; m++) {
int clr = pixels[j + width * (i * 8 + m)];
int red = (clr & 0x00ff0000) >> 16;
int green = (clr & 0x0000ff00) >> 8;
int blue = clr & 0x000000ff;
gray = (byte) ((RGB2Gray(red, green, blue) << (7 - m)) | gray);
}
res[5 + j + i * (width + 5)] = gray;
}
}
return res;
} else if (mode == 32 || mode == 33) {
byte[] res = new byte[width * height / 8 + 5 * height / 24];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 0; i < height / 24; i++) {
res[0 + i * (width * 3 + 5)] = 0x1b;
res[1 + i * (width * 3 + 5)] = 0x2a;
res[2 + i * (width * 3 + 5)] = (byte) mode;
res[3 + i * (width * 3 + 5)] = (byte) (width % 256);
res[4 + i * (width * 3 + 5)] = (byte) (width / 256);
for (int j = 0; j < width; j++) {
for (int n = 0; n < 3; n++) {
byte gray = 0;
for (int m = 0; m < 8; m++) {
int clr = pixels[j + width * (i * 24 + m + n * 8)];
int red = (clr & 0x00ff0000) >> 16;
int green = (clr & 0x0000ff00) >> 8;
int blue = clr & 0x000000ff;
gray = (byte) ((RGB2Gray(red, green, blue) << (7 - m)) | gray);
}
res[5 + j * 3 + i * (width * 3 + 5) + n] = gray;
}
}
}
return res;
} else {
return new byte[]{0x0A};
}
}
private static byte RGB2Gray(int r, int g, int b) {
return (false ? ((int) (0.29900 * r + 0.58700 * g + 0.11400 * b) > 200)
: ((int) (0.29900 * r + 0.58700 * g + 0.11400 * b) < 200)) ? (byte) 1 : (byte) 0;
}
/**
* 生成间断性黑块数据
*
* @param w : 打印纸宽度, 单位点
*/
public static byte[] initBlackBlock(int w) {
int ww = (w + 7) / 8;
int n = (ww + 11) / 12;
int hh = n * 24;
byte[] data = new byte[hh * ww + 5];
// xL
data[0] = (byte) ww;
// xH
data[1] = (byte) (ww >> 8);
data[2] = (byte) hh;
data[3] = (byte) (hh >> 8);
int k = 4;
for (int i = 0; i < n; i++) {
for (int j = 0; j < 24; j++) {
for (int m = 0; m < ww; m++) {
if (m / 12 == i) {
data[k++] = (byte) 0xFF;
} else {
data[k++] = 0;
}
}
}
}
data[k++] = 0x0A;
return data;
}
/**
* 生成一大块黑块数据
*
* @param h : 黑块高度, 单位点
* @param w : 黑块宽度, 单位点, 8的倍数
*/
public static byte[] initBlackBlock(int h, int w) {
int hh = h;
int ww = (w - 1) / 8 + 1;
byte[] data = new byte[hh * ww + 6];
// xL
data[0] = (byte) ww;
// xH
data[1] = (byte) (ww >> 8);
data[2] = (byte) hh;
data[3] = (byte) (hh >> 8);
int k = 4;
for (int i = 0; i < hh; i++) {
for (int j = 0; j < ww; j++) {
data[k++] = (byte) 0xFF;
}
}
data[k++] = 0x00;
data[k++] = 0x00;
return data;
}
/**
* 基本覆盖热敏小票打印中所有epson指令的操作
*/
public static byte[] customData() {
byte[] rv = new byte[]{
(byte) 0xB4, (byte) 0xF2, (byte) 0xD3, (byte) 0xA1, (byte) 0xBB, (byte) 0xFA, (byte) 0xD7, (byte) 0xD4, (byte) 0xBC, (byte) 0xEC, 0x0A,
// 打印机自检
0x1F, 0X1B, 0x1F, 0x53,
// 初始化打印机
0x1B, 0x40,
// 分割线---
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A,
(byte) 0xB8, (byte) 0xC4, (byte) 0xB1, (byte) 0xE4, (byte) 0xD7, (byte) 0xD6, (byte) 0xBC, (byte) 0xE4, (byte) 0xBE, (byte) 0xE0, 0x0A,
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A,
// 设置字符右间距
0x30, 0x2E, 0x30, 0x3A, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, 0x0A,
0x30, 0x2E, 0x35, 0x3A, 0x1B, 0x20, 0x0C, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, 0x0A,
0x1B, 0x20, 0x00,
0x31, 0x2E, 0x30, 0x3A, 0x1B, 0x20, 0x18, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, 0x0A,
0x1B, 0x20, 0x00,
0x32, 0x2E, 0x30, 0x3A, 0x1B, 0x20, 0x30, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, (byte) 0xB2, (byte) 0xE2, (byte) 0xCA, (byte) 0xD4, 0x0A,
0x1B, 0x20, 0x00,
// 分割线===
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x0A,
(byte) 0xD7, (byte) 0xD6, (byte) 0xCC, (byte) 0xE5, (byte) 0xD0, (byte) 0xA7, (byte) 0xB9, (byte) 0xFB, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, 0x0A,
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x0A,
// 字体效果 商米科技
// 设置加粗、倍高、倍宽、下划线、反白
0x1B, 0x21, 0x08, (byte) 0xBC, (byte) 0xD3, (byte) 0xB4, (byte) 0xD6, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, 0x0A,
0x1B, 0x21, 0x00,
0x1B, 0x45, 0x01, (byte) 0xBC, (byte) 0xD3, (byte) 0xB4, (byte) 0xD6, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, 0x0A,
0x1B, 0x45, 0x00,
0x1B, 0x21, 0x10, (byte) 0xB1, (byte) 0xB6, (byte) 0xB8, (byte) 0xDF, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, 0x0A,
0x1B, 0x21, 0x20, (byte) 0xB1, (byte) 0xB6, (byte) 0xBF, (byte) 0xED, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, 0x0A,
0x1D, 0x21, 0x11, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC,
0x1D, 0x21, 0x22, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, 0x0A,
0x1D, 0x21, 0x00,
0x1B, 0x21, (byte) 0x80, (byte) 0xCF, (byte) 0xC2, (byte) 0xBB, (byte) 0xAE, (byte) 0xCF, (byte) 0xDF, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, 0x0A,
0x1B, 0x21, 0x00,
0x1B, 0x2D, 0x01, (byte) 0xCF, (byte) 0xC2, (byte) 0xBB, (byte) 0xAE, (byte) 0xCF, (byte) 0xDF, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, 0x0A,
0x1B, 0x2D, 0x00,
0x1D, 0x42, 0x01, 0x1B, 0x21, 0x08, (byte) 0xB7, (byte) 0xB4, (byte) 0xB0, (byte) 0xD7, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC, 0x0A,
0x1D, 0x42, 0x00, 0x1B, 0x21, 0x00,
// 分割线***
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x0A,
(byte) 0xC5, (byte) 0xC5, (byte) 0xB0, (byte) 0xE6, (byte) 0xCE, (byte) 0xBB, (byte) 0xD6, (byte) 0xC3, 0x0A,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x0A,
// 排版位置
// 设置绝对位置和设置相对位置
(byte) 0xD5, (byte) 0xE2, (byte) 0xBE, (byte) 0xE4, (byte) 0xBB, (byte) 0xB0, (byte) 0xB4, (byte) 0xD3, (byte) 0xD0, (byte) 0xD0, (byte) 0xCA, (byte) 0xD7, (byte) 0xBF, (byte) 0xAA, (byte) 0xCA, (byte) 0xBC, 0x0A,
(byte) 0xBE, (byte) 0xF8, (byte) 0xB6, (byte) 0xD4, (byte) 0xCE, (byte) 0xBB, (byte) 0xD6, (byte) 0xC3, 0x1B, 0x24, (byte) 0xC0, 0x00, (byte) 0xC6, (byte) 0xAB, (byte) 0xD2, (byte) 0xC6, 0x38, (byte) 0xB8, (byte) 0xF6, (byte) 0xD7, (byte) 0xD6, 0x0A,
(byte) 0xCF, (byte) 0xE0, (byte) 0xB6, (byte) 0xD4, (byte) 0xCE, (byte) 0xBB, (byte) 0xD6, (byte) 0xC3, 0x1B, 0x5C, 0x30, 0x00, (byte) 0xC6, (byte) 0xAB, (byte) 0xD2, (byte) 0xC6, 0x32, (byte) 0xB8, (byte) 0xF6, (byte) 0xD7, (byte) 0xD6, 0x0A,
//设置对齐方式
(byte) 0xBE, (byte) 0xD3, (byte) 0xD7, (byte) 0xF3, 0x0A,
0x1B, 0x61, 0x01, (byte) 0xBE, (byte) 0xD3, (byte) 0xD6, (byte) 0xD0, 0x0A,
0x1B, 0x61, 0x02, (byte) 0xBE, (byte) 0xD3, (byte) 0xD3, (byte) 0xD2, 0x0A,
0x1B, 0x61, 0x00,
// 设置左边距
0x1D, 0x4C, 0x30, 0x00,
(byte) 0xC9, (byte) 0xE8, (byte) 0xD6, (byte) 0xC3, (byte) 0xD7, (byte) 0xF3, (byte) 0xB1, (byte) 0xDF, (byte) 0xBE, (byte) 0xE0, 0x34, 0x38, (byte) 0xCF, (byte) 0xF1, (byte) 0xCB, (byte) 0xD8, 0x0A,
// 设置打印区域宽度
0x1D, 0x57, (byte) 0xF0, 0x00,
(byte) 0xB8, (byte) 0xC4, (byte) 0xB1, (byte) 0xE4, (byte) 0xBF, (byte) 0xC9, (byte) 0xB4, (byte) 0xF2, (byte) 0xD3, (byte) 0xA1, (byte) 0xC7, (byte) 0xF8, (byte) 0xD3, (byte) 0xF2, (byte) 0xCE, (byte) 0xAA, 0x32, 0x34, 0x30, (byte) 0xCF, (byte) 0xF1, (byte) 0xCB, (byte) 0xD8, 0x0A,
// 设置左边距
0x1D, 0x4C, 0x60, 0x00,
(byte) 0xC9, (byte) 0xE8, (byte) 0xD6, (byte) 0xC3, (byte) 0xD7, (byte) 0xF3, (byte) 0xB1, (byte) 0xDF, (byte) 0xBE, (byte) 0xE0, 0x39, 0x36, (byte) 0xCF, (byte) 0xF1, (byte) 0xCB, (byte) 0xD8, 0x0A,
// 设置打印区域宽度
0x1D, 0x57, (byte) 0x78, 0x00,
(byte) 0xB8, (byte) 0xC4, (byte) 0xB1, (byte) 0xE4, (byte) 0xBF, (byte) 0xC9, (byte) 0xB4, (byte) 0xF2, (byte) 0xD3, (byte) 0xA1, (byte) 0xC7, (byte) 0xF8, (byte) 0xD3, (byte) 0xF2, (byte) 0xCE, (byte) 0xAA, 0x31, 0x32, 0x30, (byte) 0xCF, (byte) 0xF1, (byte) 0xCB, (byte) 0xD8, 0x0A,
0x1D, 0x4C, 0x00, 0x00,
0x1D, 0x57, (byte) 0x80, 0x01,
// 水平制表符-跳格
(byte) 0xC4, (byte) 0xAC, 0x09, (byte) 0xC8, (byte) 0xCF, 0x09, (byte) 0xCC, (byte) 0xF8, 0x09, (byte) 0xB8, (byte) 0xF1, 0x09, 0x0A,
0x1B, 0x44, 0x01, 0x02, 0x04, 0x08, 0x0A, 0x00,
(byte) 0xD7, (byte) 0xD4, 0x09, (byte) 0xB6, (byte) 0xA8, 0x09, (byte) 0xD2, (byte) 0xE5, 0x09, (byte) 0xCC, (byte) 0xF8, 0x09, (byte) 0xB8, (byte) 0xF1, 0x09, 0x0A,
// 设置行高
0x1B, 0x33, 0x60, (byte) 0xC9, (byte) 0xE8, (byte) 0xD6, (byte) 0xC3, (byte) 0xD0, (byte) 0xD0, (byte) 0xB8, (byte) 0xDF, 0x3A, 0x39, 0x36, (byte) 0xB5, (byte) 0xE3, (byte) 0xD0, (byte) 0xD0, 0x0A,
0x1B, 0x33, 0x40, (byte) 0xC9, (byte) 0xE8, (byte) 0xD6, (byte) 0xC3, (byte) 0xD0, (byte) 0xD0, (byte) 0xB8, (byte) 0xDF, 0x3A, 0x36, 0x34, (byte) 0xB5, (byte) 0xE3, (byte) 0xD0, (byte) 0xD0, 0x0A,
0x1B, 0x33, 0x00, (byte) 0xC9, (byte) 0xE8, (byte) 0xD6, (byte) 0xC3, (byte) 0xD0, (byte) 0xD0, (byte) 0xB8, (byte) 0xDF, 0x3A, 0x30, (byte) 0xB5, (byte) 0xE3, (byte) 0xD0, (byte) 0xD0, 0x0A,
0x1B, 0x32, (byte) 0xC4, (byte) 0xAC, (byte) 0xC8, (byte) 0xCF, (byte) 0xD0, (byte) 0xD0, (byte) 0xB8, (byte) 0xDF, 0x0A,
// 分割线---
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A,
(byte) 0xD6, (byte) 0xAE, (byte) 0xBA, (byte) 0xF3, (byte) 0xBD, (byte) 0xAB, (byte) 0xD7, (byte) 0xDF, (byte) 0xD6, (byte) 0xBD, 0x0A,
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A,
// 打印并走纸
0x1B, 0x4A, 0x40, (byte) 0xD7, (byte) 0xDF, (byte) 0xD6, (byte) 0xBD, 0x36, 0x34, (byte) 0xB5, (byte) 0xE3, (byte) 0xD0, (byte) 0xD0, 0x0A,
0x1B, 0x4A, 0x60, (byte) 0xD7, (byte) 0xDF, (byte) 0xD6, (byte) 0xBD, 0x39, 0x36, (byte) 0xB5, (byte) 0xE3, (byte) 0xD0, (byte) 0xD0, 0x0A,
0x1B, 0x64, 0x0A, (byte) 0xD7, (byte) 0xDF, (byte) 0xD6, (byte) 0xBD, 0x31, 0x30, (byte) 0xD0, (byte) 0xD0, 0x0A,
0x1B, 0x64, 0x01, (byte) 0xD7, (byte) 0xDF, (byte) 0xD6, (byte) 0xBD, 0x31, (byte) 0xD0, (byte) 0xD0, 0x0A,
// 分割线===
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x0A,
(byte) 0xB4, (byte) 0xF2, (byte) 0xD3, (byte) 0xA1, (byte) 0xCC, (byte) 0xF5, (byte) 0xC2, (byte) 0xEB, 0x0A,
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x0A,
// 打印条码
0x1D, 0x48, 0x02,
// upca
0x1B, 0x61, 0x00, 0x1D, 0x68, 0x20, 0x1D, 0x77, 0x02,
0x1D, 0x6B, 0x41, 0x0c, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x0A,
0x1D, 0x6B, 0x00, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x00, 0x0A,
// upce
0x1B, 0x61, 0x01, 0x1D, 0x68, 0x40, 0x1D, 0x77, 0x04,
0x1D, 0x6B, 0x42, 0x08, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x0A,
0x1D, 0x6B, 0x01, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x00, 0x0A,
// ean13
0x1B, 0x61, 0x02, 0x1D, 0x68, 0x60, 0x1D, 0x77, 0x02,
0x1D, 0x6B, 0x43, 0x0D, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x0A,
0x1D, 0x6B, 0x02, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x00, 0x0A,
// ean8
0x1B, 0x61, 0x00, 0x1D, 0x68, (byte) 0x80, 0x1D, 0x77, 0x05,
0x1D, 0x6B, 0x44, 0x08, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x0A,
0x1D, 0x6B, 0x03, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x00, 0x0A,
// code39
0x1B, 0x61, 0x01, 0x1D, 0x68, (byte) 0xA0, 0x1D, 0x77, 0x02,
0x1D, 0x6B, 0x45, 0x0A, 0x33, 0x36, 0x39, 0x53, 0x55, 0x4E, 0x4D, 0x49, 0x25, 0x24, 0x0A,
0x1D, 0x6B, 0x04, 0x33, 0x36, 0x39, 0x53, 0x55, 0x4E, 0x4D, 0x49, 0x25, 0x24, 0x00, 0x0A,
// itf
0x1B, 0x61, 0x02, 0x1D, 0x68, (byte) 0xC0, 0x1D, 0x77, 0x03,
0x1D, 0x6B, 0x46, 0x0C, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x0A,
0x1D, 0x6B, 0x05, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x00, 0x0A,
// codebar
0x1B, 0x61, 0x00, 0x1D, 0x68, (byte) 0xE0, 0x1D, 0x77, 0x03,
0x1D, 0x6B, 0x47, 0x0A, 0x41, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x41, 0x0A,
// 0x1D, 0x6B, 0x06, 0x41, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x41, 0x00, 0x0A,
// code93
0x1B, 0x61, 0x01, 0x1D, 0x68, (byte) 0xFF, 0x1D, 0x77, 0x02,
0x1D, 0x6B, 0x48, 0x0C, 0x53, 0x55, 0x4E, 0x4D, 0x49, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x0A,
// code128
0x1B, 0x61, 0x00, 0x1D, 0x68, (byte) 0xB0, 0x1D, 0x77, 0x02,
0x1D, 0x6B, 0x49, 0x0A, 0x7B, 0x41, 0x53, 0x55, 0x4E, 0x4D, 0x49, 0x30, 0x31, 0x32, 0x0A,
0x1D, 0x6B, 0x49, 0x0C, 0x7B, 0x42, 0x53, 0x55, 0x4E, 0x4D, 0x49, 0x73, 0x75, 0x6E, 0x6D, 0x69, 0x0A,
0x1D, 0x6B, 0x49, 0x0B, 0x7B, 0x43, 0x01, 0xc, 0x17, 0x22, 0x2d, 0x38, 0x4e, 0x59, 0x5a, 0x0A,
// 分割线***
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x0A,
(byte) 0xB4, (byte) 0xF2, (byte) 0xD3, (byte) 0xA1, (byte) 0xB6, (byte) 0xFE, (byte) 0xCE, (byte) 0xAC, (byte) 0xC2, (byte) 0xEB, 0x0A,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x0A,
// 打印二维码
0x1B, 0x61, 0x01,
0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x43, 0x09,
0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x45, 0x32,
0x1D, 0x28, 0x6B, 0x0B, 0x00, 0x31, 0x50, 0x30, (byte) 0xC9, (byte) 0xCC, (byte) 0xC3, (byte) 0xD7, (byte) 0xBF, (byte) 0xC6, (byte) 0xBC, (byte) 0xBC,
0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x51, 0x30, 0x0A,
0x1B, 0x61, 0x00,
// 分割线--- (之后将实现打印光栅位图的方法,再次之前使用1b 61 01 居中)
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A,
(byte) 0xB4, (byte) 0xF2, (byte) 0xD3, (byte) 0xA1, (byte) 0xB9, (byte) 0xE2, (byte) 0xD5, (byte) 0xA4, (byte) 0xCD, (byte) 0xBC, (byte) 0xCF, (byte) 0xF1, 0x0A,
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A,
0x1B, 0x61, 0x01,
};
return rv;
}
/**
* 部分字符打印的分割线标志
*/
public static byte[] wordData() {
byte[] bytes = new byte[]{
// 分割线===
0x1B, 0x61, 0x00,
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x0A,
(byte) 0xD7, (byte) 0xD6, (byte) 0xB7, (byte) 0xFB, (byte) 0xBC, (byte) 0xAF, (byte) 0xC9, (byte) 0xE8, (byte) 0xD6, (byte) 0xC3, 0x0A,
0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x0A,
0x1C, 0x26, 0x1C, 0x43, 0x00,
};
return bytes;
}
}
上述的代码有生成二维码相关的使用的是Zxing,如果需要打印二维码需要添加依赖:
implementation 'com.google.zxing:core:3.3.3'
如果不需要打印二维码,可以把二维码报红相关的代码注释掉即可。
顺便把日志输出类也贴出来吧
object Logger {
var LOG_ENABLE = true
private const val VERBOSE = 1
private const val DEBUG = 2
private const val INFO = 3
private const val WARN = 4
private const val ERROR = 5
private var level = VERBOSE
fun v(tag: String, msg: String) {
if (LOG_ENABLE && level <= VERBOSE) {
Log.v(tag, msg)
}
}
fun d(tag: String, msg: String) {
if (LOG_ENABLE && level <= DEBUG) {
Log.d(tag, msg)
}
}
fun i(tag: String, msg: String) {
if (LOG_ENABLE && level <= INFO) {
Log.i(tag, msg)
}
}
fun w(tag: String, msg: String) {
if (LOG_ENABLE && level <= WARN) {
Log.w(tag, msg)
}
}
@JvmStatic
fun e(tag: String, msg: String) {
if (LOG_ENABLE && level <= ERROR) {
Log.e(tag, msg)
}
}
fun e(tag: String, msg: String, tr: Throwable) {
if (LOG_ENABLE && level <= ERROR) {
Log.e(tag, msg, tr)
}
}
}
二、模拟打印数据对应的Bean对象
public class Order implements Serializable {
private String orderNo;
private OrderContent orderConsumePO;
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public OrderContent getOrderConsumePO() {
return orderConsumePO;
}
public void setOrderConsumePO(OrderContent orderConsumePO) {
this.orderConsumePO = orderConsumePO;
}
}
public class OrderContent implements Serializable {
private Long id;
private Long createDate;
private Integer version;
private Long totalAmount;
private Long discountAmount;
private String remarks;
private List<OrderItem> items;
private String waiterName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getCreateDate() {
return createDate;
}
public void setCreateDate(Long createDate) {
this.createDate = createDate;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public Long getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(Long totalAmount) {
this.totalAmount = totalAmount;
}
public Long getDiscountAmount() {
return discountAmount;
}
public void setDiscountAmount(Long discountAmount) {
this.discountAmount = discountAmount;
}
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
public List<OrderItem> getItems() {
return items;
}
public void setItems(List<OrderItem> items) {
this.items = items;
}
public String getWaiterName() {
return waiterName;
}
public void setWaiterName(String waiterName) {
this.waiterName = waiterName;
}
}
public class OrderItem implements Serializable {
private Long id;
private String skuName;
private Long price;
private Double totalCount;
private String skuSpec;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSkuName() {
return skuName;
}
public void setSkuName(String skuName) {
this.skuName = skuName;
}
public Long getPrice() {
return price;
}
public void setPrice(Long price) {
this.price = price;
}
public Double getTotalCount() {
return totalCount;
}
public void setTotalCount(Double totalCount) {
this.totalCount = totalCount;
}
public String getSkuSpec() {
return skuSpec;
}
public void setSkuSpec(String skuSpec) {
this.skuSpec = skuSpec;
}
}
三、调试好的排版格式数据打印类
class UsbPrintClient(private val usbPrinter: UsbPrinter, private val usbPrintData: UsbPrintData, private val printNumber: Int = 1) {
@SuppressLint("SimpleDateFormat")
private val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
private var number = 1;
private var order: Order = Gson().fromJson(usbPrintData.data, Order::class.java)
companion object {
// 58mm
private const val NAME_WIDTH_58 = 10
private const val NUMBER_WIDTH_58 = 7
private const val PRICE_WIDTH_58 = 7
private const val SALE_PRICE_WIDTH_58 = 8
// 80mm
private const val NAME_WIDTH_80 = 20
private const val NUMBER_WIDTH_80 = 9
private const val PRICE_WIDTH_80 = 9
private const val SALE_PRICE_WIDTH_80 = 10
}
fun usbToPrint() {
thread {
// 设置加粗
usbPrinter.bold(true)
// 文字大小
usbPrinter.setTextSize(3)
// 设置文字居中
usbPrinter.setAlign(1)
// 打印文字并换行
usbPrinter.printTextNewLine("结账单")
// 换2行
usbPrinter.printLine(2)
// 取消加粗
usbPrinter.bold(false)
usbPrinter.setTextSize(0)
// 虚线
printDashed(usbPrinter)
// 设置文字居左对齐
usbPrinter.setAlign(0)
val orderNo = order.orderNo
usbPrinter.printTextNewLine("流水号:" + orderNo.substring(orderNo.length - 5))
usbPrinter.printTextNewLine("单号:$orderNo")
usbPrinter.printTextNewLine("结账时间:" + sdf.format(Date(order.orderConsumePO.createDate)))
printDashed(usbPrinter)
usbPrinter.bold(true)
// 表头
printOrderItemTitle(usbPrinter)
usbPrinter.bold(false)
printDashed(usbPrinter)
// 商品
val orderItemList = order.orderConsumePO.items
val size = orderItemList.size
for (pos in orderItemList.indices) {
printOrderItem(usbPrinter, orderItemList[pos], pos, size, UsbPrintData.WIDTH_80 == usbPrintData.printType)
}
// 整单备注
if (order.orderConsumePO.remarks != null && "" != order.orderConsumePO.remarks) {
printDashed(usbPrinter)
usbPrinter.printTextNewLine("备注:" + order.orderConsumePO.remarks)
}
printDashed(usbPrinter)
// 设置文字居右对齐
usbPrinter.setAlign(2)
usbPrinter.printTextNewLine("订单金额:" + changeF2Y(order.orderConsumePO.totalAmount) + "元")
// 优惠金额
if (order.orderConsumePO.discountAmount != 0L) {
usbPrinter.printTextNewLine("优惠金额:" + changeF2Y(order.orderConsumePO.discountAmount) + "元")
}
// 支付金额
usbPrinter.printTextNewLine("支付金额:" + changeF2Y(order.orderConsumePO.totalAmount - order.orderConsumePO.discountAmount) + "元")
printDashed(usbPrinter)
// 收银员
usbPrinter.setAlign(0)
usbPrinter.printTextNewLine("收银员:" + order.orderConsumePO.waiterName)
// 打印时间
usbPrinter.printTextNewLine("打印时间:" + sdf.format(Date()))
usbPrinter.printLine(6)
// 切纸
usbPrinter.cutPager()
if (number != printNumber) {
number += 1
usbToPrint()
}
}
}
/**
* 打印分割线
*/
private fun printDashed(usbPrinter: UsbPrinter) {
if (UsbPrintData.WIDTH_80 == usbPrintData.printType) {
usbPrinter.printTextNewLine("------------------------------------------------")
} else {
usbPrinter.printTextNewLine("-------------------------------")
}
}
/**
* 打印表头
*/
private fun printOrderItemTitle(usbPrinter: UsbPrinter) {
if (UsbPrintData.WIDTH_80 == usbPrintData.printType) { // 80mm
usbPrinter.printTextNewLine("商品" + getSpace(16) // 20
+ getSpace(5) // 5
+ "数量" // 4
+ getSpace(5) // 5
+ "单价" // 4
+ getSpace(6) + "小计")
} else {
// 58mm
usbPrinter.printTextNewLine("商品" + getSpace(6) // 10
+ getSpace(3) // 3
+ "数量" // 4
+ getSpace(3) // 3
+ "单价" // 4
+ getSpace(4) + "小计") // 8
}
}
/**
* 打印具体的商品信息
*/
private fun printOrderItem(usbPrinter: UsbPrinter, orderItem: OrderItem, position: Int, size: Int, type: Boolean) {
val nameWidthTotal = if (type) NAME_WIDTH_80 else NAME_WIDTH_58
val numberWidthTotal = if (type) NUMBER_WIDTH_80 else NUMBER_WIDTH_58
val priceWidthTotal = if (type) PRICE_WIDTH_80 else PRICE_WIDTH_58
val salePriceWidthTotal = if (type) SALE_PRICE_WIDTH_80 else SALE_PRICE_WIDTH_58
// 名称
val productName = if (orderItem.skuSpec == null) orderItem.skuName else orderItem.skuName + "(" + orderItem.skuSpec + ")"
val nameStrLines: MutableList<String> = ArrayList()
val productNameChars = productName.toCharArray()
// 遍历字符
for (c in productNameChars) {
// 当前字符
val str = c.toString()
// 当前字符的宽度
val strWidth = usbPrinter.getStringWidth(c.toString())
// 如果行数据是空的,直接加
if (nameStrLines.isEmpty()) {
nameStrLines.add(str)
} else {
// 获取最后一行字符串
var lastLineStr = nameStrLines[nameStrLines.size - 1]
// 最后一场字符串宽度
val lastLineStrWidth = usbPrinter.getStringWidth(lastLineStr)
// 宽度小于等于10,直接加入该行
if (lastLineStrWidth + strWidth <= nameWidthTotal) {
lastLineStr += str
nameStrLines.removeAt(nameStrLines.size - 1)
nameStrLines.add(lastLineStr)
} else {
nameStrLines.add(str)
}
}
}
// 打印名称第一行
val nameFirstLine = nameStrLines[0]
val nameFirstLineWidth = usbPrinter.getStringWidth(nameFirstLine + "")
val nameFirstLineSpaceWidth = if (nameFirstLineWidth < nameWidthTotal) nameWidthTotal - nameFirstLineWidth else 0
val nameFirstLineSpace = getSpace(nameFirstLineSpaceWidth)
usbPrinter.printText(nameFirstLine)
usbPrinter.printText(nameFirstLineSpace)
// 数量
val number = orderItem.totalCount
val numberWidth = usbPrinter.getStringWidth(number.toString() + "")
val numberSpaceWidth = if (numberWidth < numberWidthTotal) numberWidthTotal - numberWidth else 0
val numberSpace = getSpace(numberSpaceWidth)
usbPrinter.printText(numberSpace)
usbPrinter.printText(number.toString() + "")
// 单价
val originalPrice = changeF2Y(orderItem.price)
val originalPriceWidth = usbPrinter.getStringWidth(originalPrice + "")
val originalPriceSpaceWidth = if (originalPriceWidth < priceWidthTotal) priceWidthTotal - originalPriceWidth else 0
val originalPriceSpace = getSpace(originalPriceSpaceWidth)
usbPrinter.printText(originalPriceSpace)
usbPrinter.printText(originalPrice + "")
// 小计
val salePrice = changeF2Y(java.lang.Double.valueOf(orderItem.price * orderItem.totalCount).toLong())
val salePriceWidth = usbPrinter.getStringWidth(salePrice + "")
val salePriceSpaceWidth = if (salePriceWidth < salePriceWidthTotal) salePriceWidthTotal - salePriceWidth else 0
val salePriceSpace = getSpace(salePriceSpaceWidth)
usbPrinter.printText(salePriceSpace)
usbPrinter.printText(salePrice + "")
if (position != size - 1) {
usbPrinter.printTextNewLine("")
}
// usbPrinter.printLine();
// 名称剩余行
if (nameStrLines.size > 1) {
for (i in 1 until nameStrLines.size) {
val nameLine = nameStrLines[i]
usbPrinter.printText(nameLine)
if (position != size - 1) {
usbPrinter.printTextNewLine("")
}
// usbPrinter.printLine();
}
}
}
/**
* 获取指定数量的空字符串
*/
private fun getSpace(num: Int): String {
if (num == 0) {
return ""
}
val sb = StringBuilder()
for (i in 0 until num) {
sb.append(" ")
}
return sb.toString()
}
/**
* 分转元
*/
private fun changeF2Y(price: Long?): String? {
return BigDecimal.valueOf(java.lang.Long.valueOf(price!!)).divide(BigDecimal(100)).toString()
}
}
上面解析数据使用到了的Gson,这个就自行添加吧,另外打印数据操作建议在子线程中执行。
辅助打印相关参数的数据存放类:
class UsbPrintData {
var data: String? = null
var dataType: String? = null
var printType: String? = null
companion object {
/**
* 结账单
*/
const val TYPE_JZD = "1"
/**
* VIP
*/
const val TYPE_VIP = "2"
/**
* 打印纸的宽度
*/
const val WIDTH_58 = "58"
const val WIDTH_80 = "80"
}
}
四、在Activity中的使用
class PrintTestActivity : BaseActivity(), View.OnClickListener {
private lateinit var activityPrintTestBinding: ActivityPrintTestBinding
private lateinit var usbPrinter: UsbPrinter
companion object {
private const val TAG = "PrintTestActivity"
fun actionStart(activity: FragmentActivity, isPutStack: Boolean) {
startActivity<PrintTestActivity>(activity, isPutStack)
}
}
override fun getLayoutView(): View {
activityPrintTestBinding = ActivityPrintTestBinding.inflate(layoutInflater)
return activityPrintTestBinding.root
}
override fun initView() {
activityPrintTestBinding.btnTestUsbPrint.setOnClickListener(this)
}
override fun initData() {
// 初始化usb串口的连接
usbPrinter = UsbPrinter.getInstance()
usbPrinter.initPrinter(this)
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btn_test_usb_print -> {
// 模拟打印数据
val order = Order()
order.orderNo = "2021082400006"
order.orderConsumePO = OrderContent()
val orderContent = order.orderConsumePO
orderContent.id = 343
orderContent.createDate = System.currentTimeMillis()
orderContent.totalAmount = 3500
orderContent.discountAmount = 200
orderContent.remarks = "少盐,少辣"
orderContent.waiterName = "测试人员"
val list: MutableList<OrderItem> = ArrayList()
orderContent.items = list
val orderItemNew1 = OrderItem()
orderItemNew1.id = 809
orderItemNew1.skuName = "面皮"
orderItemNew1.price = 500
orderItemNew1.totalCount = 1.0
orderItemNew1.skuSpec = "小份"
orderContent.items.add(orderItemNew1)
val orderItemNew2 = OrderItem()
orderItemNew2.id = 811
orderItemNew2.skuName = "重庆万州烤鱼"
orderItemNew2.price = 1500
orderItemNew2.totalCount = 2.0
// orderItem2.skuSpec = "中份"
orderContent.items.add(orderItemNew2)
val printData = Gson().toJson(order)
Logger.e(TAG, "onClick: json= $printData")
// 设置打印数据参数
val usbPrintData = UsbPrintData()
// 打印的数据
usbPrintData.data = printData
// 打印的类型 实际情况中可能不止打印小票 还可能会打印会员充值等等其它类型的小票
usbPrintData.dataType = UsbPrintData.TYPE_JZD
// 设置打印纸的宽度(80mm/58mm)
usbPrintData.printType = UsbPrintData.WIDTH_58
// 设置打印的数据及打印的份数
val usbPrintClient = UsbPrintClient(usbPrinter, usbPrintData,2)
// 执行打印
usbPrintClient.usbToPrint()
}
}
}
override fun onDestroy() {
super.onDestroy()
usbPrinter.close()
}
}
大家可以直接使用AppCompatActivity,然后在onCreate()方法中去初始化打印机USB串口的连接。
好了,全部代码都已经贴出来完了,关于打印数据的排版格式大家可以根据自己的需要做调整,上面的格式是我们用到的排版格式,如使用中遇到什么问题,可以给我留言哈。