注意:本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處。歡迎關(guān)注我的 簡書 。
本文主要講解藍(lán)牙打印機(jī)在打印小票的過程中,如何打印各種常見格式。由于之前需要調(diào)試打印格式,但是苦于網(wǎng)上沒有詳細(xì)的講解教程,無奈只能自給自足,自己封裝了一個(gè)。如果各位盆友正在或者曾經(jīng)苦惱藍(lán)牙打印機(jī)的打印格式,那么恭喜你,本篇博文就是你要尋找的。
什么是藍(lán)牙打印機(jī)
是的,你沒看錯(cuò)。一開始兔子哥先來介紹一下什么是藍(lán)牙打印機(jī)。。。好吧,這個(gè)就交給百度了:
藍(lán)牙打印機(jī)(Bluetooth printer)就是將藍(lán)牙技術(shù)應(yīng)用在打印機(jī)上,擺脫打印機(jī)連線所帶來的不便,實(shí)現(xiàn)無線打印,可以減少桌面上令人不快的電纜,并且可以將打印機(jī)遠(yuǎn)離主機(jī)任意搬動(dòng),擺放在房間中適合的位置。
常見小票樣式
這個(gè)小票格式基本就是最常見的了。這里面的各種格式,都可以從藍(lán)牙打印機(jī)的API里面找到。藍(lán)牙打印機(jī)有好多API,我把常用的給封裝了一下:PrintUtils.java
/**
* 復(fù)位打印機(jī)
*/
public static final byte[] RESET = {0x1b, 0x40};
/**
* 左對(duì)齊
*/
public static final byte[] ALIGN_LEFT = {0x1b, 0x61, 0x00};
/**
* 中間對(duì)齊
*/
public static final byte[] ALIGN_CENTER = {0x1b, 0x61, 0x01};
/**
* 右對(duì)齊
*/
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};
/**
* 設(shè)置默認(rèn)行間距
*/
public static final byte[] LINE_SPACING_DEFAULT = {0x1b, 0x32};
打印實(shí)現(xiàn)
打印小票,當(dāng)然首先需要連接藍(lán)牙打印機(jī)。至于如何掃描打印機(jī),如何連接,這個(gè)都是標(biāo)準(zhǔn)的藍(lán)牙方式,網(wǎng)上資料也很多。因?yàn)楸静┪闹饕P(guān)注打印格式,所以這個(gè)就不再贅述了。連接打印機(jī)后,需要從BluetoothSocket
中獲取OutputStream
。然后接下來都是通過OutputStream
來給打印機(jī)發(fā)送打印指令。
-
設(shè)置打印格式
設(shè)置打印格式,就要用到上面封裝的那些指令了。
/**
* 設(shè)置打印格式
*
* @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");
打印文字的時(shí)候,最后都要手動(dòng)拼接一個(gè) "\n" 用來換行。
完美嗎?
根據(jù)上面封裝的代碼,“貌似”是可以實(shí)現(xiàn)所有的打印樣式了。是的,沒毛病。因?yàn)樯厦婕扔写蛴「袷降脑O(shè)置,又有打印文字的用法。打印小票是沒問題了。but……
這種格式如何實(shí)現(xiàn)?
這種格式呢?
有的盆友可能會(huì)說,這有啥問題的??? 并且給出了他們認(rèn)為完美的解釋:
PrintUtils.printText("合計(jì) 53.50" + "\n");
PrintUtils.printText("抹零 3.50" + "\n");
PrintUtils.printText("項(xiàng)目 數(shù)量 金額" + "\n");
可是,完美嗎?
你可能覺得人工加空格是可以“實(shí)現(xiàn)”需求。but……中間的空格,你知道應(yīng)該添加多少嗎?添加多了或者少了,打印出來的結(jié)果都會(huì)一塌糊涂!并且注意小票上都是要求對(duì)齊的!合計(jì)、抹零左側(cè)對(duì)齊。金額右側(cè)對(duì)齊。項(xiàng)目、數(shù)量、金額這三列都要中心對(duì)齊。??吹竭@里,這個(gè)人工加空格的做法,還完美嗎?
給我一個(gè)完美的解釋!
“海參炒面,海參呢?給我一個(gè)完美的解釋!”
“我叫海參,面是我炒的。完美不?”
是的,我們需要一個(gè)完美的解釋。到底如何實(shí)現(xiàn)上面說的打印兩列
、打印三列
的情況。
首先,講解之前,先設(shè)置幾個(gè)默認(rèn)值:
/**
* 打印紙一行最大的字節(jié)
*/
private static final int LINE_BYTE_SIZE = 32;
/**
* 打印三列時(shí),中間一列的中心線距離打印紙左側(cè)的距離
*/
private static final int LEFT_LENGTH = 16;
/**
* 打印三列時(shí),中間一列的中心線距離打印紙右側(cè)的距離
*/
private static final int RIGHT_LENGTH = 16;
/**
* 打印三列時(shí),第一列漢字最多顯示幾個(gè)文字
*/
private static final int LEFT_TEXT_MAX_LENGTH = 5;
我們知道,通用的打印紙都是有固定寬度的。經(jīng)過大量測試,得出打印紙一行的最大字節(jié)數(shù)是32個(gè)字節(jié)。那么根據(jù)上面的注釋,我們可以得到以下結(jié)論:
LEFT_LENGTH + RIGHT_LENGTH = LINE_BYTE_SIZE
這是毋庸置疑的。左側(cè)寬度 + 右側(cè)寬度 必須要等于打印紙總寬度。
而且因?yàn)榇蛴∪械臅r(shí)候,中間一列是要居中顯示的,所以LEFT_LENGTH
和RIGHT_LENGTH
都必須是總寬度32的一半,也就是必須是16.
那么如何計(jì)算某個(gè)文字所占的字節(jié)數(shù)呢?
/**
* 獲取數(shù)據(jù)長度
*
* @param msg
* @return
*/
@SuppressLint("NewApi")
private static int getBytesLength(String msg) {
return msg.getBytes(Charset.forName("GB2312")).length;
}
OK,準(zhǔn)備了這么多,海參終于準(zhǔn)備好了。接下來就可以準(zhǔn)備炒面了~
打印兩列
/**
* 打印兩列
*
* @param leftText 左側(cè)文字
* @param rightText 右側(cè)文字
* @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);
// 計(jì)算兩側(cè)文字中間的空格
int marginBetweenMiddleAndRight = LINE_BYTE_SIZE - leftTextLength - rightTextLength;
for (int i = 0; i < marginBetweenMiddleAndRight; i++) {
sb.append(" ");
}
sb.append(rightText);
return sb.toString();
}
那位說話了:“你這代碼明明也是手動(dòng)拼的空格啊,完美個(gè)毛啊!”。大兄弟你消消氣,這里是通過邏輯進(jìn)行拼接的空格,不是無腦的拼接。打印兩列的步驟如下:
- 拼接左側(cè)一列的文字
- 拼接兩側(cè)文字中間的空格
- 拼接右側(cè)一列的文字
關(guān)鍵步驟是計(jì)算兩側(cè)文字中間的空格。怎么計(jì)算呢?很簡單,總寬度 - 左側(cè)文字長度 - 右側(cè)文字長度
就是空格的長度。
打印三列
/**
* 打印三列
*
* @param leftText 左側(cè)文字
* @param middleText 中間文字
* @param rightText 右側(cè)文字
* @return
*/
@SuppressLint("NewApi")
public static String printThreeData(String leftText, String middleText, String rightText) {
StringBuilder sb = new StringBuilder();
// 左邊最多顯示 LEFT_TEXT_MAX_LENGTH 個(gè)漢字 + 兩個(gè)點(diǎn)
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);
// 計(jì)算左側(cè)文字和中間文字的空格長度
int marginBetweenLeftAndMiddle = LEFT_LENGTH - leftTextLength - middleTextLength / 2;
for (int i = 0; i < marginBetweenLeftAndMiddle; i++) {
sb.append(" ");
}
sb.append(middleText);
// 計(jì)算右側(cè)文字和中間文字的空格長度
int marginBetweenMiddleAndRight = RIGHT_LENGTH - middleTextLength / 2 - rightTextLength;
for (int i = 0; i < marginBetweenMiddleAndRight; i++) {
sb.append(" ");
}
// 打印的時(shí)候發(fā)現(xiàn),最右邊的文字總是偏右一個(gè)字符,所以需要?jiǎng)h除一個(gè)空格
sb.delete(sb.length() - 1, sb.length()).append(rightText);
return sb.toString();
}
打印三列的步驟如下:
- 拼接左側(cè)一列的文字
- 拼接左側(cè)文字和中間文字中間的空格
- 拼接中間的文字
- 拼接右側(cè)文字和中間文字中間的空格
- 拼接右側(cè)一列的文字
在計(jì)算空格的時(shí)候,為了保證中間一列始終保持中心線對(duì)齊,所以在計(jì)算中間文字長度時(shí)候,都除以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("桌號(hào):1號(hào)桌\n\n");
PrintUtils.selectCommand(PrintUtils.NORMAL);
PrintUtils.selectCommand(PrintUtils.ALIGN_LEFT);
PrintUtils.printText(PrintUtils.printTwoData("訂單編號(hào)", "201507161515\n"));
PrintUtils.printText(PrintUtils.printTwoData("點(diǎn)菜時(shí)間", "2016-02-16 10:46\n"));
PrintUtils.printText(PrintUtils.printTwoData("上菜時(shí)間", "2016-02-16 11:46\n"));
PrintUtils.printText(PrintUtils.printTwoData("人數(shù):2人", "收銀員:張三\n"));
PrintUtils.printText("--------------------------------\n");
PrintUtils.selectCommand(PrintUtils.BOLD);
PrintUtils.printText(PrintUtils.printThreeData("項(xiàng)目", "數(shù)量", "金額\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("一個(gè)測試", "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("合計(jì)", "53.50\n"));
PrintUtils.printText(PrintUtils.printTwoData("抹零", "3.50\n"));
PrintUtils.printText("--------------------------------\n");
PrintUtils.printText(PrintUtils.printTwoData("應(yīng)收", "50.00\n"));
PrintUtils.printText("--------------------------------\n");
PrintUtils.selectCommand(PrintUtils.ALIGN_LEFT);
PrintUtils.printText("備注:不要辣、不要香菜");
PrintUtils.printText("\n\n\n\n\n");
舉一反三
學(xué)習(xí)了上面的打印格式,那么這個(gè)小票怎么打?。?區(qū)別就是打印三列的時(shí)候,中間一列是偏右了。相信大家應(yīng)該知道答案了。如果有疑問,可以給我留言。
鑒于好多讀者給我留言,要PrintUtils工具類代碼,所以我把代碼發(fā)布到github上了,大家可以自行下載。地址是:https://github.com/heroxuetao/PrintUtils
如果有幫助到你,可以順手來個(gè)star 。萬分感謝!
本文止。