編輯:關於Android編程
使用MUI開發手機APP時使用藍牙打印功能可能較少使用,MUI官方並為集成藍牙打印功能,而且似乎對iPhone藍牙打印的類庫支持也不夠完善。忙完一階段後回顧下之前的工作,想想藍牙打印功能折騰了夠長時間了,寫這篇文章既是自己做個總結,也希望能給遇到同樣為藍牙打印功能掙扎的人帶來一點幫助。
以下打印的代碼中使用了一些NativeJS的方法,如果對NativeJS沒有了解的朋友,建議自行Google一下。我們直接上代碼。
頁面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 = {}))
好了,基本上代碼都貼出來了,作為一個代碼狗,很多時候我都相信:Talk is cheap, show me the code。
先看看效果:其實畫畫板的原理很簡單,就是首先記錄下按下屏幕的點,然後每移動一下就讓這兩次移動的點連線,周而復始,圖像就由很多條直線構成了。核心代碼 :public cla
在Android中,任何一個控件都是可以滾動的,因為在View類當中有scrollTo()和scrollBy()這兩個方法,如下圖所示: 這兩個方法的主要作用是將View
最近我獨立開發的項目《全醫會》已經在內測當中了,很快將會上架到各大應用市場。之前開發的幾個項目都因為一些原因沒有上架還是比較遺憾的。所以,最近我心情格外的好。 今天在做
一、布局文件main.xml二、MainActivity.javaimport java.io.File;import java.io.FileOutputStream;