接了一个需求,需要用Android pad连接打印机进行打印,以前倒是没接触过,这次在网上找了下资料,简单实现了下需求。
在这记录下相关代码以及踩的坑
代码步骤都比较简单,先看下代码
public class EscPos {
public static final byte FS = 0x1C;
public static final byte HT = 0x9;
public static final byte LF = 0x0A;
public static final byte CR = 0x0D;
public static final byte ESC = 0x1B;
public static final byte DLE = 0x10;
public static final byte GS = 0x1D;
public static final byte STX = 0x02;
public static final byte US = 0x1F;
public static final byte CAN = 0x18;
public static final byte CLR = 0x0C;
public static final byte EOT = 0x04;
/* 初始化打印机 */
public static final byte[] ESC_INIT = new byte[]{ESC, '@'};
/* 设置标准模式 */
public static final byte[] ESC_STANDARD = new byte[]{ESC, 'S'};
/* 设置汉字打印模式 */
public static final byte[] ESC_CN_FONT = new byte[]{FS, '&'};
/* 选择字符集 */
public static final byte[] ESC_SELECT_CHARACTER = new byte[]{ESC, 'R', 9};
/* 设置用户自定义汉字字体 焗7118 */
public static final byte[] ESC_FS_2 = new byte[]{FS, 0x32, 0x71, 0x18};
/* 取消用户自定义字体 */
public static final byte[] ESC_CANCEL_DEFINE_FONT = new byte[]{ESC, '%', 0};
/* 打开钱箱指令 */
public static final byte[] ESC_OPEN_DRAWER = new byte[]{ESC, 'p', 0x00, 0x10, (byte) 0xff};
/* 切纸指令GS V m
* m 0,48 Executes a full cut(cuts the paper completely)
* 1,49 Excutes a partilal cut(one point left uncut)
*/
public static final byte[] POS_CUT_MODE_FULL = new byte[]{GS, 'V', 0x00};
public static final byte[] POS_CUT_MODE_PARTIAL = new byte[]{GS, 'V', 0x01};
/* 西文字符 (半宽)字体A (6 ×12),汉字字符 (全宽)字体A (12×12) */
public static final byte[] ESC_FONT_A = new byte[]{ESC, '!', 0};
/* 西文字符 (半宽)字体B (8×16),汉字字符 (全宽)字体B (16×16) */
public static final byte[] ESC_FONT_B = new byte[]{ESC, '!', 1};
/* 12*24 0/48*/
public static final byte[] ESC_FONTA = new byte[]{ESC, 'M', 48};
/* 9*17 1/49*/
public static final byte[] ESC_FONTB = new byte[]{ESC, 'M', 1};
/* 默认颜色字体指令 */
public static final byte[] ESC_FONT_COLOR_DEFAULT = new byte[]{ESC, 'r', 0x00};
/* 红色字体指令 */
public static final byte[] ESC_FONT_COLOR_RED = new byte[]{ESC, 'r', 0x01};
/* 标准大小 */
public static final byte[] FS_FONT_ALIGN = new byte[]{FS, 0x21, 1, ESC, 0x21, 1};
/* 横向放大一倍 */
public static final byte[] FS_FONT_ALIGN_DOUBLE = new byte[]{FS, 0x21, 4, ESC, 0x21, 4};
/* 纵向放大一倍 */
public static final byte[] FS_FONT_VERTICAL_DOUBLE = new byte[]{FS, 0x21, 8, ESC, 0x21, 8, GS, '!', 0x01};
/* 横向纵向都放大一倍 */
public static final byte[] FS_FONT_DOUBLE = new byte[]{FS, 0x21, 12, ESC, 0x21, 48};
/* 靠左打印命令 */
public static final byte[] ESC_ALIGN_LEFT = new byte[]{0x1b, 'a', 0x00};
/* 居中打印命令 */
public static final byte[] ESC_ALIGN_CENTER = new byte[]{0x1b, 'a', 0x01};
/* 靠右打印命令 */
public static final byte[] ESC_ALIGN_RIGHT = new byte[]{0x1b, 'a', 0x02};
/* 字体加粗 */
public static final byte[] ESC_SETTING_BOLD = new byte[]{ESC, 0x45, 1};
/* 取消字体加粗 */
public static final byte[] ESC_CANCEL_BOLD = new byte[]{ESC, 0x45, 0};
public static final int STATE_1 = 1;// 创建Socket连接
public static final int STATE_2 = 2;// 获取输出流
public static final int STATE_3 = 3;// 关闭Socket
public static final int STATE_0 = 0;// 成功
public static final int ERROR_2 = -2;// 创建Socket失败
public static final int ERROR_3 = -3;// 获取输出流失败
public static final int ERROR_4 = -4;// 关闭Socket出错
public static final int ERROR_100 = -100;// 失败
// 通过socket流进行读写
private OutputStream socketOut = null;
// 以ip作为key,EscPos实例作为value的Map
private static Map posMap = new HashMap();
private static EscPos escPos = null;
// 默认端口
public static int DEFAULT_PORT = 9100;
private Socket socket;
private OnPrinterListener listener;
private String ip;
private int port;
/**
* 根据ip、端口、字符编码构造工具类实例
*
* @param ip 打印机ip
* @param port 打印机端口,默认9100
* @throws IOException
*/
public EscPos(String ip, int port) {
this.ip = ip;
this.port = port;
}
public synchronized static EscPos getInstance(String ip, Integer port) {
escPos = posMap.get(ip);
if (escPos == null) {
escPos = new EscPos(ip, port);
}
return escPos;
}
public static synchronized EscPos getInstance(String ip) {
return getInstance(ip, DEFAULT_PORT);
}
public static synchronized EscPos getInstance() {
//这儿写死或者传入打印机地址和端口
return getInstance("",
"";
}
public static void myPrinter(final List list, final OnPrinterListener printerListener) {
Runnable runnable = new Runnable() {
@Override
public void run() {
EscPos escPos = EscPos.getInstance();
escPos.setListener(printerListener);
escPos.connectSocket();
try {
escPos.init();
for (int i = 0; i < list.size(); i++) {
escPos.line(1);
escPos.boldOff(true);
escPos.printStr(list.get(i));
//escPos.printStr("测试字体6");
}
escPos.line(10);
escPos.feedAndCut();
escPos.closeIOAndSocket();
printerListener.onListener(STATE_0);
} catch (IOException e) {
printerListener.onError(ERROR_100);
LOGGER.error("Socket printer write ===> ", e);
e.printStackTrace();
}
}
};
new Thread(runnable).start();
}
public void connectSocket() {
try {
socket = new Socket();
SocketAddress socAddress = new InetSocketAddress(ip, port);
socket.connect(socAddress, 5000);
if (listener != null) {
listener.onListener(STATE_1);
}
try {
socketOut = socket.getOutputStream();
if (listener != null) {
listener.onListener(STATE_2);
}
} catch (IOException e) {
if (listener != null) {
listener.onError(ERROR_3);
}
LOGGER.error("Socket printer getOutputStream ===> ", e);
e.printStackTrace();
}
} catch (IOException e) {
if (listener != null) {
listener.onError(ERROR_2);
}
LOGGER.error("Socket printer connect ===> ", e);
e.printStackTrace();
}
}
/**
* 换行
*
* @param lineNum 换行数,0为不换行
* @return
* @throws IOException
*/
public EscPos line(int lineNum) throws IOException {
for (int i = 0; i < lineNum; i++) {
socketOut.write("\n".getBytes());
socketOut.flush();
}
return this;
}
/**
* 加粗
*
* @param flag false为不加粗
* @return
* @throws IOException
*/
public EscPos bold(boolean flag) throws IOException {
if (flag) {
socketOut.write(ESC_SETTING_BOLD);
}
return this;
}
/**
* 取消粗体
*
* @param flag true为取消粗体模式
* @return
* @throws IOException
*/
public EscPos boldOff(boolean flag) throws IOException {
if (flag) {
socketOut.write(ESC_CANCEL_BOLD);
//socketOut.write(FS_FONT_DOUBLE);
}
return this;
}
/**
* 初始化打印机
*
* @return
* @throws IOException
*/
EscPos init() throws IOException {
socketOut.write(ESC_INIT);
return this;
}
/**
* 打印空白
*
* @param length 需要打印空白的长度
* @throws IOException
*/
private void printSpace(int length) throws IOException {
for (int i = 0; i < length; i++) {
socketOut.write(" ".getBytes());
}
socketOut.flush();
}
/**
* 进纸并全部切割
*
* @return
* @throws IOException
*/
public EscPos feedAndCut() throws IOException {
socketOut.write(POS_CUT_MODE_FULL);
socketOut.flush();
return this;
}
public void closeIOAndSocket() {
try {
socketOut.close();
socket.close();
if (listener != null) {
listener.onListener(STATE_3);
}
} catch (IOException e) {
if (listener != null) {
listener.onError(ERROR_4);
}
LOGGER.error("Socket printer close ===> ", e);
e.printStackTrace();
}
}
/**
* 打印字符串
*
* @param str 所需打印字符串
* @return
* @throws IOException
*/
public EscPos printStr(String str) throws IOException {
socketOut.write(ESC_CN_FONT);
socketOut.write(str.getBytes(Charset.forName("GBK")));
socketOut.write(LF);
socketOut.flush();
return this;
}
public void setListener(OnPrinterListener listener) {
this.listener = listener;
}
public interface OnPrinterListener {
void onListener(int status);
void onError(int errorCode);
}
}
打印调用示例代码
//printerData 是list类型的数据 循环打印文案
EscPos.myPrinter(printerData, new EscPos.OnPrinterListener() {
@Override
public void onListener(int status) {
}
@Override
public void onError(final int errorCode) {
((Activity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
//此时已在主线程中,可以更新UI了
if (errorCode == EscPos.ERROR_2) {
Toast.makeText(context, "连接打印机失败", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "打印异常", Toast.LENGTH_SHORT).show();
}
}
});
}
});
过程遇到的坑
socket连不上打印机
1.确保打印机的网和手机在同一个网段内
2.打印机端口和地址正确(可百度查询如何确定打印机IP和端口)
socket发送打印指令不起作用
这个当时坑死我了,我用一个串测试“hello word” ,结果就因为中间多加了空格,就是打印不出来,后来才知道,这种空格没法打印,一定要写个简单的串先测试。