本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Android如何控制打印机
项目背景
疫情下,大部分市民都拒绝隔离,国内外卖送单更流行,公司随大流开展外卖模块,手机点单,商铺接单,打印,骑手接单导航等一系列app倒是一个不差
哎,东南亚小国,大家还是不太习惯外卖,更喜欢堂食。项目大概运营了半年? 目前已经死翘翘了,只剩下骑手还在苟延残喘,要不然也轮不到我在此分享了。
这里先讲一下商铺端 打印机的控制,一般这种打印外卖订单的打印机 分为一体机和蓝牙打印机。
一体机,内部自带的打印机,内部使用串口通信,传输byte[]数据,然后控制打印。
蓝牙设备,需要Android设备连接蓝牙,使用蓝牙通信,传输byte[]数据,然后控制打印。
一般厂商会提供控制打印的SDK,如我们购买的Sunmi的打印机。
implementation 'com.sunmi:printerlibrary:1.0.7'
SDK内部有串口通信的逻辑,传输数据的逻辑是一样的。这里先看看蓝牙连接的代码:
public class BluetoothUtil {
private static final UUID PRINTER_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final String Innerprinter_Address = "00:11:22:33:44:55";
public static boolean isBlueToothPrinter = false;
private static BluetoothSocket bluetoothSocket;
private static BluetoothAdapter getBTAdapter() {
return BluetoothAdapter.getDefaultAdapter();
}
private static BluetoothDevice getDevice(BluetoothAdapter bluetoothAdapter) {
BluetoothDevice innerprinter_device = null;
Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();
for (BluetoothDevice device : devices) {
if (device.getAddress().equals(Innerprinter_Address)) {
innerprinter_device = device;
break;
}
}
return innerprinter_device;
}
private static BluetoothSocket getSocket(BluetoothDevice device) throws IOException {
BluetoothSocket socket;
socket = device.createRfcommSocketToServiceRecord(PRINTER_UUID);
socket.connect();
return socket;
}
/**
* connect bluetooth
*/
public static boolean connectBlueTooth(Context context) {
if (bluetoothSocket == null) {
if (getBTAdapter() == null) {
Toast.makeText(context, R.string.toast_3, Toast.LENGTH_SHORT).show();
return false;
}
if (!getBTAdapter().isEnabled()) {
Toast.makeText(context, R.string.toast_4, Toast.LENGTH_SHORT).show();
return false;
}
BluetoothDevice device;
if ((device = getDevice(getBTAdapter())) == null) {
Toast.makeText(context, R.string.toast_5, Toast.LENGTH_SHORT).show();
return false;
}
try {
bluetoothSocket = getSocket(device);
} catch (IOException e) {
Toast.makeText(context, R.string.toast_6, Toast.LENGTH_SHORT).show();
return false;
}
}
return true;
}
/**
* disconnect bluethooth
*/
public static void disconnectBlueTooth(Context context) {
if (bluetoothSocket != null) {
try {
OutputStream out = bluetoothSocket.getOutputStream();
out.close();
bluetoothSocket.close();
bluetoothSocket = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* send esc cmd
*/
public static void sendData(byte[] bytes) {
if (bluetoothSocket != null) {
OutputStream out = null;
try {
out = bluetoothSocket.getOutputStream();
out.write(bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
}else{
}
}
}
打印服务Byte数据处理工具类:
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 || hexstring.equals("")){
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 || decstring.equals("")){
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;
}
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];
data[0] = (byte)ww;//xL
data[1] = (byte)(ww >> 8);//xH
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;
}
/**
* 将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];
rv[0] = (byte) bw;//xL
rv[1] = (byte) (bw >> 8);//xH
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[bw*i + j/8 + 4] = (byte) (rv[bw*i + j/8 + 4] | (gray << (7 - 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 : 打印纸宽度, 单位点
* @return
*/
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];
data[0] = (byte)ww;//xL
data[1] = (byte)(ww >> 8);//xH
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的倍数
* @return
*/
public static byte[] initBlackBlock(int h, int w){
int hh = h;
int ww = (w - 1)/8 + 1;
byte[] data = new byte[ hh * ww + 6];
data[0] = (byte)ww;//xL
data[1] = (byte)(ww >> 8);//xH
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;
}
}
打印服务的入口与控制类:
public class SunmiPrintHelper {
public static int NoSunmiPrinter = 0x00000000;
public static int CheckSunmiPrinter = 0x00000001;
public static int FoundSunmiPrinter = 0x00000002;
public static int LostSunmiPrinter = 0x00000003;
/**
* sunmiPrinter means checking the printer connection status
*/
public int sunmiPrinter = CheckSunmiPrinter;
/**
* SunmiPrinterService for API
*/
private SunmiPrinterService sunmiPrinterService;
private static SunmiPrintHelper helper = new SunmiPrintHelper();
private SunmiPrintHelper() {
}
public static SunmiPrintHelper getInstance() {
return helper;
}
private InnerPrinterCallback innerPrinterCallback = new InnerPrinterCallback() {
@Override
protected void onConnected(SunmiPrinterService service) {
sunmiPrinterService = service;
checkSunmiPrinterService(service);
}
@Override
protected void onDisconnected() {
sunmiPrinterService = null;
sunmiPrinter = LostSunmiPrinter;
}
};
/**
* 打印机服务初始化-在Application初始化
* init sunmi print service
*/
public void initSunmiPrinterService(Context context) {
try {
boolean ret = InnerPrinterManager.getInstance().bindService(context,
innerPrinterCallback);
if (!ret) {
sunmiPrinter = NoSunmiPrinter;
}
} catch (InnerPrinterException e) {
e.printStackTrace();
}
}
/**
* 延时打印机服务初始化
* deInit sunmi print service
*/
public void deInitSunmiPrinterService(Context context) {
try {
if (sunmiPrinterService != null) {
InnerPrinterManager.getInstance().unBindService(context, innerPrinterCallback);
sunmiPrinterService = null;
sunmiPrinter = LostSunmiPrinter;
}
} catch (InnerPrinterException e) {
e.printStackTrace();
}
}
/**
* 检测打印机的连接
* Check the printer connection,
* like some devices do not have a printer but need to be connected to the cash drawer through a print service
*/
private void checkSunmiPrinterService(SunmiPrinterService service) {
boolean ret = false;
try {
ret = InnerPrinterManager.getInstance().hasPrinter(service);
} catch (InnerPrinterException e) {
e.printStackTrace();
}
sunmiPrinter = ret ? FoundSunmiPrinter : NoSunmiPrinter;
}
/**
* Some conditions can cause interface calls to fail
* For example: the version is too low、device does not support
* You can see {@link ExceptionConst}
* So you have to handle these exceptions
*/
private void handleRemoteException(RemoteException e) {
//TODO process when get one exception
}
/**
* 发送指令打印
* send esc cmd
*/
public void sendRawData(byte[] data) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.sendRAWData(data, null);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* 打印机在没有切纸器的机器上切纸并抛出异常
* Printer cuts paper and throws exception on machines without a cutter
*/
public void cutpaper() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.cutPaper(null);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* 初始化打印机
* Initialize the printer
* All style settings will be restored to default
*/
public void initPrinter() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.printerInit(null);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* *送纸三行
* *当行距设置为0时未禁用
*/
public void print3Line() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.lineWrap(3, null);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* 获取打印机序列号
*/
public String getPrinterSerialNo() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return "";
}
try {
return sunmiPrinterService.getPrinterSerialNo();
} catch (RemoteException e) {
handleRemoteException(e);
return "";
}
}
/**
* 获取设备模型
*/
public String getDeviceModel() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return "";
}
try {
return sunmiPrinterService.getPrinterModal();
} catch (RemoteException e) {
handleRemoteException(e);
return "";
}
}
/**
* 获取固件版本
*/
public String getPrinterVersion() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return "";
}
try {
return sunmiPrinterService.getPrinterVersion();
} catch (RemoteException e) {
handleRemoteException(e);
return "";
}
}
/**
* 获取纸张规格
*/
public String getPrinterPaper() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return "";
}
try {
return sunmiPrinterService.getPrinterPaper() == 1 ? "58mm" : "80mm";
} catch (RemoteException e) {
handleRemoteException(e);
return "";
}
}
/**
* G获取纸张规格
*/
public void getPrinterHead(InnerResultCallbcak callbcak) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.getPrinterFactory(callbcak);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* 获取启动后的打印距离
* 有些设备需要异步获取
*/
public String getPrinterDistance(InnerResultCallbcak callback) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return "";
}
try {
return sunmiPrinterService.getPrintedLength(callback) + "mm";
} catch (RemoteException e) {
handleRemoteException(e);
return "";
}
}
/**
* 设置打印机校准
*/
public void setAlign(int align) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.setAlignment(align, null);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* 由于纸张舱口和打印头之间的距离,
* 纸张需要自动送出
* 但是如果Api不支持它,它将被打印三行来代替
*/
public void feedPaper() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.autoOutPaper(null);
} catch (RemoteException e) {
print3Line();
}
}
/**
* 打印文本
* print text
* setPrinterStyle Api require V4.2.22 or later, So use esc cmd instead when not supported
* More settings reference documentation {@link WoyouConsts}
*/
public void printText(String content, float size, boolean isBold, boolean isUnderLine) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, isBold ?
WoyouConsts.ENABLE : WoyouConsts.DISABLE);
} catch (RemoteException e) {
if (isBold) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOn(), null);
} else {
sunmiPrinterService.sendRAWData(ESCUtil.boldOff(), null);
}
}
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_UNDERLINE, isUnderLine ?
WoyouConsts.ENABLE : WoyouConsts.DISABLE);
} catch (RemoteException e) {
if (isUnderLine) {
sunmiPrinterService.sendRAWData(ESCUtil.underlineWithOneDotWidthOn(), null);
} else {
sunmiPrinterService.sendRAWData(ESCUtil.underlineOff(), null);
}
}
sunmiPrinterService.printTextWithFont(content, null, size, null);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 打印条形码
* print Bar Code
*/
public void printBarCode(String data, int symbology, int height, int width, int textposition) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.printBarCode(data, symbology, height, width, textposition, null);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 打印二维码
* print Qr Code
*/
public void printQr(String data, int modulesize, int errorlevel) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.printQRCode(data, modulesize, errorlevel, null);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 打印表格
* Print a row of a table
*/
public void printTable(String[] txts, int[] width, int[] align) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.printColumnsString(txts, width, align, null);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 打印图片
* Print pictures and text in the specified orde
* After the picture is printed,
* the line feed output needs to be called,
* otherwise it will be saved in the cache
* In this example, the image will be printed because the print text content is added
*/
public void printBitmap(Bitmap bitmap, int orientation) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
if (orientation == 0) {
sunmiPrinterService.printBitmap(bitmap, null);
sunmiPrinterService.printText("横向排列\n", null);
sunmiPrinterService.printBitmap(bitmap, null);
sunmiPrinterService.printText("横向排列\n", null);
} else {
sunmiPrinterService.printBitmap(bitmap, null);
sunmiPrinterService.printText("\n纵向排列\n", null);
sunmiPrinterService.printBitmap(bitmap, null);
sunmiPrinterService.printText("\n纵向排列\n", null);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 获取当前打印机是否处于黑色标记模式
* Gets whether the current printer is in black mark mode
*/
public boolean isBlackLabelMode() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return false;
}
try {
return sunmiPrinterService.getPrinterMode() == 1;
} catch (RemoteException e) {
return false;
}
}
/**
* 事务打印:
* Transaction printing:
* enter->print->exit(get result) or
* enter->first print->commit(get result)->twice print->commit(get result)->exit(don't care
* result)
*/
public void printTrans(Context context, InnerResultCallbcak callbcak) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.enterPrinterBuffer(true);
printExample();
sunmiPrinterService.exitPrinterBufferWithCallback(true, callbcak);
} catch (RemoteException e) {
e.printStackTrace();
}
}
// ============================ 其他的设备支持 begin ↓ =============================
/**
* Open cash box
* This method can be used on Sunmi devices with a cash drawer interface
* If there is no cash box (such as V1、P1) or the call fails, an exception will be thrown
* <p>
* Reference to https://docs.sunmi.com/general-function-modules/external-device-debug/cash-box-driver/}
*/
public void openCashBox() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.openDrawer(null);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* LCD screen control
*
* @param flag 1 —— Initialization
* 2 —— Light up screen
* 3 —— Extinguish screen
* 4 —— Clear screen contents
*/
public void controlLcd(int flag) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.sendLCDCommand(flag);
} catch (RemoteException e) {
handleRemoteException(e);
}
}
/**
* Display text SUNMI,font size is 16 and format is fill
* sendLCDFillString(txt, size, fill, callback)
* Since the screen pixel height is 40, the font should not exceed 40
*/
public void sendTextToLcd() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.sendLCDFillString("SUNMI", 16, true, new InnerLcdCallback() {
@Override
public void onRunResult(boolean show) throws RemoteException {
//TODO handle result
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* Display two lines and one empty line in the middle
*/
public void sendTextsToLcd() {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
String[] texts = {"SUNMI", null, "SUNMI"};
int[] align = {2, 1, 2};
sunmiPrinterService.sendLCDMultiString(texts, align, new InnerLcdCallback() {
@Override
public void onRunResult(boolean show) throws RemoteException {
//TODO handle result
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* Display one 128x40 pixels and opaque picture
*/
public void sendPicToLcd(Bitmap pic) {
if (sunmiPrinterService == null) {
//TODO Service disconnection processing
return;
}
try {
sunmiPrinterService.sendLCDBitmap(pic, new InnerLcdCallback() {
@Override
public void onRunResult(boolean show) throws RemoteException {
//TODO handle result
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
拿到这个入口控制类,我们就能打印自定义格式的小票:
public void printExample() {
if (sunmiPrinterService == null) {
return;
}
try {
int paper = sunmiPrinterService.getPrinterPaper();
sunmiPrinterService.printerInit(null);
sunmiPrinterService.setAlignment(1, null); //1代表居中打印
sunmiPrinterService.printText("测试样张\n", null);
sunmiPrinterService.lineWrap(1, null); //走纸一行
sunmiPrinterService.setAlignment(0, null); //0代表左侧打印
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.SET_LINE_SPACING, 0); //设置文本默认行间距
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(new byte[]{0x1B, 0x33, 0x00}, null);
}
//打印自定义的宽度
sunmiPrinterService.printTextWithFont("说明:这是一个自定义的小票样式例子,开发者可以仿照此进行自己的构建\n", null, 12, null);
//打印横线
if (paper == 1) {
sunmiPrinterService.printText("--------------------------------\n", null);
} else {
sunmiPrinterService.printText("------------------------------------------------\n", null);
}
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.ENABLE); //设置文本加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOn(), null);
}
String txts[] = new String[]{"商品", "数量", "价格"};
int width[] = new int[]{3, 1, 1}; //代表权重
int align[] = new int[]{0, 1, 2};
sunmiPrinterService.printColumnsString(txts, width, align, null);
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.DISABLE); //设置取消文字加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOff(), null);
}
//打印横线
if (paper == 1) {
sunmiPrinterService.printText("--------------------------------\n", null);
} else {
sunmiPrinterService.printText("------------------------------------------------\n", null);
}
//开始打印商品
txts[0] = "汉堡";
txts[1] = "X2";
txts[2] = "17¥";
sunmiPrinterService.printColumnsString(txts, width, align, null);
txts[0] = "可乐";
txts[1] = "X2";
txts[2] = "10¥";
sunmiPrinterService.printColumnsString(txts, width, align, null);
txts[0] = "薯条";
txts[1] = "X2";
txts[2] = "11¥";
sunmiPrinterService.printColumnsString(txts, width, align, null);
txts[0] = "炸鸡";
txts[1] = "X2";
txts[2] = "11¥";
sunmiPrinterService.printColumnsString(txts, width, align, null);
txts[0] = "圣代";
txts[1] = "X2";
txts[2] = "10¥";
sunmiPrinterService.printColumnsString(txts, width, align, null);
//打印横线
if (paper == 1) {
sunmiPrinterService.printText("--------------------------------\n", null);
} else {
sunmiPrinterService.printText("------------------------------------------------\n", null);
}
//打印左侧的自定义大小文字
sunmiPrinterService.printTextWithFont("总计: 59¥\b", null, 40, null);
//居中打印二维码
// sunmiPrinterService.setAlignment(1, null);
// sunmiPrinterService.printQRCode("谢谢惠顾", 10, 0, null);
//
// //设置打印字体大小,然后打印字体
// sunmiPrinterService.setFontSize(36, null);
// sunmiPrinterService.printText("谢谢惠顾", null);
//打印完成出一点纸
sunmiPrinterService.autoOutPaper(null);
} catch (RemoteException e) {
e.printStackTrace();
}
}
可以测试一下各种样式,可以理解一个小票为一个FrameLayout,常用的一些属性为:文本的左对齐,右对齐,居中对齐,背景颜色,换行,空格,横线,二维码,Bitmap,等。
可以各种样式打印一次 看看效果,最后就是封装自己格式的小票了:
/**
* 自定义的打印机管理-打印输出
*/
public class MyPrintManager {
private static final MyPrintManager ourInstance = new MyPrintManager();
public static MyPrintManager get() {
return ourInstance;
}
private MyPrintManager() {
initPrinterStyle();
}
private void initPrinterStyle() {
if (BluetoothUtil.isBlueToothPrinter) {
BluetoothUtil.sendData(ESCUtil.init_printer());
} else {
SunmiPrintHelper.getInstance().initPrinter();
}
}
/**
* 打印我的自定义小票
*/
public void printMyOrder(OrderDetailResponseData detail) {
if (detail == null) return;
SunmiPrintHelper printHelper = SunmiPrintHelper.getInstance();
CommUtils.getHandler().post(() -> {
String printNum = SPUtils.getInstance(CommUtils.getContext()).getString(Constants.CACHE_PRINT_ORDER_NUM, "3");
for (int i = 0; i < Integer.parseInt(printNum); i++) {
String storeId = detail.order.store_id;
if (storeId.length() == 1) {
storeId = "#000" + storeId;
} else if (storeId.length() == 2) {
storeId = "#00" + storeId;
} else if (storeId.length() == 3) {
storeId = "#0" + storeId;
} else if (storeId.length() >= 4) {
storeId = "#" + storeId;
}
printHelper.printYYOrder(storeId, detail.order.store_name, detail.order.receive_time,
detail.order.deliver_no, detail.order.member_name, "Paid",
detail.order.deliver_type.equals("1") ? "Delivery Time" : "Self Collection",
detail.order.deliver_time, detail.goods.size(), detail.goods, detail.order.sold_total, detail.order.total,
detail.order.gst_total, detail.order.user_remark, new InnerResultCallbcak() {
@Override
public void onRunResult(boolean isSuccess) throws RemoteException {
YYLogUtils.w("isSuccess:" + isSuccess);
//打印成功回调
//发送通知打印订单
LiveEventBus.get(Constants.EVENT_PHONE_PRINT_ORDER_SUCCESS, Boolean.class).post(true);
}
@Override
public void onReturnString(String result) throws RemoteException {
YYLogUtils.w("result:" + result);
}
@Override
public void onRaiseException(int code, String msg) throws RemoteException {
YYLogUtils.w("msg:" + msg);
}
@Override
public void onPrintResult(int code, String msg) throws RemoteException {
YYLogUtils.w("msg:" + msg);
}
});
}
});
}
}
具体文本排列如下:
//打印YY-Food订单
public void printYYOrder(String merchantId, String merchantName, String orderDate, String pickNum, String customerName,
String paidStatusStr, String deliverType, String deliveryTime, int foodCount,
List<OrderDetailResponseData.GoodsData> goods, String submitMoney,
String totalMoney, String gstMoney, String remark, ICallback callback) {
if (sunmiPrinterService == null) {
return;
}
try {
int paper = sunmiPrinterService.getPrinterPaper();
sunmiPrinterService.printerInit(null);
sunmiPrinterService.setAlignment(1, null); //1代表居中打印
sunmiPrinterService.setFontSize(24, null); //默认为18字体
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.ENABLE); //设置文本加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOn(), null);
}
//店铺的id(加粗)
sunmiPrinterService.printTextWithFont("YY Food " + merchantId + "\n", null, 30, null);
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.DISABLE); //设置取消文字加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOff(), null);
}
//店铺的名字
sunmiPrinterService.printText(merchantName + "\n", null);
//接单时间
sunmiPrinterService.printText(orderDate + "\n", null);
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.ENABLE); //设置文本加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOn(), null);
}
//取货编号(加粗)
sunmiPrinterService.printTextWithFont(pickNum + "\n", null, 30, null);
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.DISABLE); //设置取消文字加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOff(), null);
}
//打印横线
sunmiPrinterService.printTextWithFont("--------------------------------\n", null, 24, null);
sunmiPrinterService.setAlignment(1, null); //1代表居中打印
//客户名字
sunmiPrinterService.printText("Customer:\n", null);
sunmiPrinterService.printText(customerName + "\n", null);
//支付状态 ,使用反白模式打印
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_ANTI_WHITE, WoyouConsts.ENABLE);
sunmiPrinterService.printTextWithFont(" " + paidStatusStr + " \n", null, 26, null);
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_ANTI_WHITE, WoyouConsts.DISABLE);
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.ENABLE); //设置文本加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOn(), null);
}
//送达时间(加粗)
sunmiPrinterService.printTextWithFont(deliverType + "\n", null, 30, null);
sunmiPrinterService.printTextWithFont(deliveryTime + " \n", null, 26, null);
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.DISABLE); //设置取消文字加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOff(), null);
}
//共计多少数量
sunmiPrinterService.printText(foodCount + " Items\n", null);
//打印横线
sunmiPrinterService.printTextWithFont("--------------------------------\n", null, 24, null);
sunmiPrinterService.setAlignment(0, null); //0代表左侧打印
//开始商品的展示
String txts[] = new String[]{"", "", ""};
int width[] = new int[]{1, 4, 2}; //代表权重
int align[] = new int[]{0, 0, 2};
//Food遍历
for (OrderDetailResponseData.GoodsData food : goods) {
//打印食品
sunmiPrinterService.setFontSize(26, null);
sunmiPrinterService.lineWrap(1, null); //走纸一行
txts[0] = food.quantity + " X";
txts[1] = food.goods_name;
txts[2] = "$" + food.goods_price;
sunmiPrinterService.printColumnsString(txts, width, align, null);
//打印选项
List<OrderDetailResponseData.OptionData> values = food.option_value;
for (OrderDetailResponseData.OptionData value : values) {
String raisePrice = value.raise_price;
sunmiPrinterService.setFontSize(23, null);
txts[0] = "";
txts[1] = "1 x " + value.value_name;
txts[2] = !CheckUtil.isEmpty(raisePrice) && Double.parseDouble(raisePrice) > 0 ? "$" + raisePrice : "-";
sunmiPrinterService.printColumnsString(txts, width, align, null);
}
}
sunmiPrinterService.lineWrap(1, null); //走纸一行
sunmiPrinterService.setAlignment(1, null); //居中
//打印横线
sunmiPrinterService.printTextWithFont("--------------------------------\n", null, 24, null);
sunmiPrinterService.setAlignment(0, null); //0代表左侧打印
//如果有remark才打印出来
if (!CheckUtil.isEmpty(remark)) {
sunmiPrinterService.lineWrap(1, null); //走纸一行
sunmiPrinterService.printText("Remark: " + remark + "\n", null);
sunmiPrinterService.lineWrap(1, null); //走纸一行
sunmiPrinterService.printTextWithFont("--------------------------------\n", null, 24, null);
}
//统计
sunmiPrinterService.setFontSize(24, null);
String submits[] = new String[]{"", ""};
submits[0] = "SUBMIT";
submits[1] = "$" + submitMoney;
sunmiPrinterService.printColumnsString(submits, new int[]{1, 1}, new int[]{0, 2}, null);
sunmiPrinterService.lineWrap(1, null); //走纸一行
//TOTAL
sunmiPrinterService.printTextWithFont("Total\n", null, 26, null);
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.ENABLE); //设置文本加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOn(), null);
}
//Total金额
sunmiPrinterService.printTextWithFont("$" + totalMoney + "\n", null, 30, null);
try {
sunmiPrinterService.setPrinterStyle(WoyouConsts.ENABLE_BOLD, WoyouConsts.DISABLE); //设置取消文字加粗
} catch (RemoteException e) {
sunmiPrinterService.sendRAWData(ESCUtil.boldOff(), null);
}
//GST
sunmiPrinterService.setFontSize(23, null);
String gsts[] = new String[]{"", ""};
gsts[0] = "GST (Included)";
gsts[1] = "$" + gstMoney;
sunmiPrinterService.printColumnsString(gsts, new int[]{1, 1}, new int[]{0, 2}, null);
//打印完成出一点纸
sunmiPrinterService.lineWrap(1, null); //走纸一行
sunmiPrinterService.autoOutPaper(callback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
打印效果如下:
到处结束啦,希望对大家能有所帮助!