注意:本文原創,轉載請注明出處。歡迎關注我的 簡書 。
本文主要講解藍牙打印機在打印小票的過程中,如何打印各種常見格式。由于之前需要調試打印格式,但是苦于網上沒有詳細的講解教程,無奈只能自給自足,自己封裝了一個。如果各位盆友正在或者曾經苦惱藍牙打印機的打印格式,那么恭喜你,本篇博文就是你要尋找的。
什么是藍牙打印機
是的,你沒看錯。一開始兔子哥先來介紹一下什么是藍牙打印機。。。好吧,這個就交給百度了:
藍牙打印機(Bluetooth printer)就是將藍牙技術應用在打印機上,擺脫打印機連線所帶來的不便,實現無線打印,可以減少桌面上令人不快的電纜,并且可以將打印機遠離主機任意搬動,擺放在房間中適合的位置。
常見小票樣式
這個小票格式基本就是最常見的了。這里面的各種格式,都可以從藍牙打印機的API里面找到。藍牙打印機有好多API,我把常用的給封裝了一下:PrintUtils.java
/**
* 復位打印機
*/
public static final byte[] RESET = {0x1b, 0x40};
/**
* 左對齊
*/
public static final byte[] ALIGN_LEFT = {0x1b, 0x61, 0x00};
/**
* 中間對齊
*/
public static final byte[] ALIGN_CENTER = {0x1b, 0x61, 0x01};
/**
* 右對齊
*/
public static final byte[] ALIGN_RIGHT = {0x1b, 0x61, 0x02};
/**
* 選擇加粗模式
*/
public static final byte[] BOLD = {0x1b, 0x45, 0x01};
/**
* 取消加粗模式
*/
public static final byte[] BOLD_CANCEL = {0x1b, 0x45, 0x00};
/**
* 寬高加倍
*/
public static final byte[] DOUBLE_HEIGHT_WIDTH = {0x1d, 0x21, 0x11};
/**
* 寬加倍
*/
public static final byte[] DOUBLE_WIDTH = {0x1d, 0x21, 0x10};
/**
* 高加倍
*/
public static final byte[] DOUBLE_HEIGHT = {0x1d, 0x21, 0x01};
/**
* 字體不放大
*/
public static final byte[] NORMAL = {0x1d, 0x21, 0x00};
/**
* 設置默認行間距
*/
public static final byte[] LINE_SPACING_DEFAULT = {0x1b, 0x32};
打印實現
打印小票,當然首先需要連接藍牙打印機。至于如何掃描打印機,如何連接,這個都是標準的藍牙方式,網上資料也很多。因為本博文主要關注打印格式,所以這個就不再贅述了。連接打印機后,需要從BluetoothSocket
中獲取OutputStream
。然后接下來都是通過OutputStream
來給打印機發送打印指令。
-
設置打印格式
設置打印格式,就要用到上面封裝的那些指令了。
/**
* 設置打印格式
*
* @param command 格式指令
*/
public static void selectCommand(byte[] command) {
try {
outputStream.write(command);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
用法如下:
PrintUtils.selectCommand(PrintUtils.RESET);
PrintUtils.selectCommand(PrintUtils.LINE_SPACING_DEFAULT);
PrintUtils.selectCommand(PrintUtils.ALIGN_CENTER);
PrintUtils.selectCommand(PrintUtils.NORMAL);
- 打印文字
/**
* 打印文字
*
* @param text 要打印的文字
*/
public static void printText(String text) {
try {
byte[] data = text.getBytes("gbk");
outputStream.write(data, 0, data.length);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
用法如下:
PrintUtils.printText("好吃的牛肉面" + "\n");
打印文字的時候,最后都要手動拼接一個 "\n" 用來換行。
完美嗎?
根據上面封裝的代碼,“貌似”是可以實現所有的打印樣式了。是的,沒毛病。因為上面既有打印格式的設置,又有打印文字的用法。打印小票是沒問題了。but……
這種格式如何實現?
這種格式呢?
有的盆友可能會說,這有啥問題的??? 并且給出了他們認為完美的解釋:
PrintUtils.printText("合計 53.50" + "\n");
PrintUtils.printText("抹零 3.50" + "\n");
PrintUtils.printText("項目 數量 金額" + "\n");
可是,完美嗎?
你可能覺得人工加空格是可以“實現”需求。but……中間的空格,你知道應該添加多少嗎?添加多了或者少了,打印出來的結果都會一塌糊涂!并且注意小票上都是要求對齊的!合計、抹零左側對齊。金額右側對齊。項目、數量、金額這三列都要中心對齊。??吹竭@里,這個人工加空格的做法,還完美嗎?
給我一個完美的解釋!
“海參炒面,海參呢?給我一個完美的解釋!”
“我叫海參,面是我炒的。完美不?”
是的,我們需要一個完美的解釋。到底如何實現上面說的打印兩列
、打印三列
的情況。
首先,講解之前,先設置幾個默認值:
/**
* 打印紙一行最大的字節
*/
private static final int LINE_BYTE_SIZE = 32;
/**
* 打印三列時,中間一列的中心線距離打印紙左側的距離
*/
private static final int LEFT_LENGTH = 16;
/**
* 打印三列時,中間一列的中心線距離打印紙右側的距離
*/
private static final int RIGHT_LENGTH = 16;
/**
* 打印三列時,第一列漢字最多顯示幾個文字
*/
private static final int LEFT_TEXT_MAX_LENGTH = 5;
我們知道,通用的打印紙都是有固定寬度的。經過大量測試,得出打印紙一行的最大字節數是32個字節。那么根據上面的注釋,我們可以得到以下結論:
LEFT_LENGTH + RIGHT_LENGTH = LINE_BYTE_SIZE
這是毋庸置疑的。左側寬度 + 右側寬度 必須要等于打印紙總寬度。
而且因為打印三列的時候,中間一列是要居中顯示的,所以LEFT_LENGTH
和RIGHT_LENGTH
都必須是總寬度32的一半,也就是必須是16.
那么如何計算某個文字所占的字節數呢?
/**
* 獲取數據長度
*
* @param msg
* @return
*/
@SuppressLint("NewApi")
private static int getBytesLength(String msg) {
return msg.getBytes(Charset.forName("GB2312")).length;
}
OK,準備了這么多,海參終于準備好了。接下來就可以準備炒面了~
打印兩列
/**
* 打印兩列
*
* @param leftText 左側文字
* @param rightText 右側文字
* @return
*/
@SuppressLint("NewApi")
public static String printTwoData(String leftText, String rightText) {
StringBuilder sb = new StringBuilder();
int leftTextLength = getBytesLength(leftText);
int rightTextLength = getBytesLength(rightText);
sb.append(leftText);
// 計算兩側文字中間的空格
int marginBetweenMiddleAndRight = LINE_BYTE_SIZE - leftTextLength - rightTextLength;
for (int i = 0; i < marginBetweenMiddleAndRight; i++) {
sb.append(" ");
}
sb.append(rightText);
return sb.toString();
}
那位說話了:“你這代碼明明也是手動拼的空格啊,完美個毛??!”。大兄弟你消消氣,這里是通過邏輯進行拼接的空格,不是無腦的拼接。打印兩列的步驟如下:
- 拼接左側一列的文字
- 拼接兩側文字中間的空格
- 拼接右側一列的文字
關鍵步驟是計算兩側文字中間的空格。怎么計算呢?很簡單,總寬度 - 左側文字長度 - 右側文字長度
就是空格的長度。
打印三列
/**
* 打印三列
*
* @param leftText 左側文字
* @param middleText 中間文字
* @param rightText 右側文字
* @return
*/
@SuppressLint("NewApi")
public static String printThreeData(String leftText, String middleText, String rightText) {
StringBuilder sb = new StringBuilder();
// 左邊最多顯示 LEFT_TEXT_MAX_LENGTH 個漢字 + 兩個點
if (leftText.length() > LEFT_TEXT_MAX_LENGTH) {
leftText = leftText.substring(0, LEFT_TEXT_MAX_LENGTH) + "..";
}
int leftTextLength = getBytesLength(leftText);
int middleTextLength = getBytesLength(middleText);
int rightTextLength = getBytesLength(rightText);
sb.append(leftText);
// 計算左側文字和中間文字的空格長度
int marginBetweenLeftAndMiddle = LEFT_LENGTH - leftTextLength - middleTextLength / 2;
for (int i = 0; i < marginBetweenLeftAndMiddle; i++) {
sb.append(" ");
}
sb.append(middleText);
// 計算右側文字和中間文字的空格長度
int marginBetweenMiddleAndRight = RIGHT_LENGTH - middleTextLength / 2 - rightTextLength;
for (int i = 0; i < marginBetweenMiddleAndRight; i++) {
sb.append(" ");
}
// 打印的時候發現,最右邊的文字總是偏右一個字符,所以需要刪除一個空格
sb.delete(sb.length() - 1, sb.length()).append(rightText);
return sb.toString();
}
打印三列的步驟如下:
- 拼接左側一列的文字
- 拼接左側文字和中間文字中間的空格
- 拼接中間的文字
- 拼接右側文字和中間文字中間的空格
- 拼接右側一列的文字
在計算空格的時候,為了保證中間一列始終保持中心線對齊,所以在計算中間文字長度時候,都除以2。
完整打印代碼
PrintUtils.selectCommand(PrintUtils.RESET);
PrintUtils.selectCommand(PrintUtils.LINE_SPACING_DEFAULT);
PrintUtils.selectCommand(PrintUtils.ALIGN_CENTER);
PrintUtils.printText("美食餐廳\n\n");
PrintUtils.selectCommand(PrintUtils.DOUBLE_HEIGHT_WIDTH);
PrintUtils.printText("桌號:1號桌\n\n");
PrintUtils.selectCommand(PrintUtils.NORMAL);
PrintUtils.selectCommand(PrintUtils.ALIGN_LEFT);
PrintUtils.printText(PrintUtils.printTwoData("訂單編號", "201507161515\n"));
PrintUtils.printText(PrintUtils.printTwoData("點菜時間", "2016-02-16 10:46\n"));
PrintUtils.printText(PrintUtils.printTwoData("上菜時間", "2016-02-16 11:46\n"));
PrintUtils.printText(PrintUtils.printTwoData("人數:2人", "收銀員:張三\n"));
PrintUtils.printText("--------------------------------\n");
PrintUtils.selectCommand(PrintUtils.BOLD);
PrintUtils.printText(PrintUtils.printThreeData("項目", "數量", "金額\n"));
PrintUtils.printText("--------------------------------\n");
PrintUtils.selectCommand(PrintUtils.BOLD_CANCEL);
PrintUtils.printText(PrintUtils.printThreeData("面", "1", "0.00\n"));
PrintUtils.printText(PrintUtils.printThreeData("米飯", "1", "6.00\n"));
PrintUtils.printText(PrintUtils.printThreeData("鐵板燒", "1", "26.00\n"));
PrintUtils.printText(PrintUtils.printThreeData("一個測試", "1", "226.00\n"));
PrintUtils.printText(PrintUtils.printThreeData("牛肉面啊啊", "1", "2226.00\n"));
PrintUtils.printText(PrintUtils.printThreeData("牛肉面啊啊啊牛肉面啊啊啊", "888", "98886.00\n"));
PrintUtils.printText("--------------------------------\n");
PrintUtils.printText(PrintUtils.printTwoData("合計", "53.50\n"));
PrintUtils.printText(PrintUtils.printTwoData("抹零", "3.50\n"));
PrintUtils.printText("--------------------------------\n");
PrintUtils.printText(PrintUtils.printTwoData("應收", "50.00\n"));
PrintUtils.printText("--------------------------------\n");
PrintUtils.selectCommand(PrintUtils.ALIGN_LEFT);
PrintUtils.printText("備注:不要辣、不要香菜");
PrintUtils.printText("\n\n\n\n\n");
舉一反三
學習了上面的打印格式,那么這個小票怎么打??? 區別就是打印三列的時候,中間一列是偏右了。相信大家應該知道答案了。如果有疑問,可以給我留言。
鑒于好多讀者給我留言,要PrintUtils工具類代碼,所以我把代碼發布到github上了,大家可以自行下載。地址是:https://github.com/heroxuetao/PrintUtils
如果有幫助到你,可以順手來個star 。萬分感謝!
本文止。