我已经制作了一个通过蓝牙连接到斑马打印机的Android应用程序,它运行正常。这可以归功于Zebra提供的库。
我的问题是,如果我想使用其他类型的打印机,那将迫使我再次编程并使用另一个库。
有没有办法打印到任何蓝牙打印机?无需针对每种特定类型的品牌进行编程?
答案 0 :(得分:1)
这里是github link,提供了一个简单的android蓝牙打印库,该库仅可在任何蓝牙打印机上使用。它可以轻松地集成到您的项目中。我做了一些调整,以使我们每次打印时都不必选择蓝牙设备。
我用它来为此video中显示的应用程序提供打印功能。基本上有三个重要的类。
public class PrinterCommands {
public static final byte HT = 0x9;
public static final byte LF = 0x0A;
public static final byte CR = 0x0D;
public static final byte ESC = 0x1B;
public static final byte DLE = 0x10;
public static final byte GS = 0x1D;
public static final byte FS = 0x1C;
public static final byte STX = 0x02;
public static final byte US = 0x1F;
public static final byte CAN = 0x18;
public static final byte CLR = 0x0C;
public static final byte EOT = 0x04;
public static final byte[] INIT = {27, 64};
public static byte[] FEED_LINE = {10};
public static byte[] SELECT_FONT_A = {20, 33, 0};
public static byte[] SET_BAR_CODE_HEIGHT = {29, 104, 100};
public static byte[] PRINT_BAR_CODE_1 = {29, 107, 2};
public static byte[] SEND_NULL_BYTE = {0x00};
public static byte[] SELECT_PRINT_SHEET = {0x1B, 0x63, 0x30, 0x02};
public static byte[] FEED_PAPER_AND_CUT = {0x1D, 0x56, 66, 0x00};
public static byte[] SELECT_CYRILLIC_CHARACTER_CODE_TABLE = {0x1B, 0x74, 0x11};
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, -128, 0};
public static byte[] SET_LINE_SPACING_24 = {0x1B, 0x33, 24};
public static byte[] SET_LINE_SPACING_30 = {0x1B, 0x33, 30};
public static byte[] TRANSMIT_DLE_PRINTER_STATUS = {0x10, 0x04, 0x01};
public static byte[] TRANSMIT_DLE_OFFLINE_PRINTER_STATUS = {0x10, 0x04, 0x02};
public static byte[] TRANSMIT_DLE_ERROR_STATUS = {0x10, 0x04, 0x03};
public static byte[] TRANSMIT_DLE_ROLL_PAPER_SENSOR_STATUS = {0x10, 0x04, 0x04};
public static final byte[] ESC_FONT_COLOR_DEFAULT = new byte[] { 0x1B, 'r',0x00 };
public static final byte[] FS_FONT_ALIGN = new byte[] { 0x1C, 0x21, 1, 0x1B,
0x21, 1 };
public static final byte[] ESC_ALIGN_LEFT = new byte[] { 0x1b, 'a', 0x00 };
public static final byte[] ESC_ALIGN_RIGHT = new byte[] { 0x1b, 'a', 0x02 };
public static final byte[] ESC_ALIGN_CENTER = new byte[] { 0x1b, 'a', 0x01 };
public static final byte[] ESC_CANCEL_BOLD = new byte[] { 0x1B, 0x45, 0 };
/*********************************************/
public static final byte[] ESC_HORIZONTAL_CENTERS = new byte[] { 0x1B, 0x44, 20, 28, 00};
public static final byte[] ESC_CANCLE_HORIZONTAL_CENTERS = new byte[] { 0x1B, 0x44, 00 };
/*********************************************/
public static final byte[] ESC_ENTER = new byte[] { 0x1B, 0x4A, 0x40 };
public static final byte[] PRINTE_TEST = new byte[] { 0x1D, 0x28, 0x41 };
}
下面显示的另一个类帮助您从配对的Bluetooth设备列表中选择打印机。
public class DeviceListActivity extends Activity {
protected static final String TAG = "TAG";
private BluetoothAdapter mBluetoothAdapter;
private ArrayAdapter<String> mPairedDevicesArrayAdapter;
private static PrinterSelectedListener printerSelectedListener;
@Override
protected void onCreate(Bundle mSavedInstanceState) {
super.onCreate(mSavedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.devices_list);
newConnection();
}
public static void setPrinterSelectedListener(PrinterSelectedListener printerSelectedListener_) {
printerSelectedListener = printerSelectedListener_;
}
private void newConnection() {
try {
setResult(Activity.RESULT_CANCELED);
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
ListView mPairedListView = findViewById(R.id.paired_devices);
mPairedListView.setAdapter(mPairedDevicesArrayAdapter);
mPairedListView.setOnItemClickListener(mDeviceClickListener);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> mPairedDevices = mBluetoothAdapter.getBondedDevices();
if (mPairedDevices.size() > 0) {
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
for (BluetoothDevice mDevice : mPairedDevices) {
mPairedDevicesArrayAdapter.add(mDevice.getName() + "\n" + mDevice.getAddress());
}
} else {
String mNoDevices = "None Paired";//getResources().getText(R.string.none_paired).toString();
mPairedDevicesArrayAdapter.add(mNoDevices);
}
}catch(Exception ex){
Log.e("exception", ex.toString());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mBluetoothAdapter != null) {
mBluetoothAdapter.cancelDiscovery();
}
}
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
public void onItemClick(AdapterView<?> mAdapterView, View mView, int mPosition, long mLong) {
try {
mBluetoothAdapter.cancelDiscovery();
String mDeviceInfo = ((TextView) mView).getText().toString();
String mDeviceAddress = mDeviceInfo.substring(mDeviceInfo.length() - 17);
String mDeviceName = mDeviceInfo.substring(0, mDeviceInfo.length()- 17);
Bundle mBundle = new Bundle();
mBundle.putString("DeviceAddress", mDeviceAddress);
//save this printer address in sharedpreference
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor editor = pref.edit();
editor.putString("bluetooth_printer", mDeviceAddress); //
editor.putString("bluetooth_name", mDeviceName);
editor.apply();
//respond to listerner
if(printerSelectedListener != null)
{
printerSelectedListener.onPrinterSelected(mDeviceName);
}
Intent mBackIntent = new Intent();
mBackIntent.putExtras(mBundle);
setResult(Activity.RESULT_OK, mBackIntent);
finish();
}
catch (Exception ex)
{
}
}
};
}
第三类是您应该扩展的类,以便super方法可以为您打印。 此类如下所示。
public class MainActivity extends AppCompatActivity implements Runnable {
protected static final String TAG = "TAG";
protected static final int REQUEST_CONNECT_DEVICE = 1;
protected static final int REQUEST_ENABLE_BT = 2;
protected static final int BT_ON = 3;
protected BluetoothAdapter mBluetoothAdapter;
protected UUID applicationUUID = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
protected ProgressDialog mBluetoothConnectProgressDialog;
protected BluetoothSocket mBluetoothSocket;
protected BluetoothDevice mBluetoothDevice;
protected OutputStream outputStream;
public String BILL = "";
protected String printerName = "";
protected boolean isChangingName = false;
protected boolean isTestingPrinter = false;
@Override
protected void onCreate(Bundle mSavedInstanceState) {
super.onCreate(mSavedInstanceState);
setContentView(R.layout.activity_printer);
}// onCreate
public void doPrint(final String job) {
String name = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("bluetooth_printer", "");
printerName = name;
this.BILL = job;
this.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (name.equalsIgnoreCase(""))
{
if (this.mBluetoothAdapter == null)
{
Toast.makeText(getApplicationContext(), "Your Bluetooth adapter has issues", Toast.LENGTH_LONG).show();
return;
} else if (!this.mBluetoothAdapter.isEnabled())
{
//put on the bluetooth
Intent enableBtIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent,
REQUEST_ENABLE_BT);
return;
} else
{
introduceNewDevice();
return;
}
}else
{
Intent enableBtIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent,
BT_ON);
return;
}
}
protected void printingProcess(final String BILL, String name) {
this.mBluetoothDevice = this.mBluetoothAdapter.getRemoteDevice(name);
try {
this.mBluetoothSocket = this.mBluetoothDevice.createRfcommSocketToServiceRecord(this.applicationUUID);
this.mBluetoothSocket.connect();
} catch (IOException eConnectException) {
Toast.makeText(MainActivity.this, "The printer is not available. Check if it is on", Toast.LENGTH_SHORT).show();
}
new Thread() {
public void run() {
try { //outputStream
outputStream = mBluetoothSocket.getOutputStream();
if(isTestingPrinter){
//invoice details
printConfig(BILL, 2, 1,1);//align 1=center
printNewLine();
}
closeSocket(mBluetoothSocket); //close the connection
} catch (Exception e) {
Log.e("MainActivity", "Exe ", e);
}
}
}.start();
}
protected void printConfig(String bill, int size, int style, int align)
{
//size 1 = large, size 2 = medium, size 3 = small
//style 1 = Regular, style 2 = Bold
//align 0 = left, align 1 = center, align 2 = right
try{
byte[] format = new byte[]{27,33, 0};
byte[] change = new byte[]{27,33, 0};
outputStream.write(format);
//different sizes, same style Regular
if (size==1 && style==1) //large
{
change[2] = (byte) (0x10); //large
outputStream.write(change);
}else if(size==2 && style==1) //medium
{
//nothing to change, uses the default settings
}else if(size==3 && style==1) //small
{
change[2] = (byte) (0x3); //small
outputStream.write(change);
}
//different sizes, same style Bold
if (size==1 && style==2) //large
{
change[2] = (byte) (0x10 | 0x8); //large
outputStream.write(change);
}else if(size==2 && style==2) //medium
{
change[2] = (byte) (0x8);
outputStream.write(change);
}else if(size==3 && style==2) //small
{
change[2] = (byte) (0x3 | 0x8); //small
outputStream.write(change);
}
switch (align) {
case 0:
//left align
outputStream.write(PrinterCommands.ESC_ALIGN_LEFT);
break;
case 1:
//center align
outputStream.write(PrinterCommands.ESC_ALIGN_CENTER);
break;
case 2:
//right align
outputStream.write(PrinterCommands.ESC_ALIGN_RIGHT);
break;
}
outputStream.write(bill.getBytes());
outputStream.write(PrinterCommands.LF);
}catch(Exception ex){
Log.e("error", ex.toString());
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
try {
if (mBluetoothSocket != null)
mBluetoothSocket.close();
} catch (Exception e) {
Log.e("Tag", "Exe ", e);
}
}
@Override
public void onBackPressed() {
try {
if (mBluetoothSocket != null)
mBluetoothSocket.close();
} catch (Exception e) {
Log.e("Tag", "Exe ", e);
}
setResult(RESULT_CANCELED);
finish();
}
public void onActivityResult(int mRequestCode, int mResultCode,
Intent mDataIntent) {
super.onActivityResult(mRequestCode, mResultCode, mDataIntent);
try {
switch (mRequestCode) {
case REQUEST_CONNECT_DEVICE:
if (mResultCode == Activity.RESULT_OK) {
Bundle mExtra = mDataIntent.getExtras();
String mDeviceAddress = mExtra.getString("DeviceAddress");
Log.e(TAG, "Coming incoming address " + mDeviceAddress);
mBluetoothDevice = mBluetoothAdapter
.getRemoteDevice(mDeviceAddress);
mBluetoothConnectProgressDialog = ProgressDialog.show(this,
"Connecting...", mBluetoothDevice.getName() + " : "
+ mBluetoothDevice.getAddress(), true, false);
mBluetoothAdapter.cancelDiscovery();
mHandler.sendEmptyMessage(0);
//don't print if we are just changing name
if (!isChangingName)
printingProcess(BILL, mDeviceAddress);
else {
Toast.makeText(MainActivity.this, "Printer selected successfully!", Toast.LENGTH_SHORT).show();
}
}
break;
case REQUEST_ENABLE_BT:
if (mResultCode == Activity.RESULT_OK) {
introduceNewDevice();
} else {
Toast.makeText(MainActivity.this, "Request denied", Toast.LENGTH_SHORT).show();
}
break; //BT_ON
case BT_ON:
if (mResultCode == Activity.RESULT_OK) {
if (isChangingName) {
introduceNewDevice();
} else {
printingProcess(BILL, printerName);
}
} else {
Toast.makeText(MainActivity.this, "Request denied", Toast.LENGTH_SHORT).show();
}
break;
}
}catch(Exception ex){
Log.e(TAG, ex.toString());
}
}
protected void introduceNewDevice() {
ListPairedDevices();
Intent connectIntent = new Intent(MainActivity.this,
DeviceListActivity.class);
startActivityForResult(connectIntent, REQUEST_CONNECT_DEVICE);
}
protected void ListPairedDevices() {
try {
Set<BluetoothDevice> mPairedDevices = mBluetoothAdapter
.getBondedDevices();
if (mPairedDevices.size() > 0) {
for (BluetoothDevice mDevice : mPairedDevices) {
Log.e(TAG, "PairedDevices: " + mDevice.getName() + " "
+ mDevice.getAddress());
}
}
}catch(Exception ex){
Log.e(TAG, ex.toString());
}
}
public void run() {
try {
mBluetoothSocket = mBluetoothDevice
.createRfcommSocketToServiceRecord(applicationUUID);
mBluetoothAdapter.cancelDiscovery();
mBluetoothSocket.connect();
mHandler.sendEmptyMessage(0);
Log.e("main run","inside the main run");
} catch (IOException eConnectException) {
Log.d(TAG, "CouldNotConnectToSocket", eConnectException);
closeSocket(mBluetoothSocket);
return;
}
}
protected void closeSocket(BluetoothSocket nOpenSocket) {
try {
nOpenSocket.close();
Log.d(TAG, "SocketClosed");
} catch (IOException ex) {
Log.d(TAG, "CouldNotCloseSocket");
}
}
protected Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mBluetoothConnectProgressDialog.dismiss();
// Toast.makeText(MainActivity.this, "Device Connected", Toast.LENGTH_SHORT).show();
}
};
public static byte intToByteArray(int value) {
byte[] b = ByteBuffer.allocate(4).putInt(value).array();
for (int k = 0; k < b.length; k++) {
System.out.println("Selva [" + k + "] = " + "0x"
+ UnicodeFormatter.byteToHex(b[k]));
}
return b[3];
}
public byte[] sel(int val) {
ByteBuffer buffer = ByteBuffer.allocate(2);
buffer.putInt(val);
buffer.flip();
return buffer.array();
}
//print photo
public void printPhoto(int img) {
try {
Bitmap bmp = BitmapFactory.decodeResource(getResources(),
img);
if(bmp!=null){
byte[] command = Utils.decodeBitmap(bmp);
outputStream.write(PrinterCommands.ESC_ALIGN_CENTER);
printText(command);
}else{
Log.e("Print Photo error", "the file isn't exists");
}
} catch (Exception e) {
e.printStackTrace();
Log.e("PrintTools", "the file isn't exists");
}
}
//print unicode
public void printUnicode(){
try {
outputStream.write(PrinterCommands.ESC_ALIGN_CENTER);
printText(Utils.UNICODE_TEXT);
} catch (UnsupportedEncodingException e) {
Log.e("printUnicodeProblem", e.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
//print new line
protected void printNewLine() {
try {
outputStream.write(PrinterCommands.FEED_LINE);
} catch (IOException e) {
e.printStackTrace();
}
}
public void resetPrint() {
try{
outputStream.write(PrinterCommands.ESC_FONT_COLOR_DEFAULT);
outputStream.write(PrinterCommands.FS_FONT_ALIGN);
outputStream.write(PrinterCommands.ESC_ALIGN_LEFT);
outputStream.write(PrinterCommands.ESC_CANCEL_BOLD);
outputStream.write(PrinterCommands.LF);
} catch (IOException e) {
e.printStackTrace();
}
}
//print text
protected void printText(String msg) {
try {
// Print normal text
outputStream.write(msg.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
//print byte[]
protected void printText(byte[] msg) {
try {
// Print normal text
outputStream.write(msg);
// printNewLine();
} catch (IOException e) {
Log.e("printTextError",e.toString());
}
}
protected String leftRightAlign(String str1, String str2) {
String ans = str1 +str2;
if(ans.length() <31){
int n = (31 - str1.length() + str2.length());
ans = str1 + new String(new char[n]).replace("\0", " ") + str2;
}
return ans;
}
以上类中的布局activity_printer.xml应该更改为您的布局。但是,您需要包括这个devices_list.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/title_paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#666"
android:paddingLeft="5dip"
android:text="Bluetooth Devices"
android:textColor="#fff"
android:visibility="gone" />
<ListView
android:id="@+id/paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
android:layout_weight="1"
android:stackFromBottom="true" />
</LinearLayout>
要打印,只需将字符串传递给方法
doPrint("your strings that needs to be printed");
我可以添加一些内容,以便在选择打印机后可以回电。界面如下图所示。您必须在希望用户选择打印机并对其进行测试的活动中实现该接口
public interface PrinterSelectedListener {
void onPrinterSelected(String name); }
要选择打印机并在单独的活动中对其进行测试,您的活动必须扩展MainActivity并实现PrinterSelectedListener。要选择新打印机或更换打印机,请致电
isChangingName = true;
doPrint("");
要测试打印机,请致电
isChangingName = false;
isTestingPrinter = true;
String test = "This is a test printer page" + "\n";
doPrint(test);
答案 1 :(得分:0)
讨厌回答我自己的问题,但我已经对这个话题进行了一些阅读
首先,没有办法从Android打印到几个品牌的蓝牙打印机,因为它们没有实现一些使其成为可能的标准。
有一个打印框架调用APF,它有很多打印机注册,如果找到项目所需的打印机型号,它可能很有用。但是是付费的,没有好的文件。不会使用,因为没有覆盖新的打印机。
您可以做的最佳选择是选择最常见类型的打印机,例如Zebra,Woosim,Bixolon等。并为此进行编程,以便您在购买打印机时可以选择并且不依赖于特定品牌。< / p>