Android 蓝牙打印小票与WiFi打印小票两种打印方式的实现(带有图片和二维码) |
您所在的位置:网站首页 › 手机如何打印小票 › Android 蓝牙打印小票与WiFi打印小票两种打印方式的实现(带有图片和二维码) |
转载至: https://blog.csdn.net/u011056653/article/details/74308254 最近在做小票打印这块,项目需求是蓝牙和WiFi两种都要实现,开始做的时候也是一脸懵,然后网上找了一些代码,看了好多博主的文章,最后自己把这些都综合到一起0.0,结果还好成功了 先看看图吧,蓝牙和WiFi打印出的都是一样的,毕竟都是同一个工具类(打印机型号 爱宝POS-80C 、 资江 ZJ-80) 先来看看WiFi打印(旁边的色块是打印纸自带的,我也很无奈) 首先在你的activity里触发点击事件 right = (TextView) findViewById(R.id.right);//点击WiFi打印 right.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { pos(); } }); //调用打印的方法private void pos() { // 开启一个子线程 new Thread() { public void run() { try { //打印机网口IP 打印机端口号 编码方式 pos = new Pos(Preference.instance(PayTypeActivity.this).getIPAddress(), Integer.parseInt(Preference.instance(PayTypeActivity.this).getPost_IDs()), "gbk"); //初始化打印机 pos.initPos(); if (!TextUtils.isEmpty(objBean.getHead_img())){ pos.printLocation(1); bitmap = pos.compressPic(returnBitMap(objBean.getHead_img())); pos.draw2PxPoint(bitmap); } pos.printLocation(1); pos.bold(true); pos.printTabSpace(2); pos.printWordSpace(1); pos.printTextNewLine(objBean.getShop_name()); pos.printLocation(0); pos.printTextNewLine("----------------------------------------------"); pos.bold(false); pos.printTextNewLine("订 单 号:"+objBean.getSn()); pos.printTextNewLine("用 户 名:"+objBean.getMember()); pos.printTextNewLine("订单日期:"+objBean.getCreate_time()); pos.printTextNewLine("支付方式:"+objBean.getPay_way()); pos.printTextNewLine("订单折扣:"+objBean.getDiscount()); pos.printTextNewLine("订单备注:"+objBean.getRemarks()); pos.printLine(2); pos.printText("货号 尺码 颜色 数量 小计"); pos.printLocation(20, 1); pos.printTextNewLine("----------------------------------------------"); for (ParentBean foods : foodsBean) { pos.printTextNewLine(foods.getGoods_name()+" "+foods.getCode_number()+" "+foods.getColour()+" "+foods.getNum()+" "+foods.getPay_back()); pos.printLocation(20, 1); } pos.printTextNewLine("----------------------------------------------"); pos.printLocation(0); pos.printLine(1); pos.printTextNewLine("订单总数:"+objBean.getNums()); pos.printTextNewLine("应收金额:"+objBean.getActual_pay()); pos.printTextNewLine("优惠金额:"+objBean.getDiscount_pay()); pos.printTextNewLine("实收金额:"+objBean.getPay()); pos.printLine(2); // //打印二维码 -- 如果提供了二维码的地址则用该方法 // pos.qrCode(objBean.getQr_code()); //打印二维码的图片 -- 如果提供了二维码的截图则用该方法 if (!TextUtils.isEmpty(objBean.getQr_code())){ pos.printLocation(1); Qbitmap = pos.compressPic(returnBitMap(objBean.getQr_code()));//url 转换为bitmap pos.draw2PxPoint(Qbitmap); } //切纸 pos.feedAndCut(); pos.closeIOAndSocket(); pos = null; } catch (UnknownHostException e) { e.printStackTrace(); showToast(e.toString()); Log.d("tag", "错误信息1:" + e.toString()); } catch (IOException e) { e.printStackTrace(); showToast(e.toString()); Log.d("tag", "错误信息2:" + e.toString()); } } }.start(); } //将图片url转换为bitmap public Bitmap returnBitMap(String url) { URL myFileUrl = null; Bitmap bitmap = null; try { myFileUrl = new URL(url); } catch (MalformedURLException e) { e.printStackTrace(); } try { HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection(); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(is); is.close(); } catch (IOException e) { e.printStackTrace(); } return bitmap; } 再来看看调用的工具类(蓝牙和WiFi共用一个,只是初始化方式不同) import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.Socket; public class Pos { //定义编码方式 private static String encoding = null; private Socket sock = null; // 通过socket流进行读写 private OutputStream socketOut = null; private OutputStreamWriter writer = null; /** * 初始化Pos实例 * * @param ip 打印机IP * @param port 打印机端口号 * @param encoding 编码 * @throws IOException */ public Pos(String ip, int port, String encoding) throws IOException { sock = new Socket(ip, port); socketOut = new DataOutputStream(sock.getOutputStream()); this.encoding = encoding; writer = new OutputStreamWriter(socketOut, encoding); } //蓝牙初始化 public Pos(OutputStream outputStream, String encoding) throws IOException { writer = new OutputStreamWriter(outputStream, encoding); socketOut = outputStream; initPrinter(); } public void print(byte[] bs) throws IOException { socketOut.write(bs); } public void printRawBytes(byte[] bytes) throws IOException { socketOut.write(bytes); socketOut.flush(); } /** * 初始化打印机 * * @throws IOException */ public void initPrinter() throws IOException { socketOut.write(0x1B); socketOut.write(0x40); socketOut.flush(); } /** * 关闭IO流和Socket * * @throws IOException */ public void closeIOAndSocket() throws IOException { writer.close(); socketOut.close(); sock.close(); } /** * 打印二维码 * * @param qrData 二维码的内容 * @throws IOException */ public void qrCode(String qrData) throws IOException { int moduleSize = 8; int length = qrData.getBytes(encoding).length; //打印二维码矩阵 writer.write(0x1D);// init writer.write("(k");// adjust height of barcode writer.write(length + 3); // pl writer.write(0); // ph writer.write(49); // cn writer.write(80); // fn writer.write(48); // writer.write(qrData); writer.write(0x1D); writer.write("(k"); writer.write(3); writer.write(0); writer.write(49); writer.write(69); writer.write(48); writer.write(0x1D); writer.write("(k"); writer.write(3); writer.write(0); writer.write(49); writer.write(67); writer.write(moduleSize); writer.write(0x1D); writer.write("(k"); writer.write(3); // pl writer.write(0); // ph writer.write(49); // cn writer.write(81); // fn writer.write(48); // m writer.flush(); } /** * 进纸并全部切割 * * @return * @throws IOException */ public void feedAndCut() throws IOException { writer.write(0x1D); writer.write(86); writer.write(65); // writer.write(0); //切纸前走纸多少 writer.write(100); writer.flush(); //另外一种切纸的方式 // byte[] bytes = {29, 86, 0}; // socketOut.write(bytes); } /** * 打印换行 * * @return length 需要打印的空行数 * @throws IOException */ public void printLine(int lineNum) throws IOException { for (int i = 0; i < lineNum; i++) { writer.write("\n"); } writer.flush(); } /** * 打印换行(只换一行) * * @throws IOException */ protected void printLine() throws IOException { writer.write("\n"); writer.flush(); } /** * 打印空白(一个Tab的位置,约4个汉字) * * @param length 需要打印空白的长度, * @throws IOException */ public void printTabSpace(int length) throws IOException { for (int i = 0; i < length; i++) { writer.write("\t"); } writer.flush(); } /** * 打印空白(一个汉字的位置) * * @param length 需要打印空白的长度, * @throws IOException */ public void printWordSpace(int length) throws IOException { for (int i = 0; i < length; i++) { writer.write(" "); } writer.flush(); } /** * 打印位置调整 * * @param position 打印位置 0:居左(默认) 1:居中 2:居右 * @throws IOException */ public void printLocation(int position) throws IOException { writer.write(0x1B); writer.write(97); writer.write(position); writer.flush(); } /** * 绝对打印位置 * * @throws IOException */ public void printLocation(int light, int weight) throws IOException { writer.write(0x1B); writer.write(0x24); writer.write(light); writer.write(weight); writer.flush(); } /** * 打印文字 * * @param text * @throws IOException */ public void printText(String text) throws IOException { String s = text; byte[] content = s.getBytes("gbk"); socketOut.write(content); socketOut.flush(); } /** * 新起一行,打印文字 * * @param text * @throws IOException */ public void printTextNewLine(String text) throws IOException { //换行 writer.write("\n"); writer.flush(); String s = text; byte[] content = s.getBytes("gbk"); socketOut.write(content); socketOut.flush(); } /** * 初始化打印机 * * @throws IOException */ public void initPos() throws IOException { writer.write(0x1B); writer.write(0x40); writer.flush(); } /** * 加粗 * * @param flag false为不加粗 * @return * @throws IOException */ public void bold(boolean flag) throws IOException { if (flag) { //常规粗细 writer.write(0x1B); writer.write(69); writer.write(0xF); writer.flush(); } else { //加粗 writer.write(0x1B); writer.write(69); writer.write(0); writer.flush(); } } /** * 对图片进行压缩(去除透明度) * * @param */ public static Bitmap compressPic(Bitmap bitmap) { // 获取这个图片的宽和高 int width = bitmap.getWidth(); int height = bitmap.getHeight(); // 指定调整后的宽度和高度 int newWidth = 200; int newHeight = 200; Bitmap targetBmp = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888); Canvas targetCanvas = new Canvas(targetBmp); targetCanvas.drawColor(0xffffffff); targetCanvas.drawBitmap(bitmap, new Rect(0, 0, width, height), new Rect(0, 0, newWidth, newHeight), null); return targetBmp; } /** * 灰度图片黑白化,黑色是1,白色是0 * * @param x 横坐标 * @param y 纵坐标 * @param bit 位图 * @return */ public static byte px2Byte(int x, int y, Bitmap bit) { if (x < bit.getWidth() && y < bit.getHeight()) { byte b; int pixel = bit.getPixel(x, y); int red = (pixel & 0x00ff0000) >> 16; // 取高两位 int green = (pixel & 0x0000ff00) >> 8; // 取中两位 int blue = pixel & 0x000000ff; // 取低两位 int gray = RGB2Gray(red, green, blue); if (gray < 128) { b = 1; } else { b = 0; } return b; } return 0; } /** * 图片灰度的转化 */ private static int RGB2Gray(int r, int g, int b) { int gray = (int) (0.29900 * r + 0.58700 * g + 0.11400 * b); //灰度转化公式 return gray; } /************************************************************************* * 假设一个240*240的图片,分辨率设为24, 共分10行打印 * 每一行,是一个 240*24 的点阵, 每一列有24个点,存储在3个byte里面。 * 每个byte存储8个像素点信息。因为只有黑白两色,所以对应为1的位是黑色,对应为0的位是白色 **************************************************************************/ /** * 把一张Bitmap图片转化为打印机可以打印的字节流 * * @param bmp * @return */ public void draw2PxPoint(Bitmap bmp) throws IOException { //用来存储转换后的 bitmap 数据。为什么要再加1000,这是为了应对当图片高度无法 //整除24时的情况。比如bitmap 分辨率为 240 * 250,占用 7500 byte, //但是实际上要存储11行数据,每一行需要 24 * 240 / 8 =720byte 的空间。再加上一些指令存储的开销, //所以多申请 1000byte 的空间是稳妥的,不然运行时会抛出数组访问越界的异常。 int size = bmp.getWidth() * bmp.getHeight() / 8 + 1000; byte[] data = new byte[size]; int k = 0; //设置行距为0的指令 data[k++] = 0x1B; data[k++] = 0x33; data[k++] = 0x00; // 逐行打印 for (int j = 0; j < bmp.getHeight() / 24f; j++) { //打印图片的指令 data[k++] = 0x1B; data[k++] = 0x2A; data[k++] = 33; data[k++] = (byte) (bmp.getWidth() % 256); //nL data[k++] = (byte) (bmp.getWidth() / 256); //nH //对于每一行,逐列打印 for (int i = 0; i < bmp.getWidth(); i++) { //每一列24个像素点,分为3个字节存储 for (int m = 0; m < 3; m++) { //每个字节表示8个像素点,0表示白色,1表示黑色 for (int n = 0; n < 8; n++) { byte b = px2Byte(i, j * 24 + m * 8 + n, bmp); data[k] += data[k] + b; } k++; } } data[k++] = 10;//换行 } socketOut.write(data); socketOut.flush(); } }
WiFi打印小票到此基本上就可以了 下面开始我们的蓝牙打印 蓝牙打印的话首先就要有权限 //蓝牙权限 再来看看蓝牙相关的布局
接着具体看看代码里面怎么实现的,包括蓝牙的一些检索和设置 public class PrinterSettingActivity extends BasePrintActivity implements View.OnClickListener{ private Root root; private String result; private ObjBean obj; private ObjBean objBean; private List foodsBean; private List listBeen; ListView mLvPairedDevices; Button mBtnSetting; Button mBtnTest; Button mBtnPrint; DeviceListAdapter mAdapter; int mSelectedPosition = -1; final static int TASK_TYPE_CONNECT = 1; final static int TASK_TYPE_PRINT = 2; private String id; private Pos pos; private OutputStream mOutputStream = null; private Bitmap head_bitmap; private Bitmap code_bitmap; private LinearLayout back; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_printer_setting); id = getIntent().getStringExtra("id"); setTitle("蓝牙打印"); initViews(); print();//获取小票数据 } private void print() { HttpUtil.ticket(this, id, new HttpUtil.KWiListener() { @Override public void onResult(int ret, Object msg) { Log.d("tag", "打印小票: " + msg.toString()); Gson gson = new Gson(); String jsonString = msg.toString(); root = gson.fromJson(jsonString, Root.class); result = root.getResult().getResultCode(); if (result.equals("0")) { objBean = root.getObj(); foodsBean = objBean.getParent(); } else { ToastUtil.show(root.getResult().getResultMessage()); } } }); } @Override protected void onResume() { super.onResume(); fillAdapter(); } private void initViews() { back = (LinearLayout) findViewById(R.id.back); back.setOnClickListener(this); mLvPairedDevices = (ListView) findViewById(R.id.lv_paired_devices); mBtnSetting = (Button) findViewById(R.id.btn_goto_setting); mBtnTest = (Button) findViewById(R.id.btn_test_conntect); mBtnPrint = (Button) findViewById(R.id.btn_print); mLvPairedDevices.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { mSelectedPosition = position; mAdapter.notifyDataSetChanged(); } }); mBtnSetting.setOnClickListener(this); mBtnTest.setOnClickListener(this); mBtnPrint.setOnClickListener(this); mAdapter = new DeviceListAdapter(this); mLvPairedDevices.setAdapter(mAdapter); } /** * 从所有已配对设备中找出打印设备并显示 */ private void fillAdapter() { //推荐使用 BluetoothUtil.getPairedPrinterDevices() List printerDevices = BluetoothUtil.getPairedDevices(); mAdapter.clear(); mAdapter.addAll(printerDevices); refreshButtonText(printerDevices); } private void refreshButtonText(List printerDevices) { if (printerDevices.size() > 0) { mBtnSetting.setText("配对更多设备"); } else { mBtnSetting.setText("还未配对打印机,去设置"); } } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_goto_setting: startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS)); break; case R.id.btn_test_conntect: connectDevice(TASK_TYPE_CONNECT); break; case R.id.btn_print: connectDevice(TASK_TYPE_PRINT); break; case R.id.back: finish(); break; default: break; } } private void connectDevice(int taskType){ if(mSelectedPosition >= 0){ BluetoothDevice device = mAdapter.getItem(mSelectedPosition); if(device!= null) super.connectDevice(device, taskType); }else{ Toast.makeText(this, "还未选择打印设备", Toast.LENGTH_SHORT).show(); } } @Override public void onConnected(BluetoothSocket socket, int taskType) { switch (taskType){ case TASK_TYPE_PRINT: if(socket != null&&socket.isConnected()){ pos(socket); } break; } } private void pos(final BluetoothSocket socket) { // 开启一个子线程 new Thread() { public void run() { try { //初始化打印机 pos = new Pos(socket.getOutputStream(),"GBK"); if (!TextUtils.isEmpty(objBean.getHead_img())){ pos.printLocation(1); head_bitmap = pos.compressPic(returnBitMap(objBean.getHead_img())); pos.draw2PxPoint(head_bitmap); } pos.printLocation(1); pos.bold(true); pos.printTabSpace(2); pos.printWordSpace(1); pos.printTextNewLine(objBean.getShop_name()); pos.printLocation(0); pos.printTextNewLine("----------------------------------------------"); pos.bold(false); pos.printTextNewLine("订 单 号:"+objBean.getSn()); pos.printTextNewLine("用 户 名:"+objBean.getMember()); pos.printTextNewLine("订单日期:"+objBean.getCreate_time()); pos.printTextNewLine("支付方式:"+objBean.getPay_way()); pos.printTextNewLine("订单折扣:"+objBean.getDiscount()); pos.printTextNewLine("订单备注:"+objBean.getRemarks()); pos.printLine(2); pos.printText("货号 尺码 颜色 数量 小计"); pos.printLocation(20, 1); pos.printTextNewLine("----------------------------------------------"); for (ParentBean foods : foodsBean) { pos.printTextNewLine(foods.getGoods_name()+" "+foods.getCode_number()+" "+foods.getColour()+" "+foods.getNum()+" "+foods.getPay_back()); pos.printLocation(20, 1); } pos.printTextNewLine("----------------------------------------------"); pos.printLocation(0); pos.printLine(1); pos.printTextNewLine("订单总数:"+objBean.getNums()); pos.printTextNewLine("应收金额:"+objBean.getActual_pay()); pos.printTextNewLine("优惠金额:"+objBean.getDiscount_pay()); pos.printTextNewLine("实收金额:"+objBean.getPay()); pos.printLine(2); //打印二维码 -- 如果提供了二维码的地址则用该方法 // pos.qrCode(objBean.getQr_code()); //打印二维码的图片 -- 如果提供了二维码的截图则用该方法 if (!TextUtils.isEmpty(objBean.getQr_code())){ pos.printLocation(1); code_bitmap = pos.compressPic(returnBitMap(objBean.getQr_code())); pos.draw2PxPoint(code_bitmap); } pos.printLine(4); //切纸 pos.feedAndCut(); // pos.closeIOAndSocket(); pos = null; } catch (UnknownHostException e) { Log.d("tag", "错误信息1:" + e.toString()); } catch (IOException e) { Log.d("tag", "错误信息2:" + e.toString()); } } }.start(); } class DeviceListAdapter extends ArrayAdapter { public DeviceListAdapter(Context context) { super(context, 0); } @Override public View getView(int position, View convertView, ViewGroup parent) { BluetoothDevice device = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_bluetooth_device, parent, false); } TextView tvDeviceName = (TextView) convertView.findViewById(R.id.tv_device_name); CheckBox cbDevice = (CheckBox) convertView.findViewById(R.id.cb_device); tvDeviceName.setText(device.getName()); cbDevice.setChecked(position == mSelectedPosition); return convertView; } } public Bitmap returnBitMap(String url) { URL myFileUrl = null; Bitmap bitmap = null; try { myFileUrl = new URL(url); } catch (MalformedURLException e) { e.printStackTrace(); } try { HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection(); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(is); is.close(); } catch (IOException e) { e.printStackTrace(); } return bitmap; } } R.layout.item_bluetooth_device的布局如下:
最后还是打印小票的方法 pos(socket);这个方法和之前WiFi方法基本一致,只是初始化不一样 private void pos(final BluetoothSocket socket) { // 开启一个子线程 new Thread() { public void run() { try { //初始化打印机 pos = new Pos(socket.getOutputStream(),"GBK"); if (!TextUtils.isEmpty(objBean.getHead_img())){ pos.printLocation(1); head_bitmap = pos.compressPic(returnBitMap(objBean.getHead_img())); pos.draw2PxPoint(head_bitmap); } pos.printLocation(1); pos.bold(true); pos.printTabSpace(2); pos.printWordSpace(1); pos.printTextNewLine(objBean.getShop_name()); pos.printLocation(0); pos.printTextNewLine("----------------------------------------------"); pos.bold(false); pos.printTextNewLine("订 单 号:"+objBean.getSn()); pos.printTextNewLine("用 户 名:"+objBean.getMember()); pos.printTextNewLine("订单日期:"+objBean.getCreate_time()); pos.printTextNewLine("支付方式:"+objBean.getPay_way()); pos.printTextNewLine("订单备注:"+objBean.getRemarks()); pos.printLine(2); pos.printText("货号 尺码 颜色 数量 小计"); pos.printLocation(20, 1); pos.printTextNewLine("----------------------------------------------"); for (ParentBean foods : foodsBean) { pos.printTextNewLine(foods.getGoods_name()+" "+foods.getCode_number()+" "+foods.getColour()+" "+foods.getNum()+" "+foods.getPay_back()); pos.printLocation(20, 1); } pos.printTextNewLine("----------------------------------------------"); pos.printLocation(0); pos.printLine(1); pos.printTextNewLine("总数:"+objBean.getNums()); pos.printTextNewLine("合计:"+objBean.getPay()); pos.printLine(2); //打印二维码 -- 如果提供了二维码的地址则用该方法 // pos.qrCode(objBean.getQr_code()); //打印二维码的图片 -- 如果提供了二维码的截图则用该方法 if (!TextUtils.isEmpty(objBean.getQr_code())){ pos.printLocation(1); code_bitmap = pos.compressPic(returnBitMap(objBean.getQr_code()));//returnBitmap方法和上面WiFi的一样 pos.draw2PxPoint(code_bitmap); } pos.printLine(4); //切纸 pos.feedAndCut(); // pos.closeIOAndSocket(); pos = null; } catch (UnknownHostException e) { Log.d("tag", "错误信息1:" + e.toString()); } catch (IOException e) { Log.d("tag", "错误信息2:" + e.toString()); } } }.start(); } 到此的话基本两种方式都可以了,我用两台打印机试过都没问题 下载地址:https://download.csdn.net/download/u011056653/10567104 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |