记录下最近项目开发中使用到的蓝牙打印机功能
/**
* 从已配对设配中,删选出某一特定类型的设备展示
*
* @param deviceClass
* @return
*/
public static List<BluetoothDevice> getSpecificDevice(int deviceClass) {
List<BluetoothDevice> devices = BluetoothUtil.getPairedDevices();
List<BluetoothDevice> printerDevices = new ArrayList<>();
for (BluetoothDevice device : devices) {
@SuppressLint("MissingPermission") BluetoothClass klass = device.getBluetoothClass();
// 关于蓝牙设备分类参考 http://stackoverflow.com/q/23273355/4242112
if (klass.getMajorDeviceClass() == deviceClass)
printerDevices.add(device);
}
return printerDevices;
}
/**
* 获取所有已配对的设备
*/
@SuppressLint("MissingPermission")
public static List<BluetoothDevice> getPairedDevices() {
List deviceList = new ArrayList<>();
if (BluetoothAdapter.getDefaultAdapter() != null && BluetoothAdapter.getDefaultAdapter().getBondedDevices() != null) {
Set<BluetoothDevice> pairedDevices = BluetoothAdapter.getDefaultAdapter().getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
deviceList.add(device);
}
}
}
return deviceList;
}
public void connectDevice(BluetoothDevice device, int taskType) {
if (checkBluetoothState() && device != null) {
mConnectTask = new ConnectBluetoothTask(taskType).execute(device);
}
}
class ConnectBluetoothTask extends AsyncTask<BluetoothDevice, Integer, BluetoothSocket> {
int mTaskType;
public ConnectBluetoothTask(int taskType) {
this.mTaskType = taskType;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected BluetoothSocket doInBackground(BluetoothDevice... params) {
try {
if (mSocket != null) {
mSocket.close();
}
mSocket = BluetoothUtil.connectDevice(params[0]);
return mSocket;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(BluetoothSocket socket) {
checkSocket(socket);
super.onPostExecute(socket);
}
}
private void checkSocket(BluetoothSocket socket) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (socket == null || !socket.isConnected()) {
LogUtils.v(TAG, "连接打印机失败");
connectDevice(selectDevice, 2);
} else {
LogUtils.v(TAG, "连接打印机成功");
loadingDialog = LoadingUtils.showLoadingText(SalesBillingOrderActivity.this, ConstantValue.PRINT_TEXT);
printData(socket);
}
}
}, 200);
}
public void printData(BluetoothSocket bluetoothSocket) {
try {
PrintUtil pUtil = new PrintUtil(bluetoothSocket.getOutputStream(), "GBK");
// 店铺名 居中 放大
pUtil.printAlignment(1);
if (!TextUtils.isEmpty(printUserBean.getData().getBossmanName())) {
pUtil.printLargeText(printUserBean.getData().getBossmanName() + "销售单");
pUtil.printLine(2);
}
pUtil.printAlignment(0);
pUtil.printText("客户: " + getIntent().getStringExtra(ConstantValue.NAME));
pUtil.printLine();
pUtil.printAlignment(0);
pUtil.printText("日期: " + TimeSelectUtils.getFormatTime(new Date()));
pUtil.printLine();
// 分隔线
pUtil.printDashLine();
pUtil.printLine();
if (productlistBeansList != null && productlistBeansList.size() > 0) {
for (int i = 0; i < productlistBeansList.size(); i++) {
pUtil.printAlignment(0);
pUtil.printText((i + 1) + " " + productlistBeansList.get(i).getProductName());
pUtil.printLine();
pUtil.printAlignment(0);
pUtil.printText(productlistBeansList.get(i).getSpecifications() + " " + productlistBeansList.get(i).getBarCode());
pUtil.printLine();
if (productlistBeansList.get(i).isZpFlag()) {
pUtil.printThreeColumn(productlistBeansList.get(i).getLevelPrice() + "元", "x" + productlistBeansList.get(i).getSelectNum() + productlistBeansList.get(i).getUnitName(), "赠品");
} else {
pUtil.printThreeColumn(productlistBeansList.get(i).getLevelPrice() + "元", "x" + productlistBeansList.get(i).getSelectNum() + productlistBeansList.get(i).getUnitName(), Float.parseFloat(productlistBeansList.get(i).getLevelPrice()) * Float.parseFloat(productlistBeansList.get(i).getSelectNum()) + "元");
}
pUtil.printLine(1);
}
}
pUtil.printLine();
pUtil.printAlignment(2);
pUtil.printText("合计金额:" + mTvTotalPrice.getText().toString().trim() + "元");
pUtil.printLine();
//添加费用支出
if (mCheckBox.isChecked()) {
pUtil.printText("费用名称:" + selectValue);
pUtil.printLine();
pUtil.printText("费用金额:" + mEtFyje.getText().toString().trim() + "元");
pUtil.printLine();
pUtil.printText("费用说明:" + mEtFysm.getText().toString().trim());
pUtil.printLine();
pUtil.printText("应收金额:" + mTvYsje.getText().toString().trim() + "元");
pUtil.printLine();
}
//
pUtil.printDashLine();
pUtil.printLine(1);
pUtil.printAlignment(0);
pUtil.printText("备注: ");
pUtil.printLine(3);
pUtil.printDashLine();
pUtil.printLine();
if (!TextUtils.isEmpty(printUserBean.getData().getSalesmanName())) {
saleName = printUserBean.getData().getSalesmanName();
} else {
saleName = "";
}
if (!TextUtils.isEmpty(printUserBean.getData().getSalesmanTel())) {
saleTel = printUserBean.getData().getSalesmanTel();
} else {
saleTel = "";
}
pUtil.printAlignment(0);
pUtil.printText("业务员: " + saleName + " " + saleTel);
pUtil.printLine();
if (!TextUtils.isEmpty(printUserBean.getData().getBossmanTel1())) {
bossTelOne = printUserBean.getData().getBossmanTel1();
} else {
bossTelOne = "";
}
if (!TextUtils.isEmpty(printUserBean.getData().getBossmanTel2())) {
bossTelTwo = printUserBean.getData().getBossmanTel2();
} else {
bossTelTwo = "";
}
pUtil.printAlignment(0);
pUtil.printText("公司电话: " + bossTelOne + " " + bossTelTwo);
pUtil.printLine();
if (!TextUtils.isEmpty(printUserBean.getData().getBossmanLoc())) {
bossLoc = printUserBean.getData().getBossmanLoc();
} else {
bossLoc = "";
}
pUtil.printAlignment(0);
pUtil.printText("公司地址: " + bossLoc);
pUtil.printLine(1);
if (!TextUtils.isEmpty(printUserBean.getData().getTailContent())) {
pUtil.printAlignment(0);
pUtil.printText(printUserBean.getData().getTailContent());
pUtil.printLine(3);
}
LoadingUtils.hideLoadingDelay(loadingDialog);
} catch (IOException e) {
e.printStackTrace();
ToastUtils.showShort("打印失败");
}
}
/**
* 蓝牙打印工具类
*/
public class PrintUtil {
private OutputStreamWriter mWriter = null;
private OutputStream mOutputStream = null;
// public final static int WIDTH_PIXEL = 550;
public final static int WIDTH_PIXEL = 458;
public final static int IMAGE_SIZE = 320;
/**
* 初始化Pos实例
*
* @param encoding 编码
* @throws IOException
*/
public PrintUtil(OutputStream outputStream, String encoding) throws IOException {
mWriter = new OutputStreamWriter(outputStream, encoding);
mOutputStream = outputStream;
initPrinter();
}
public void print(byte[] bs) throws IOException {
mOutputStream.write(bs);
}
public void printRawBytes(byte[] bytes) throws IOException {
mOutputStream.write(bytes);
mOutputStream.flush();
}
/**
* 初始化打印机
*
* @throws IOException
*/
public void initPrinter() throws IOException {
mWriter.write(0x1B);
mWriter.write(0x40);
mWriter.flush();
}
/**
* 打印换行
*
* @return length 需要打印的空行数
* @throws IOException
*/
public void printLine(int lineNum) throws IOException {
for (int i = 0; i < lineNum; i++) {
mWriter.write("\n");
}
mWriter.flush();
}
/**
* 打印换行(只换一行)
*
* @throws IOException
*/
public void printLine() throws IOException {
printLine(1);
}
/**
* 打印空白(一个Tab的位置,约4个汉字)
*
* @param length 需要打印空白的长度,
* @throws IOException
*/
public void printTabSpace(int length) throws IOException {
for (int i = 0; i < length; i++) {
mWriter.write("\t");
}
mWriter.flush();
}
/**
* 绝对打印位置
*
* @return
* @throws IOException
*/
public byte[] setLocation(int offset) throws IOException {
byte[] bs = new byte[4];
bs[0] = 0x1B;
bs[1] = 0x24;
bs[2] = (byte) (offset % 256);
bs[3] = (byte) (offset / 256);
return bs;
}
public byte[] getGbk(String stText) throws IOException {
byte[] returnText = stText.getBytes("GBK"); // 必须放在try内才可以
return returnText;
}
private int getStringPixLength(String str) {
int pixLength = 0;
char c;
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (Pinyin.isChinese(c)) {
pixLength += 24;
} else {
pixLength += 12;
}
}
return pixLength;
}
public int getOffset(String str) {
return WIDTH_PIXEL - getStringPixLength(str);
}
/**
* 打印文字
*
* @param text
* @throws IOException
*/
public void printText(String text) throws IOException {
mWriter.write(text);
mWriter.flush();
}
/**
* 对齐0:左对齐,1:居中,2:右对齐
*/
public void printAlignment(int alignment) throws IOException {
mWriter.write(0x1b);
mWriter.write(0x61);
mWriter.write(alignment);
}
public void printLargeText(String text) throws IOException {
mWriter.write(0x1b);
mWriter.write(0x21);
// mWriter.write(0x30);
mWriter.write(48);
mWriter.write(text);
mWriter.write(0x1b);
mWriter.write(0x21);
mWriter.write(0);
mWriter.flush();
}
public void printTwoColumn(String title, String content) throws IOException {
int iNum = 0;
byte[] byteBuffer = new byte[100];
byte[] tmp;
tmp = getGbk(title);
System.arraycopy(tmp, 0, byteBuffer, iNum, tmp.length);
iNum += tmp.length;
tmp = setLocation(getOffset(content));
System.arraycopy(tmp, 0, byteBuffer, iNum, tmp.length);
iNum += tmp.length;
tmp = getGbk(content);
System.arraycopy(tmp, 0, byteBuffer, iNum, tmp.length);
print(byteBuffer);
}
public void printThreeColumn(String left, String middle, String right) throws IOException {
int iNum = 0;
byte[] byteBuffer = new byte[200];
byte[] tmp = new byte[0];
System.arraycopy(tmp, 0, byteBuffer, iNum, tmp.length);
iNum += tmp.length;
tmp = getGbk(left);
System.arraycopy(tmp, 0, byteBuffer, iNum, tmp.length);
iNum += tmp.length;
int pixLength = getStringPixLength(left) % WIDTH_PIXEL;
if (pixLength > WIDTH_PIXEL / 2 || pixLength == 0) {
middle = "\n\t\t" + middle;
}
tmp = setLocation(205);
System.arraycopy(tmp, 0, byteBuffer, iNum, tmp.length);
iNum += tmp.length;
tmp = getGbk(middle);
System.arraycopy(tmp, 0, byteBuffer, iNum, tmp.length);
iNum += tmp.length;
tmp = setLocation(getOffset(right));
System.arraycopy(tmp, 0, byteBuffer, iNum, tmp.length);
iNum += tmp.length;
tmp = getGbk(right);
System.arraycopy(tmp, 0, byteBuffer, iNum, tmp.length);
print(byteBuffer);
}
public void printDashLine() throws IOException {
printText("-------------------------------------");
}
public void printBitmap(Bitmap bmp) throws IOException {
bmp = compressPic(bmp);
byte[] bmpByteArray = draw2PxPoint(bmp);
printRawBytes(bmpByteArray);
}
/*************************************************************************
* 假设一个360*360的图片,分辨率设为24, 共分15行打印 每一行,是一个 360 * 24 的点阵,y轴有24个点,存储在3个byte里面。
* 即每个byte存储8个像素点信息。因为只有黑白两色,所以对应为1的位是黑色,对应为0的位是白色
**************************************************************************/
private byte[] draw2PxPoint(Bitmap bmp) {
//先设置一个足够大的size,最后在用数组拷贝复制到一个精确大小的byte数组中
int size = bmp.getWidth() * bmp.getHeight() / 8 + 1000;
byte[] tmp = new byte[size];
int k = 0;
// 设置行距为0
tmp[k++] = 0x1B;
tmp[k++] = 0x33;
tmp[k++] = 0x00;
// 居中打印
tmp[k++] = 0x1B;
tmp[k++] = 0x61;
tmp[k++] = 1;
for (int j = 0; j < bmp.getHeight() / 24f; j++) {
tmp[k++] = 0x1B;
tmp[k++] = 0x2A;// 0x1B 2A 表示图片打印指令
tmp[k++] = 33; // m=33时,选择24点密度打印
tmp[k++] = (byte) (bmp.getWidth() % 256); // nL
tmp[k++] = (byte) (bmp.getWidth() / 256); // nH
for (int i = 0; i < bmp.getWidth(); i++) {
for (int m = 0; m < 3; m++) {
for (int n = 0; n < 8; n++) {
byte b = px2Byte(i, j * 24 + m * 8 + n, bmp);
tmp[k] += tmp[k] + b;
}
k++;
}
}
tmp[k++] = 10;// 换行
}
// 恢复默认行距
tmp[k++] = 0x1B;
tmp[k++] = 0x32;
byte[] result = new byte[k];
System.arraycopy(tmp, 0, result, 0, k);
return result;
}
/**
* 图片二值化,黑色是1,白色是0
*
* @param x 横坐标
* @param y 纵坐标
* @param bit 位图
* @return
*/
private 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 int RGB2Gray(int r, int g, int b) {
int gray = (int) (0.29900 * r + 0.58700 * g + 0.11400 * b); // 灰度转化公式
return gray;
}
/**
* 对图片进行压缩(去除透明度)
*
* @param bitmapOrg
*/
private Bitmap compressPic(Bitmap bitmapOrg) {
// 获取这个图片的宽和高
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
// 定义预转换成的图片的宽度和高度
int newWidth = IMAGE_SIZE;
int newHeight = IMAGE_SIZE;
Bitmap targetBmp = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
Canvas targetCanvas = new Canvas(targetBmp);
targetCanvas.drawColor(0xffffffff);
targetCanvas.drawBitmap(bitmapOrg, new Rect(0, 0, width, height), new Rect(0, 0, newWidth, newHeight), null);
return targetBmp;
}
}
以上是蓝牙打印小票的记录。