app连接蓝牙打印机实现打印并排版

做项目的时候遇到需要连接打印机打印小票的情况,因为从未接触过这方面的东西,就在网上下载了一个demo,demo地址:http://blog.csdn.net/reality_jie_blog/article/details/11895843,demo中是可以实现百分百连接打印的,但是我在移植过程中发现每次都只能在手机开关机之后的第一次才能成功连接,之后就再也连不上了,完全退出app也不行,纠结了2天,查了各种资料都不行,直到做了一个梦。。。

原demo中是先连接再输入数据实现打印的,我这个是先输入数据再连接,因为打印的数据是固定格式的,不用现输入,排版好,选择打印机即可开始打印,先是排版

/** 装载要打印的数据 */
	private void initPrintData() {
		AlertUtil.showLoadingMessage(this);

		OrderDetails details = adapter.getData();
		StringBuffer sb = new StringBuffer();
		// 排版标题
		sb.append(BluetoothPrintFormatUtil.printTitle("xx网\n"))
				.append(BluetoothPrintFormatUtil.printTitle("www.baidu.com\n")).append("\n\n" + line + "\n");

		// 排版订单信息
		LinkedHashMap MsgMap = new LinkedHashMap();

		sb.append("订单编号:" + details.getOrderId() + bar);
		ArrayList arr = details.getStateloglist();
		if (arr.size() != 0) {
			sb.append("下单时间:" + startTime + bar);
		}
		sb.append("打印时间:" + DateUtils.getYearAndDay2(0) + bar);
		sb.append(line + bar);
		for (int i = 0; i < details.getStoreList().size(); i++) {
			for (int j = 0; j < details.getStoreList().get(i).getPlist().size(); j++) {
				String type = details.getStoreList().get(i).getPlist().get(j).getType();
				if(type==null||"".equals(type)){
					type="kg";
				}
				double weight = details.getStoreList().get(i).getPlist().get(j).getWeight();
				sb.append(details.getStoreList().get(i).getPlist().get(j).getProductName() + bar);
				MsgMap.put(details.getStoreList().get(i).getPlist().get(j).getCount()+"份"+"  重约" + CommonUtil.getDouble(weight) + type,
						"¥" + details.getStoreList().get(i).getPlist().get(j).getPrice() + bar);
				sb.append(BluetoothPrintFormatUtil.printMiddleMsg(MsgMap));
				MsgMap.clear();
			}
		}
		sb.append(line + bar);
		MsgMap.put("运费", "+¥" + details.getShipPrice() + bar);
		if(details.getOneYuan()>0){
			MsgMap.put("一元购", "-¥" + details.getOneYuan() + bar);
		}
		if(details.getFirstDiscount()>0){
			MsgMap.put("首单减免", "-¥" + details.getFirstDiscount() + bar);
		}
		if(details.getOverDiscount()>0){
			MsgMap.put("满减优惠", "-¥" + details.getOverDiscount() + bar);
		}
		if(details.getBonusPay()>0){
			MsgMap.put("红包抵扣", "-¥" + details.getBonusPay() + bar);
		}
		if(details.getHalfPrice()>0){
			MsgMap.put("第二件半价", "-¥" + details.getHalfPrice() + bar);
		}
		MsgMap.put("订单总价", "¥" + details.getSOAmout() + bar);
		sb.append(BluetoothPrintFormatUtil.printMiddleMsg(MsgMap));
		MsgMap.clear();
		sb.append(line + bar);
		StringBuffer sb2 = new StringBuffer();
		MsgMap.put("实付金额", "¥" + details.getCashPay() + bar);
		sb2.append(BluetoothPrintFormatUtil.printMiddleMsg(MsgMap));
		MsgMap.clear();
		sb2.append(line + bar);
		sb2.append("客户姓名:" + details.getReceiveName() + bar);
		sb2.append("电话号码:" + details.getCellPhone() + bar);
		sb2.append("配送时间:" + details.getDeliveryTime() + bar);
		sb2.append("收货地址:" + details.getReceiveAddress() + bar);
		sb2.append("订单备注:" + details.getMemo() + bar + bar + bar + bar + bar);
		sb2.append(BluetoothPrintFormatUtil.printMiddleMsg(MsgMap));
		MsgMap.clear();

		AlertUtil.dismiss(this);
		/**进入蓝牙列表页面*/
		Intent intent = new Intent(OrderDetailsActivity.this, BluetoothSelectActivity.class);
		intent.putExtra("data", sb.toString());
		intent.putExtra("data2", sb2.toString());
		intent.putExtra("orderid", details.getOrderId());
		startActivity(intent);
	}


然后是排版的类,我用的58mm的热敏蓝牙打印机


只用了2个方法,

/**
	 * 打印纸一行最大的字节
	 */
	private static final int LINE_BYTE_SIZE = 32;
	
	private static StringBuffer sb = new StringBuffer();

	/**
	 * 排版居中标题
	 * @param title
	 * @return
	 */
	public static String printTitle(String title){
		sb.delete(0, sb.length());
		for(int i=0;i<(LINE_BYTE_SIZE - getBytesLength(title))/2;i++){
			sb.append(" ");
		}
		sb.append(title);
		return sb.toString();
	}
	
	/**
	 * 排版左右靠边的内容
	 * 
	 * 例:姓名             李白
	 * 	       病区       5A病区
	 * 	      住院号    11111
	 * 
	 * @param msg
	 * @return
	 */
	public static String printMiddleMsg(LinkedHashMap middleMsgMap){
		sb.delete(0, sb.length());
		String separated = " ";
		//将一张纸的宽度除去边缘的距离之后一分为二
		int leftLength = (LINE_BYTE_SIZE-getBytesLength(separated))/2;
		for(Entry middleEntry : middleMsgMap.entrySet()){
			//第一部分先写数据,剩余的填充空格,空格的数量为左半部分长度减去数据长度
			sb.append(middleEntry.getKey());
			for(int i=0;i<(leftLength-getBytesLength(middleEntry.getKey()));i++){
				sb.append(" ");
			}
			//第二部分先填充空格,再写数据,空格的数量为左半部分长度减去数据长度
			int size=leftLength-getBytesLength(middleEntry.getValue());
			for (int i = 0; i < size; i++) {
				sb.append(" ");
			}
			sb.append(middleEntry.getValue());
		}
		return sb.toString();
	}

这个排版类是在csdn上下载的,准确的说,点都不好用,还是我自己去改的,不过思路很好,类里面还提供了别的方法,但是没有测试过,所以不给出,附链接:http://blog.csdn.net/qq331710168/article/details/9170135

打开蓝牙并让自己的手机能在120秒内被搜索到

//打开蓝牙
		Intent enableBtIntent = new Intent(    
                BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);    
        startActivityForResult(enableBtIntent, 1); 


操作蓝牙的类


public class BluetoothService {
	private Context context = null;
	private BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	private ArrayList unbondDevices = null; // 用于存放未配对蓝牙设备
	private ListView unbondDevicesListView = null;
	private String data = "",data2="";
	ProgressDialog progressDialog = null;

	/**
	 * 添加未绑定蓝牙设备到ListView
	 */
	private void addUnbondDevicesToListView() {
		ArrayList> data = new ArrayList>();
		int count = this.unbondDevices.size();
		System.out.println("未绑定设备数量:" + count);
		for (int i = 0; i < count; i++) {
			HashMap map = new HashMap();
			map.put("deviceName", this.unbondDevices.get(i).getName());
			data.add(map);// 把item项的数据加到data中
		}
		String[] from = { "deviceName" };
		int[] to = { R.id.undevice_name };
		SimpleAdapter simpleAdapter = new SimpleAdapter(this.context, data, R.layout.unbonddevice_item, from, to);

		// 把适配器装载到listView中
		this.unbondDevicesListView.setAdapter(simpleAdapter);

		// 为每个item绑定监听,用于设备间的配对
		this.unbondDevicesListView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView arg0, View arg1, int arg2, long arg3) {
				try {
					AlertUtil.showLoadingMessage(context);
					BluetoothDevice device = unbondDevices.get(arg2);
					PrintDataService printDataService = new PrintDataService(context, device.getAddress(),BluetoothService.this.data,BluetoothService.this.data2);
					if(printDataService.connect()){
						printDataService.send();
					}
				} catch (Exception e) {
					AlertUtil.showErrorMessage(context, "配对失败!");
				}

			}
		});
	}

	public BluetoothService(Context context, ListView unbondDevicesListView, String data,String data2) {
		this.context = context;
		this.unbondDevicesListView = unbondDevicesListView;
		this.unbondDevices = new ArrayList();
		this.data = data;
		this.data2 = data2;
		this.initIntentFilter();

	}

	private void initIntentFilter() {
		// 设置广播信息过滤
		IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
		intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
		intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
		// 注册广播接收器,接收并处理搜索结果
		context.registerReceiver(receiver, intentFilter);

	}

	/**
	 * 打开蓝牙
	 */
	public void openBluetooth(Activity activity) {
		Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
		activity.startActivityForResult(enableBtIntent, 1);

	}

	/**
	 * 关闭蓝牙
	 */
	public void closeBluetooth() {
		this.bluetoothAdapter.disable();
	}

	/**
	 * 判断蓝牙是否打开
	 * 
	 * @return boolean
	 */
	public boolean isOpen() {
		return this.bluetoothAdapter.isEnabled();

	}

	/**
	 * 搜索蓝牙设备
	 */
	public void searchDevices() {
		this.unbondDevices.clear();

		// 寻找蓝牙设备,android会将查找到的设备以广播形式发出去
		this.bluetoothAdapter.startDiscovery();
	}
	
	/**
	 * 取消搜索蓝牙设备
	 */
	public void cancelDevices() {
		this.unbondDevices.clear();
		if(progressDialog!=null){
			progressDialog.dismiss();
		}
		// 取消寻找蓝牙设备
		this.bluetoothAdapter.cancelDiscovery();
	}

	/**
	 * 添加未绑定蓝牙设备到list集合
	 * 
	 * @param device
	 */
	public void addUnbondDevices(BluetoothDevice device) {
		System.out.println("未绑定设备名称:" + device.getName());
		if (!this.unbondDevices.contains(device)) {
			this.unbondDevices.add(device);
		}
	}

	/**
	 * 添加已绑定蓝牙设备到list集合
	 * 
	 * @param device
	 */
	public void addbondDevices() {
		Set devices = this.bluetoothAdapter.getBondedDevices();
		for (BluetoothDevice device : devices) {
			if (!this.unbondDevices.contains(device)) {
				this.unbondDevices.add(device);
			}
		}
		addUnbondDevicesToListView();
	}

	/**
	 * 蓝牙广播接收器
	 */
	private BroadcastReceiver receiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (BluetoothDevice.ACTION_FOUND.equals(action)) {
				BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
				addUnbondDevices(device);
			} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
				progressDialog = ProgressDialog.show(context, "请稍等...", "搜索蓝牙设备中...", true);
				progressDialog.setOnKeyListener(onKeyListener);
			} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
				System.out.println("设备搜索完毕");
				if(progressDialog!=null){
					progressDialog.dismiss();
				}
				addUnbondDevicesToListView();
			}
		}
	};
	
	private OnKeyListener onKeyListener = new OnKeyListener() {
		//监听返回键
        @Override
        public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
            	cancelDevices();
            }
            return false;
        }
    };

}

bluetoothService.addbondDevices();方法会搜索手机曾经配对成功过的所有蓝牙设备(当然,你取消配对了就取不到了)

bluetoothService.searchDevices();方法会发起一个长达2分钟的广播搜索,很耗时,不建议使用,直接用手机自带的蓝牙配对之后再执行bluetoothService.addbondDevices();要快无数倍,不建议使用

try {
					AlertUtil.showLoadingMessage(context);
					BluetoothDevice device = unbondDevices.get(arg2);
					PrintDataService printDataService = new PrintDataService(context, device.getAddress(),BluetoothService.this.data,BluetoothService.this.data2);
					if(printDataService.connect()){
						printDataService.send();
					}
				} catch (Exception e) {
					AlertUtil.showErrorMessage(context, "配对失败!");
				}

这里并没有配对的操作,是我复制远demo之后忘记改了,这里执行的是连接之后并打印,配对放在了printDataService.connect()方法里面

这才是配对

if (device.getBondState() == BluetoothDevice.BOND_NONE) {
					Method creMethod = BluetoothDevice.class.getMethod("createBond");
					Log.e("TAG", "开始配对");
					creMethod.invoke(device);
				}

连接并打印类

public class PrintDataService {
	private Context context = null;
	private String deviceAddress = null;
	private BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	private BluetoothDevice device = null;
	private static BluetoothSocket bluetoothSocket = null;
	private static OutputStream outputStream = null;
	private String sendData = "" ,sendData2 = "";
	private Bitmap bitmap;
//	private static final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	private static UUID uuid=null;
	
	final String[] items = { "复位打印机", "标准ASCII字体", "压缩ASCII字体", "字体不放大", "宽高加倍", "取消加粗模式", "选择加粗模式", "取消倒置打印", "选择倒置打印",
			"取消黑白反显", "选择黑白反显", "取消顺时针旋转90°", "选择顺时针旋转90°" };
	final byte[][] byteCommands = { { 0x1b, 0x40 }, // 复位打印机
			{ 0x1b, 0x4d, 0x00 }, // 标准ASCII字体
			{ 0x1b, 0x4d, 0x01 }, // 压缩ASCII字体
			{ 0x1d, 0x21, 0x00 }, // 字体不放大
			{ 0x1d, 0x21, 0x11 }, // 宽高加倍
			{ 0x1b, 0x45, 0x00 }, // 取消加粗模式
			{ 0x1b, 0x45, 0x01 }, // 选择加粗模式
			{ 0x1b, 0x7b, 0x00 }, // 取消倒置打印
			{ 0x1b, 0x7b, 0x01 }, // 选择倒置打印
			{ 0x1d, 0x42, 0x00 }, // 取消黑白反显
			{ 0x1d, 0x42, 0x01 }, // 选择黑白反显
			{ 0x1b, 0x56, 0x00 }, // 取消顺时针旋转90°
			{ 0x1b, 0x56, 0x01 },// 选择顺时针旋转90°
			{ 0x1b, 0x21, 13 },// 字体放大
	};
	
	Handler handler=new Handler(){
		public void handleMessage(android.os.Message msg) {
			if(msg.what==1000){
				send();
			}
		};
	};

	public PrintDataService(Context context, String deviceAddress,String sendData,String sendData2) {
		super();
		this.context = context;
		this.deviceAddress = deviceAddress;
		this.sendData=sendData;
		this.sendData2=sendData2;
		this.device = this.bluetoothAdapter.getRemoteDevice(this.deviceAddress);
		ParcelUuid[] uuids= this.device.getUuids();
		uuid = UUID.fromString(uuids[0].toString());
	}

	/**
	 * 获取设备名称
	 * 
	 * @return String
	 */
	public String getDeviceName() {
		return this.device.getName();
	}

	/**
	 * 连接蓝牙设备
	 */
	public boolean connect() {
		if (bluetoothSocket==null||!bluetoothSocket.isConnected()) {
			try {
				if (device.getBondState() == BluetoothDevice.BOND_NONE) {
					Method creMethod = BluetoothDevice.class.getMethod("createBond");
					Log.e("TAG", "开始配对");
					creMethod.invoke(device);
				}
				int sdk = Integer.parseInt(Build.VERSION.SDK);
				if (sdk >= 10) {
					bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid);
				} else {
					bluetoothSocket = device.createRfcommSocketToServiceRecord(uuid);
				}
				bluetoothSocket.connect();
				outputStream = bluetoothSocket.getOutputStream();
				if (this.bluetoothAdapter.isDiscovering()) {
					System.out.println("关闭适配器!");
					this.bluetoothAdapter.cancelDiscovery();
				}
			} catch (Exception e) {
				AlertUtil.showErrorMessage(context, "连接失败!");
				return false;
			}
			AlertUtil.showSuccessMessage(context, this.device.getName() + "连接成功!");
			return true;
		} else {
			return true;
		}
	}

	/**
	 * 断开蓝牙设备连接
	 */
	public static void disconnect() {
		System.out.println("断开蓝牙设备连接");
		try {
			bluetoothSocket.close();
			outputStream.close();
			bluetoothSocket=null;
			outputStream=null;
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 选择指令
	 */
	public void selectCommand() {
		new AlertDialog.Builder(context).setTitle("请选择指令").setItems(items, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				try {
					outputStream.write(byteCommands[which]);
				} catch (IOException e) {
					Toast.makeText(context, "设置指令失败!", Toast.LENGTH_SHORT).show();
				}
			}
		}).create().show();
	}

	/**
	 * 发送数据
	 */
	public void send() {
		if (outputStream != null) {
			System.out.println("开始打印!!");
			try {
				for (int i = 0; i < 2; i++) {
					byte[] data = sendData.getBytes("gbk");
					outputStream.write(byteCommands[0]);
					outputStream.write(data, 0, data.length);
					outputStream.flush();
					byte[] data2 = sendData2.getBytes("gbk");
					outputStream.write(byteCommands[13]);
					outputStream.write(data2, 0, data2.length);
					outputStream.flush();
				}
				AlertUtil.showSuccessMessage(context, "数据打印完成",new OnClickListener() {
					
					@Override
					public void onClick(View v) {
						//数据打印完成后关闭蓝牙选择页面
						Intent intent=new Intent();
						intent.setAction(GlobalConstant.BROADCAST_BLUETOOTH_FINISH);
						context.sendBroadcast(intent);
					}
				});
			} catch (IOException e) {
				AlertUtil.showErrorMessage(context, "数据发送失败!");
			}finally{
				disconnect();
			}
		} else {
			AlertUtil.showErrorMessage(context, "设备未连接,请重新连接!");
		}
	}
}

事实上我在连接这一步卡了整整2天,原因是我每次连接完之后是会退出蓝牙选择页面的,但是没有退出app,原demo中写了这个方法,但是并没有看到在哪里调用了,我以为不需要,结果作死卡了自己2天,

BluetoothSocket在连接完之后,下次连接之前一定要好好清理掉,不然下次是无法连接的,原demo只有2个页面,一个选择蓝牙连接,一个输入数据打印,退出的时候可能是调用了这个方法的,但是我没注意到!!!我每次都是开机之后第一次可以连,第二次就不行了!!!找了2天才梦到是不是因为这个原因,气得要死,,,不过主要原因还是因为自己对BluetoothSocket完全不熟悉导致的,写个文纪念一下


/**
	 * 断开蓝牙设备连接
	 */
	public static void disconnect() {
		System.out.println("断开蓝牙设备连接");
		try {
			bluetoothSocket.close();
			outputStream.close();
			bluetoothSocket=null;
			outputStream=null;
		} catch (IOException e) {
			e.printStackTrace();
		}

	}





你可能感兴趣的:(app连接蓝牙打印机实现打印并排版)