Android打印到任何蓝牙打印机

时间:2017-04-18 02:57:28

标签: android printing bluetooth

我已经制作了一个通过蓝牙连接到斑马打印机的Android应用程序,它运行正常。这可以归功于Zebra提供的库。

我的问题是,如果我想使用其他类型的打印机,那将迫使我再次编程并使用另一个库。

有没有办法打印到任何蓝牙打印机?无需针对每种特定类型的品牌进行编程?

2 个答案:

答案 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>