MUI蓝牙打印(Android)
使用MUI开发手机APP时使用蓝牙打印功能可能较少使用,MUI官方并为集成蓝牙打印功能,而且似乎对iPhone蓝牙打印的类库支持也不够完善。忙完一阶段后回顾下之前的工作,想想蓝牙打印功能折腾了够长时间了,写这篇文章既是自己做个总结,也希望能给遇到同样为蓝牙打印功能挣扎的人带来一点帮助。
感谢以下几篇文章给予的帮助。
- Android 蓝牙连接 ESC/POS 热敏打印机打印(ESC/POS指令篇)
-
以下打印的代码中使用了一些NativeJS的方法,如果对NativeJS没有了解的朋友,建议自行Google一下。我们直接上代码。
页面HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<!--标准mui.css-->
<link rel="stylesheet" href="../../css/mui.min.css">
<link rel="stylesheet" href="../../css/wg-common.css">
<link rel="stylesheet" href="../../css/iconfont.css">
<!--App自定义的css-->
<link rel="stylesheet" type="text/css" href="../../css/app.css" />
<title></title>
</head>
<style>
.wg-info-title {
background-color: white;
color: #007AFF;
}
.mui-btn {
font-size: 16px;
padding: 8px;
margin: 3px;
}
</style>
<body>
<header class="mui-bar mui-bar-nav" style="padding-top: 2px;">
<div style="float: left;padding-right: 8px;">
<a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
</div>
<h1 id="title" class="mui-title">打印</h1>
<div style="float: right;padding-right: 8px;">
<a class="mui-icon mui-icon-refreshempty" id="btnRefresh"></a>
</div>
</header>
<div class="mui-content mui-content-padded">
<h5 class="mui-content-padded">点击设备开始连接</h5>
<ul id="list1" class="mui-table-view mui-table-view-chevron">
</ul>
<br />
<h5 class="mui-content-padded">点击设备开始打印</h5>
<ul id="list2" class="mui-table-view mui-table-view-chevron">
</ul>
</div>
</body>
</html>
页面JS代码
// 导入的java包
var Context, BluetoothAdapter, BluetoothDevice;
// 蓝牙服务与适配
var BManager, BAdapter, BluetoothSocket, mDevice, receiver;
var mMain, mUUID;
var vlist1 = document.getElementById('list1'); //注册容器用来显示未配对设备
var vlist2 = document.getElementById('list2'); //注册容器用来显示未配对设备
var buttonRefresh = document.getElementById('btnRefresh');
document.addEventListener('plusready', function(event) {
var self = plus.webview.currentWebview();
// 打开蓝牙设备并扫描
if (mui.os.android) {
// 打开蓝牙
openAndroidBluetooth();
// 设置延时,防止蓝牙未完全开启时调用
CommonUtil.WaitFor(
function() {
return BAdapter.isEnabled();
},
function() {
// 获取已连接设备列表
getConnectedDevices();
},
3000);
}
});
// 刷新(重新扫描)
buttonRefresh.addEventListener('tap', function(event) {
buttonRefresh.disabled = true;
if (mui.os.android) {
// 扫描
searchDevices();
}
buttonRefresh.disabled = false;
});
// list1/list2的监听事件只能用on绑定,如果使用
// li2.addEventListener('tap', function(event) {
// print(li2.getAttribute('id'));
// });
// 会导致打印时socket超时,具体原因不明
mui('#list1').on('tap', 'li', function() {
connect(this.id);
})
mui('#list2').on('tap', 'li', function() {
print(this.id);
})
/**
*打开蓝牙(Android)
*/
function openAndroidBluetooth() {
mMain = plus.android.runtimeMainActivity();
Context = plus.android.importClass("android.content.Context");
BManager = mMain.getSystemService(Context.BLUETOOTH_SERVICE);
plus.android.importClass(BManager); //引入相关的method函数
BAdapter = BManager.getAdapter();
plus.android.importClass(BAdapter); //引入相关的method函数,这样之后才会有isEnabled函数支持
if (!BAdapter.isEnabled()) {
BAdapter.enable();
}
}
/**
* 获取已配对的蓝牙设备列表
*/
function getConnectedDevices() {
// var main = plus.android.runtimeMainActivity();
// var BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
// var BAdapter = BluetoothAdapter.getDefaultAdapter(); //获得本机蓝牙适配器
if (!BAdapter.isEnabled()) {
plus.nativeUI.toast('请先打开蓝牙');
return;
}
var lists = BAdapter.getBondedDevices(); //获取配对的设备列表
plus.android.importClass(lists);
var iterator = lists.iterator();
plus.android.importClass(iterator);
var vlist2 = document.getElementById('list2'); //注册容器用来显示未配对设备
while (iterator.hasNext()) {
var d = iterator.next();
plus.android.importClass(d);
var li2 = genLi(d);
vlist2.appendChild(li2);
}
}
/**
*扫描蓝牙设备
*/
function searchDevices() {
//console.log("开始搜索设备");
if (!BAdapter.isEnabled()) {
plus.nativeUI.toast('请先打开蓝牙');
return;
}
plus.nativeUI.showWaiting('正在搜索设备,请稍后...', {
back: 'none' // 可取值"none"表示截获处理返回键,但不做任何响应;"close"表示截获处理返回键并关闭等待框;"transmit"表示不截获返回键,向后传递给Webview窗口继续处理(与未显示等待框的情况一致)。
});
vlist1.innerHTML = ''; //清空容器
vlist2.innerHTML = ''; //清空容器
// 初始化蓝牙广播接收器
initReceiver();
// 开启搜索
BAdapter.startDiscovery();
// 初始化广播信息过滤
initIntentFilter();
}
function initReceiver() {
BluetoothDevice = plus.android.importClass("android.bluetooth.BluetoothDevice");
var bdevice = new BluetoothDevice();
receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
onReceive: function(context, intent) { //实现onReceiver回调函数
plus.android.importClass(intent); //通过intent实例引入intent类,方便以后的‘.’操作
//console.log(intent.getAction()); //获取action
if (intent.getAction() == "android.bluetooth.adapter.action.DISCOVERY_FINISHED") {
mMain.unregisterReceiver(receiver); //取消监听
//console.log("搜索结束")
plus.nativeUI.closeWaiting();
} else {
BleDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// console.log(JSON.stringify(BluetoothDevice));
// console.log(JSON.stringify(BleDevice));
//判断是否配对
if (BleDevice.getBondState() == bdevice.BOND_NONE) {
//console.log("未配对蓝牙设备:" + BleDevice.getName() + ' ' + BleDevice.getAddress());
if (!document.getElementById(BleDevice.getAddress())) { //判断防止重复添加
var li1 = genLi(BleDevice);
vlist1.appendChild(li1);
}
} else {
if (!document.getElementById(BleDevice.getAddress())) { //判断防止重复添加
//console.log("已配对蓝牙设备:" + BleDevice.getName() + ' ' + BleDevice.getAddress());
var li2 = genLi(BleDevice);
vlist2.appendChild(li2);
}
}
}
}
});
}
function initIntentFilter() {
// 设置广播信息过滤
var IntentFilter = plus.android.importClass('android.content.IntentFilter');
var filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BAdapter.ACTION_STATE_CHANGED);
// 注册广播接收器,接收并处理搜索结果
mMain.registerReceiver(receiver, filter);
}
function connect(mac_address) {
// 查找蓝牙设备
var unConnected = BAdapter.getRemoteDevice(mac_address);
if (unConnected) {
try {
if (BleDevice.createBond()) { //配对命令.createBond()
//console.log("配对成功");
}
} catch (e) {
//TODO handle the exception
mui.alert('连接到设备失败');
}
}
}
//mac_address:打印机的mac地址
function print(mac_address, str) {
if (!mac_address) {
mui.toast('请选择蓝牙打印机');
return;
}
if (mDevice == null) {
// mMain = plus.android.runtimeMainActivity();
// BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
// BAdapter = BluetoothAdapter.getDefaultAdapter();
UUID = plus.android.importClass("java.util.UUID");
mUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
mDevice = BAdapter.getRemoteDevice(mac_address);
plus.android.importClass(mDevice);
BluetoothSocket = mDevice.createInsecureRfcommSocketToServiceRecord(mUUID);
}
plus.android.importClass(BluetoothSocket);
if (!BluetoothSocket.isConnected()) {
//console.log('检测到设备未连接,尝试连接....');
try {
BluetoothSocket.connect();
} catch (e) {
//TODO handle the exception
mui.alert('连接超时');
return;
}
}
// 防止蓝牙未完成连接时调用打印
CommonUtil.WaitFor(
function() { // 等待条件
return BluetoothSocket.isConnected();
},
function() { // 回调方法
if (!BluetoothSocket.isConnected()) {
plus.nativeUI.toast('请连接打印机');
return;
}
printTest();
//printPictureTest();
}, 3000);
}
function printTest() {
// 初始化PintUtil
PrintUtil.init(BluetoothSocket);
// 以下测试打印
var printStr = '测试打印\r\n';
// 设置字体大小
PrintUtil.SetFontSize(30);
// 打印字符串
PrintUtil.PrintString(printStr);
// 设置字体大小
PrintUtil.SetFontSize(20);
// 打印字符串
PrintUtil.PrintString(printStr);
// 重置打印机
PrintUtil.Reset();
// 打印字符串
PrintUtil.PrintString(printStr);
// 切纸
PrintUtil.CutPage();
// 结束打印
PrintUtil.End();
}
function genLi(bleDevice) {
var li = document.createElement('li');
li.setAttribute('id', bleDevice.getAddress());
li.className = 'mui-table-view-cell';
var a = document.createElement('a');
a.setAttribute('class', 'mui-navigate-right')
a.innerText = bleDevice.getName();
li.appendChild(a);
return li;
}
辅助方法
页面上用到的几个方法也贴出来吧。
CommonUtil.WaitFor = function(condition, callback, timeout, unitTime) {
// 设置默认等待时间(循环间隔)
if(!unitTime || isNaN(unitTime)) {
unitTime = 100;
}
// 设置超时(到达超时则返回)
if(!timeout || isNaN(timeout)) {
timeout = 100;
}
if(condition && condition()) { // 等待条件成立,则执行回调
callback();
} else if(timeout - unitTime <= 0) { // 等待超时,则执行回调
callback();
} else { // 设置延时等待操作
setTimeout(function() {
owner.WaitFor(condition, callback, timeout - unitTime, unitTime);
}, unitTime);
}
};
PrintUtil打印公共方法
这里使用了一些打印机指令,具体的指令可以自己根据使用的打印机去找。
(function($, owner) {
owner.OutputStream = null;
owner.init = function(BluetoothSocket) {
owner.OutputStream = BluetoothSocket.getOutputStream();
plus.android.importClass(owner.OutputStream);
}
// 设置字体大小
owner.SetFontSize = function(n) {
var font = [0x1D, 0X21, n]
owner.OutputStream.write(font);
};
// 打印字符串
owner.PrintString = function(string) {
var bytes = plus.android.invoke(string, 'getBytes', 'gbk');
owner.OutputStream.write(bytes);
};
// 重置打印机
owner.Reset = function() {
var reset = [0x1B, 0X40];
owner.OutputStream.write(reset);
};
// 打印下划线
owner.Underline = function() {
// 下划线指令
var underline = [0x1b, 0x2d, 0x01];
owner.OutputStream.write(underline);
};
// 结束打印
owner.End = function() {
owner.OutputStream.flush();
var end = [0x1d, 0x4c, 0x1f, 0x00];
owner.OutputStream.write(end);
};
// 打印图片(暂不可用)
owner.Picture = function() {
var picture = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x40, 0x1B, 0x33, 0x00];
// var picture = [0x1B, 0x2A];
owner.OutputStream.write(picture);
};
// 切纸(暂不可用)
owner.CutPage = function() {
// 发送切纸指令
var end = [0x1B, 0x69];
owner.OutputStream.write(end);
};
// 条形码打印(暂不可用)
owner.PrintBarcode = function(n) {
var barcode = [0x1D, 0x6B, 65, 5, 11, 12, 3, 6, 23];
owner.OutputStream.write(barcode);
};
}(mui, window.PrintUtil = {}))
转自:
https://blog.csdn.net/u012843100/article/details/52384219?locationNum=15
好了,基本上代码都贴出来了,作为一个代码狗,很多时候我都相信:Talk is cheap, show me the code。