Android(安卓)实现蓝牙打印的功能
第一步:首先需要一个蓝牙打印工具类
import android.bluetooth.BluetoothSocket;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import com.github.promeg.pinyinhelper.Pinyin;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Calendar;
/**
-
蓝牙打印工具类
*/
public class PrintUtil {private OutputStreamWriter mWriter = null;
private OutputStream mOutputStream = null;public final static int WIDTH_PIXEL = 384;
public final static int IMAGE_SIZE = 320;
private static String myTime;/**
- 初始化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();
//获取当前时间
getTime();
}
/**
- 打印换行
- @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©) {
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(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(192); 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;
}
public static void printTest(BluetoothSocket bluetoothSocket, Bitmap bitmap) {
try { PrintUtil pUtil = new PrintUtil(bluetoothSocket.getOutputStream(), "GBK"); // 店铺名 居中 放大 pUtil.printAlignment(1); pUtil.printLargeText("广州德胜"); pUtil.printLine(); pUtil.printAlignment(0); pUtil.printLine(); pUtil.printTwoColumn("时间:", myTime); pUtil.printLine(); pUtil.printTwoColumn("订单号:", System.currentTimeMillis() + ""); pUtil.printLine(); pUtil.printTwoColumn("付款人:", "VitaminChen"); pUtil.printLine(); // 分隔线 pUtil.printDashLine(); pUtil.printLine(); //打印商品列表 pUtil.printText("商品"); pUtil.printTabSpace(2); pUtil.printText("数量"); pUtil.printTabSpace(1); pUtil.printText(" 单价"); pUtil.printLine(); pUtil.printThreeColumn("iphone6", "1", "4999.00"); pUtil.printThreeColumn("测试测试", "1", "4999.00"); pUtil.printDashLine(); pUtil.printLine(); pUtil.printTwoColumn("订单金额:", "9998.00"); pUtil.printLine(); pUtil.printTwoColumn("实收金额:", "10000.00"); pUtil.printLine(); pUtil.printTwoColumn("找零:", "2.00"); pUtil.printLine(); pUtil.printDashLine(); //打印图片 // pUtil.printBitmap(bitmap); pUtil.printLine(4); } catch (IOException e) { }
}
private void getTime() {
final Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH) + 1;
int day = c.get(Calendar.DATE);
int hour = c.get(Calendar.HOUR);
int minute = c.get(Calendar.MINUTE);
if (month > 9) {
myTime = year + “-” + month + “-” + day;
} else {
if (day > 9) {
myTime = year + “-” + “0” + month + “-” + day;
} else {
myTime = year + “-” + “0” + month + “-” + “0” + day;
}}
}
}
第二步 :还有一个检测蓝牙是否连接的工具类
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class BluetoothUtil {
/** * 蓝牙是否打开 */public static boolean isBluetoothOn() {BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if (mBluetoothAdapter != null)// 蓝牙已打开if (mBluetoothAdapter.isEnabled())return true;return false;}/** * 获取所有已配对的设备 */public static List getPairedDevices() {List deviceList = new ArrayList<>();Set pairedDevices = BluetoothAdapter.getDefaultAdapter().getBondedDevices();if (pairedDevices.size() > 0) {for (BluetoothDevice device : pairedDevices) {deviceList.add(device);}}return deviceList;}/** * 获取所有已配对的打印类设备 */public static List getPairedPrinterDevices() {return getSpecificDevice(BluetoothClass.Device.Major.IMAGING);}/** * 从已配对设配中,删选出某一特定类型的设备展示 * @param deviceClass * @return */public static List getSpecificDevice(int deviceClass){List devices = BluetoothUtil.getPairedDevices();List printerDevices = new ArrayList<>();for (BluetoothDevice device : devices) {BluetoothClass klass = device.getBluetoothClass();// 关于蓝牙设备分类参考 http://stackoverflow.com/q/23273355/4242112if (klass.getMajorDeviceClass() == deviceClass)printerDevices.add(device);}return printerDevices;}/** * 弹出系统对话框,请求打开蓝牙 */public static void openBluetooth(Activity activity) {Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);activity.startActivityForResult(enableBtIntent, 666);}public static BluetoothSocket connectDevice(BluetoothDevice device) {BluetoothSocket socket = null;try {socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));socket.connect();} catch (IOException e) {try {socket.close();} catch (IOException closeException) {return null;}return null;}return socket;}
}
第三步:创建一个基类
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import com.will.bluetoothprinterdemo.utils.BluetoothUtil;
import java.io.IOException;
public abstract class BasePrintActivity extends AppCompatActivity {
String tag = getClass().getSimpleName();private BluetoothSocket mSocket;private BluetoothStateReceiver mBluetoothStateReceiver;private AsyncTask mConnectTask;private ProgressDialog mProgressDialog;/** * 蓝牙连接成功后回调,该方法在子线程执行,可执行耗时操作 */public abstract void onConnected(BluetoothSocket socket, int taskType);/** * 蓝牙状态发生变化时回调 */public void onBluetoothStateChanged(Intent intent) {}@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initReceiver();}@Overrideprotected void onStop() { cancelConnectTask(); closeSocket(); super.onStop();}protected void closeSocket() { if (mSocket != null) { try { mSocket.close(); } catch (IOException e) { mSocket = null; e.printStackTrace(); } }}protected void cancelConnectTask() { if (mConnectTask != null) { mConnectTask.cancel(true); mConnectTask = null; }}@Overrideprotected void onDestroy() { unregisterReceiver(mBluetoothStateReceiver); super.onDestroy();}private void initReceiver() { mBluetoothStateReceiver = new BluetoothStateReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(mBluetoothStateReceiver, filter);}/** * 检查蓝牙状态,如果已打开,则查找已绑定设备 * * @return */public boolean checkBluetoothState() { if (BluetoothUtil.isBluetoothOn()) { return true; } else { BluetoothUtil.openBluetooth(this); return false; }}public void connectDevice(BluetoothDevice device, int taskType) { if (checkBluetoothState() && device != null) { mConnectTask = new ConnectBluetoothTask(taskType).execute(device); }}class ConnectBluetoothTask extends AsyncTask { int mTaskType; public ConnectBluetoothTask(int taskType) { this.mTaskType = taskType; } @Override protected void onPreExecute() { showProgressDialog("请稍候..."); super.onPreExecute(); } @Override protected BluetoothSocket doInBackground(BluetoothDevice... params) { if(mSocket != null){ try { mSocket.close(); } catch (IOException e) { e.printStackTrace(); } } mSocket = BluetoothUtil.connectDevice(params[0]);; onConnected(mSocket, mTaskType); return mSocket; } @Override protected void onPostExecute(BluetoothSocket socket) { mProgressDialog.dismiss(); if (socket == null || !socket.isConnected()) { toast("连接打印机失败"); } else { toast("成功!"); } super.onPostExecute(socket); }}protected void showProgressDialog(String message) { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(this); mProgressDialog.setCanceledOnTouchOutside(false); mProgressDialog.setCancelable(false); } mProgressDialog.setMessage(message); if (!mProgressDialog.isShowing()) { mProgressDialog.show(); }}protected void toast(String message) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show();}/** * 监听蓝牙状态变化的系统广播 */class BluetoothStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); switch (state) { case BluetoothAdapter.STATE_TURNING_ON: toast("蓝牙已开启"); break; case BluetoothAdapter.STATE_TURNING_OFF: toast("蓝牙已关闭"); break; } onBluetoothStateChanged(intent); }}
}
第四步:主界面代码
package com.will.bluetoothprinterdemo.ui;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.will.bluetoothprinterdemo.R;
import com.will.bluetoothprinterdemo.utils.BluetoothUtil;
import com.will.bluetoothprinterdemo.utils.PrintUtil;
import java.util.List;
public class PrinterSettingActivity extends BasePrintActivity implements View.OnClickListener{
ListView mLvPairedDevices;Button mBtnSetting;Button mBtnTest;Button mBtnPrint;DeviceListAdapter mAdapter;int mSelectedPosition = -1;final static int TASK_TYPE_CONNECT = 1;final static int TASK_TYPE_PRINT = 2;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_printer_setting); initViews();}@Overrideprotected void onResume() { super.onResume(); fillAdapter();}private void initViews() { mLvPairedDevices = (ListView) findViewById(R.id.lv_paired_devices); mBtnSetting = (Button) findViewById(R.id.btn_goto_setting); mBtnTest = (Button) findViewById(R.id.btn_test_conntect); mBtnPrint = (Button) findViewById(R.id.btn_print); mLvPairedDevices.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { mSelectedPosition = position; mAdapter.notifyDataSetChanged(); } }); mBtnSetting.setOnClickListener(this); mBtnTest.setOnClickListener(this); mBtnPrint.setOnClickListener(this); mAdapter = new DeviceListAdapter(this); mLvPairedDevices.setAdapter(mAdapter);}/** * 从所有已配对设备中找出打印设备并显示 */private void fillAdapter() { //推荐使用 BluetoothUtil.getPairedPrinterDevices() List printerDevices = BluetoothUtil.getPairedDevices(); mAdapter.clear(); mAdapter.addAll(printerDevices); refreshButtonText(printerDevices);}private void refreshButtonText(List printerDevices) { if (printerDevices.size() > 0) { mBtnSetting.setText("配对更多设备"); } else { mBtnSetting.setText("还未配对打印机,去设置"); }}@Overridepublic void onClick(View v) { switch (v.getId()){ case R.id.btn_goto_setting: startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS)); break; case R.id.btn_test_conntect: connectDevice(TASK_TYPE_CONNECT); break; case R.id.btn_print: connectDevice(TASK_TYPE_PRINT); break; }}private void connectDevice(int taskType){ if(mSelectedPosition >= 0){ BluetoothDevice device = mAdapter.getItem(mSelectedPosition); if(device!= null) super.connectDevice(device, taskType); }else{ Toast.makeText(this, "还未选择打印设备", Toast.LENGTH_SHORT).show(); }}@Overridepublic void onConnected(BluetoothSocket socket, int taskType) { switch (taskType){ case TASK_TYPE_PRINT: Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test_image); PrintUtil.printTest(socket, bitmap); break; }}class DeviceListAdapter extends ArrayAdapter { public DeviceListAdapter(Context context) { super(context, 0); } @Override public View getView(int position, View convertView, ViewGroup parent) { BluetoothDevice device = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_bluetooth_device, parent, false); } TextView tvDeviceName = (TextView) convertView.findViewById(R.id.tv_device_name); CheckBox cbDevice = (CheckBox) convertView.findViewById(R.id.cb_device); tvDeviceName.setText(device.getName()); cbDevice.setChecked(position == mSelectedPosition); return convertView; }}
}
第五步:主界面布局
activity_printer_setting.xml
布局二:
<?xml version="1.0" encoding="utf-8"?>
更多相关文章
- 《 Android物联网开发从入门到实战》国内第一本开发书籍!
- 《 Android物联网开发从入门到实战》国内第一本开发书籍!
- Android(安卓)热敏打印机打印二维码
- Android(安卓)蓝牙开发基本流程
- Android(安卓)C++层打印调用栈
- Android(安卓)蓝牙开发浅析
- android 简单实用的Log打印类封装,助你快速定位问题
- Android(安卓)SDK自带教程之BluetoothChat
- Android(安卓)在屏幕上打印LOG