Android 打印到任何蓝牙打印机

标签 android printing bluetooth

我已经为 android 开发了一个应用程序,可以通过蓝牙连接到 zebra 打印机,它工作正常。这要归功于 Zebra 提供的库。

我的问题是,如果我想使用另一种类型的打印机,那将迫使我重新编程并使用另一个库。

有什么方法可以打印到任何蓝牙打印机吗?无需针对每种特定类型的品牌进行编程?

最佳答案

这是一个 github link它提供了一个简单的安卓蓝牙打印库,适用于任何蓝牙打印机。它可以很容易地集成到您的项目中。我对其进行了一些调整,这样就不必在每次打印时都选择蓝牙设备。

我用它为这个 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 };

下面显示的另一个类可帮助您从已配对的蓝牙设备列表中选择打印机。

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");

我做了一些补充,这样您就可以在选择打印机后得到回电。界面如下图。您必须在您希望用户选择打印机并对其进行测试的 Activity 中实现该接口(interface)

public interface PrinterSelectedListener {

void onPrinterSelected(String name); }

要选择一台打印机并在单独的 Activity 中对其进行测试,您的 Activity 必须扩展 MainActivity 并实现 PrinterSelectedListener。要选择新打印机或更改打印机,请调用此

isChangingName = true;
doPrint("");

要测试打印机,调用这个

                isChangingName = false;
                isTestingPrinter = true;
                String test = "This is a test printer page" + "\n";
                doPrint(test);

关于Android 打印到任何蓝牙打印机,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43462836/

相关文章:

android - 在 Android Wear 上运行 Android 文本转语音?

azure - 是否可以从 Azure 工作线程进行打印?

css - Firefox CSS 反对我??

java - 检查 GPS 和蓝牙是否已激活时出现问题

java - ClassCastException:AdView 错误地转换为 EditText

android - Google Play游戏服务,取消发布,删除和重新开始

Android 在按下 widget ListView 项目时启动 Activity

css - 修复了仅最后一页打印的粘性页脚

android - 如何修改 Android 的蓝牙堆栈以启用 A2dp Sink

c# - 如何从运行在 Windows 10 Creators Update 上的 WPF 应用注册 BLE 通知?