【Android连接蓝牙并打印(仅支持票据模式)】

蓝牙连接和打印数据发送

/**
 * 保持蓝牙连接的线程
 */
public class BluetoothThread extends Thread {

    // 固定不变,表示蓝牙通用串口服务
    private static final UUID BLUETOOTH_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    private BluetoothSocket mSocket; // 蓝牙连接的socket

    private OutputStream mOutputStream;   // 用于打印数据的输出流

    private Handler mHandler;
    private Callback mCallback;

    private BluetoothDevice mDevice;

    public BluetoothThread(BluetoothDevice bondedDevice) throws Exception {
        mDevice = bondedDevice;
        if (bondedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
            throw new Exception("BluetoothDevice has not bonded.");
        }

        try {
            mSocket = bondedDevice.createRfcommSocketToServiceRecord(BLUETOOTH_UUID);  // 创建连接的Socket对象
            mSocket.connect();

            mOutputStream = mSocket.getOutputStream();
        } catch (IOException e) {
            if (mSocket != null) {
                try {
                    mSocket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }

            throw new Exception("BluetoothDevice connect fail. " + e.getMessage());
        }
    }

    public void quit() {
        if (mHandler == null) {
            return;
        }

        mHandler.sendEmptyMessage(-1);
    }

    /**
     * 发送打印信息(文本或图片  字节码数值格式)
     * @param bytes
     */
    public void write(byte[] bytes) {
        if (mHandler == null) {
            return;
        }
        Message msg = mHandler.obtainMessage(0);
        msg.obj = bytes;
        mHandler.sendMessage(msg);
    }

    public boolean isInnerPrinter() {
        return "00:11:22:33:44:55".equals(mDevice.getAddress());
    }

    @SuppressLint("HandlerLeak")
    @Override
    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (msg.what == -1) {
                    Looper looper = Looper.myLooper();
                    if (looper != null) {
                        looper.quit();
                    }
                    removeCallbacksAndMessages(null);
                    mHandler = null;
                } else {
                    byte[] bytes = (byte[]) msg.obj;
                    try {

                        mOutputStream.write(bytes);

                        if (mCallback != null) {
                            mCallback.onWriteFinished(mDevice);
                        }
                    } catch (IOException e) {
                        if (mCallback != null) {
                            mCallback.onWriteFail(mDevice);
                        }
                    }
                }
            }
        };

        Looper.loop();

        // 线程结束,则关闭
        try {
            mOutputStream.close();
            mSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void setCallback(Callback callback) {
        mCallback = callback;
    }

    interface Callback {
        void onWriteFinished(BluetoothDevice device);

        void onWriteFail(BluetoothDevice device);
    }
}

打印管理类

/**
 * 负责蓝牙相关的业务逻辑
 */
public class BluetoothManager {

    private static BluetoothManager sBluetoothManager; // 防止创建多次,设置为单例
    private static Map<BluetoothDevice, BluetoothThread> mBluetoothThreadMap = new HashMap<>();

    private int mPrintingThreadCount = 0;

    private BluetoothManager() {
        // 通过getInstance()方法获取实例
    }

    /**
     * 获取当前类示例
     */
    public synchronized static BluetoothManager getInstance() {
        if (sBluetoothManager == null) {
            sBluetoothManager = new BluetoothManager();
        }
        return sBluetoothManager;
    }

    /**
     * 连接蓝牙设备
     */
    public static boolean connect(BluetoothDevice device) {
        BluetoothAdapter.getDefaultAdapter().cancelDiscovery();

        BluetoothThread thread = mBluetoothThreadMap.get(device);
        if (thread == null) {
            try {
                thread = new BluetoothThread(device);
                thread.start();

                mBluetoothThreadMap.put(device, thread);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        } else {
            return true;
        }
    }

    private void preparePrint() {
        int count;
        do {
            synchronized (BluetoothManager.class) {
                count = mPrintingThreadCount;
                if (count <= 0) {
                    mPrintingThreadCount = mBluetoothThreadMap.size();
                }
            }
        } while (count > 0);
    }

    /**
     * 断开当前正在连接的蓝牙设备
     */
    public void disconnect(BluetoothDevice device) {
        BluetoothThread thread = mBluetoothThreadMap.get(device);
        if (thread != null) {
            thread.quit();
            mBluetoothThreadMap.remove(device);
        }
    }

    /**
     * 指定某个蓝牙设备打印文本内容
     *
     * @param text 打印的文本内容
     */
    public void printText(BluetoothDevice device, String text, final OnPrintListener listener) {
        if (TextUtils.isEmpty(text)) {
            return;
        }
        byte[] bytes = new byte[0];
        try {
            bytes = text.getBytes("GBK");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        addListenerAndPrint(device,bytes,listener);
    }

    /**
     * 指定某个蓝牙设备打印图片
     * @param device
     * @param bitmap
     */
    public void printBitmap(BluetoothDevice device, Bitmap bitmap, final OnPrintListener listener) {
        if (null==bitmap) {
            return;
        }
        byte[] bit = PrintUtil.draw2PxPoint(bitmap);
        addListenerAndPrint(device,bit,listener);
    }

    private void addListenerAndPrint(BluetoothDevice device,byte[] bit,final OnPrintListener listener){
        final BluetoothThread thread = mBluetoothThreadMap.get(device);
        if (thread == null) {
            throw new RuntimeException("蓝牙连接不存在!");
        }
        if (listener != null) {
            thread.setCallback(new BluetoothThread.Callback() {
                @Override
                public void onWriteFinished(BluetoothDevice device) {
                    thread.setCallback(null);
                    listener.onPrintFinished();
                }

                @Override
                public void onWriteFail(BluetoothDevice device) {
                    thread.setCallback(null);
                    listener.onPrintFail(device);
                }
            });
        }
        thread.write(bit);
    }

    public interface OnPrintListener {
        void onPrintFinished();

        void onPrintFail(BluetoothDevice device);
    }
}

打印工具类

public class PrintUtil {

    /**
     * 对图片进行压缩(去除透明度)
     *
     * @param bitmap
     */
    public static Bitmap compressPic(Bitmap bitmap) {
        // 获取这个图片的宽和高
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        // 指定调整后的宽度和高度
        int newWidth = 240;
        int newHeight = 240;
        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;
    }

    /*************************************************************************
     * 假设一个240*240的图片,分辨率设为24, 共分10行打印
     * 每一行,是一个 240*24 的点阵, 每一列有24个点,存储在3个byte里面。
     * 每个byte存储8个像素点信息。因为只有黑白两色,所以对应为1的位是黑色,对应为0的位是白色
     **************************************************************************/
    /**
     * 把一张Bitmap图片转化为打印机可以打印的字节流
     *
     * @param bmp
     * @return
     */
    public static byte[] draw2PxPoint(Bitmap bmp) {
//        bmp = compressPic(bmp);
        //用来存储转换后的 bitmap 数据。为什么要再加1000,这是为了应对当图片高度无法
        //整除24时的情况。比如bitmap 分辨率为 240 * 250,占用 7500 byte,
        //但是实际上要存储11行数据,每一行需要 24 * 240 / 8 =720byte 的空间。再加上一些指令存储的开销,
        //所以多申请 1000byte 的空间是稳妥的,不然运行时会抛出数组访问越界的异常。
        int size = bmp.getWidth() * bmp.getHeight() / 8 + 5000;
        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;//换行
        }
        return data;
    }

    /**
     * 灰度图片黑白化,黑色是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;
    }
}

最后

代码整合了诸多大佬的博客,非原创。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值