首先我們下載官網的SDK Sample
點我到官網下載頁面
解壓縮之後就可以開啟官方的 Sample Code 了
Run 出來看看範到程式是如何操作的
然後先找到我們的列印機
點擊Unselected State後會出現連結方式
這裡選擇用LAN來連線
找到了我們的列印機
連結成功後會如下圖
接下來我們就可以開始第一次的列印囉!
點擊Printer下的Sample
會出現語言選單
再來會有紙單大小的選項
都選擇完畢後
我們會來到這個畫面
有各式的Sample可供列印
我們就先印一張 Text Receipt 來看看吧
成功印下一張收據之後
我們再來看看Code是怎麼寫的
首先是與設備的連線
在MainFragment中的每一row都是ListView來裝載的
所以我們Tapped Unselected Status後會進到這裡
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
super.onItemClick(parent, view, position, id);
Intent intent = new Intent(getActivity()
, CommonActivity.class);
switch (position) {
case 1: { // Tapped Destination Device row
intent.putExtra(
CommonActivity.BUNDLE_KEY_ACTIVITY_LAYOUT
, R.layout.activity_printer_search);
intent.putExtra(
CommonActivity.BUNDLE_KEY_TOOLBAR_TITLE
, "Search Port");
intent.putExtra(
CommonActivity.BUNDLE_KEY_SHOW_HOME_BUTTON
, true);
intent.putExtra(
CommonActivity.BUNDLE_KEY_SHOW_RELOAD_BUTTON
, true);
startActivityForResult(intent
, PRINTER_SET_REQUEST_CODE);
break;
}
從這段Code可以看到我們進到CommonActivity中
在這個共同的Activity中
利用CommonActivity的常數來做為Intent的識別字串帶入參數
而Intent所帶入的第一個參數
就是連結哪一個layout
第二是Title的字串
第三&四是button的顯示
最後帶入Request Code 便於之後 Callback
進入CommonActivity後
我們看到的第一件事是這行
setContentView(intent.getIntExtra(
CommonActivity.BUNDLE_KEY_ACTIVITY_LAYOUT
, R.layout.fragment_dummy));
// Display a user selected menu list.
將畫面設定為R.layout.activity_printer_search Code如下
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android
="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/printerSearchFragment"
android:name
="com.starmicronics.starprntsdk.SearchPortFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
可以從android:name看到此xml與SearchPortFragment連結
來到SearchPortFragment後第一個執行的method是onCreate
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mProgressDialog = new ProgressDialog(getActivity());
mProgressDialog.setMessage("Communicating...");
mProgressDialog.setProgressStyle(
ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
updatePrinterList();
}
這裡創建了一個ProgressDialog來告知User等待
再來執行了一個method
private void updatePrinterList() {
adapter.clear();
InterfaceSelectDialogFragment dialog
= InterfaceSelectDialogFragment
.newInstance(INTERFACE_SELECT_DIALOG);
dialog.show(getChildFragmentManager());
}
先將原有資料清掉後
創建了一個DialogFragment Object
Constructor接收一個String Parameter為了等等Callback 可以識別
在DialogFragment 的onCreatDialog中
除了畫面的設定之外還取得了父級的Callback
mCallbackTarget
= (CommonAlertDialogFragment.Callback) getParentFragment();
再來則是本身的ClickListener裡面的onClick method
做了下面這些事
String[] selectedInterfaceArray;
switch (which) {
case 0:
selectedInterfaceArray
= new String[] {PrinterSetting.IF_TYPE_ETHERNET};
break;
case 1:
selectedInterfaceArray
= new String[] {PrinterSetting.IF_TYPE_BLUETOOTH};
break;
以下省略 。。。
}
Intent intentForPassingData = new Intent();
intentForPassingData.putExtra(
CommonActivity.BUNDLE_KEY_INTERFACE
, selectedInterfaceArray);
mCallbackTarget.onDialogResult(
getArguments().getString(DIALOG_TAG)
, intentForPassingData);
dismiss();
這裡用一個 String Array儲存選擇的參數
利用Intent return Parameter
Callback.onDialogResult 第一個Parameter是識別的String
讓我們回到SearchPortFragment
這裡實作了CommonAlertDialogFragment.Callback
找到我們剛剛回傳的地方如下
public void onDialogResult(String tag, Intent data) {
switch (tag) {
case INTERFACE_SELECT_DIALOG : {
String[] selectedInterfaces
= data.getStringArrayExtra(
CommonActivity.BUNDLE_KEY_INTERFACE);
boolean isCanceled = data.hasExtra(
CommonAlertDialogFragment.LABEL_NEGATIVE);
if (selectedInterfaces != null) {
addTitle("List");
if (selectedInterfaces.length <= 1
&& selectedInterfaces[0].equals(
PrinterSetting.IF_TYPE_MANUAL)) {
mMacAddress = "";
PrinterSetting setting
= new PrinterSetting(getActivity());
PortNameDialogFragment dialog
= PortNameDialogFragment.newInstance(
PORT_NAME_INPUT_DIALOG, setting.getPortName());
dialog.show(getChildFragmentManager());
}
else {
for (String selectedInterface : selectedInterfaces){
SearchTask searchTask = new SearchTask();
searchTask.execute(selectedInterface);
}
mProgressDialog.show();
}
}
else if (isCanceled) {
getActivity().finish();
}
break;
}
這一段是在做selectedInterfaces的判斷
我們所進入的是黃色 { } 的這一段
執行SearchTask 並且讓ProgressDialog顯示
@Override
protected Void doInBackground(String... interfaceType) {
try {
mPortList = StarIOPort.searchPrinter(interfaceType[0], getActivity());
}
catch (StarIOPortException e) {
mPortList = new ArrayList<>();
}
return null;
}
而SerchTask在背後做的是
把interfaceType&Activity傳進StarIOPort搜尋設備
searchPrinter 這個method會 return ArrayList
那就讓我們來看看searchPrinter做了什麼吧
public static synchronized ArrayList<PortInfo>
searchPrinter(String var0, Context var1)
throws StarIOPortException {
String var2 = var0.toUpperCase();
new ArrayList();
ArrayList var3;
if(var2.startsWith("TCP:")) {
var3 = TCPPort.c();
} else if(var2.startsWith("BT:")) {
var3 = i.c();
if(!var2.equals("BT:")) {
ArrayList var4 = new ArrayList();
String var5 = var0.substring(3);
Iterator var6 = var3.iterator();
while(var6.hasNext()) {
PortInfo var7 = (PortInfo)var6.next();
String var8 = var7.getPortName().substring(3);
if(var8.startsWith(var5)) {
var4.add(var7);
}
}
return var4;
}
} else {
if(!var2.startsWith("USB:")) {
throw new StarIOPortException("Invalid argument.");
}
var3 = f.a(var1);
}
return var3;
}
這裡的 if 判斷interface 是 LAN、Bluetooth or USB 之中的哪一種
以我們現在的操作為例
我們使用LAN的interface會進入黃色 { } 區域執行TCPPort.c ( )
這個method做的事是利用 java.net.DatagramSocket
對區網發送Broadcast 內容為自訂的加密格式or訊息格式
當收到此格式時
所有match的 IP 都會回應相對的格式資料
然後此method會解析資料,包進PortInfo Object中
以ArrayList的方式return有效裝置
//點我看Socket專題報導
取得每個有效的PortInfo之後
讓我們回到SearchTask
@Override
protected void onPostExecute(Void doNotUse) {
for (PortInfo info : mPortList) {
addItem(info);
}
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
}
這一段純粹在做UI的顯示
取消剛剛要User等待的ProgressDialog
並歷遍所有PorInfo
把PortName、ModelName、MacAddress顯示出來供User選擇
當我們點擊後
會跑到SearchPortFragment中的onItemClick
執行下方Code
ModelConfirmDialogFragment dialog = ModelConfirmDialogFragment
.newInstance(MODEL_CONFIRM_DIALOG, model);
dialog.show(getChildFragmentManager());
出現一個確認的Dialog
當我們按下YES之後,執行下一段的Code
public void onClick(DialogInterface dialog, int which) {
Intent intentForPassingData = new Intent();
intentForPassingData.putExtra(LABEL_POSITIVE
, LABEL_POSITIVE);
intentForPassingData.putExtra(SELECTED_INDEX
, getArguments().getInt(SELECTED_INDEX));
intentForPassingData.putExtra(CommonActivity.SELECTED_MODEL
, getArguments().getInt(CommonActivity.SELECTED_MODEL));
callbackToTarget(args.getString(DIALOG_TAG)
, intentForPassingData);
dialog.dismiss();
}
這裡讓Intent帶入三樣參數
一個是LABEL_POSITIVE的識別字串
還有選擇的INDEX與選擇的連接MODEL
最後Callback回到SearchPortFragment
case MODEL_CONFIRM_DIALOG: {
boolean isPressedYes = data.hasExtra(
CommonAlertDialogFragment.LABEL_POSITIVE);
int selectedModel = data.getIntExtra(
CommonActivity.SELECTED_MODEL, ModelCapability.NONE);
if (isPressedYes) {
mPortSettings = ModelCapability
.getPortSettings(selectedModel);
mEmulation = ModelCapability
.getEmulation(selectedModel);
if (ModelCapability.getDrawerOpenStatus(selectedModel)) {
DrawerOpenActiveSelectDialogFragment dialog
= DrawerOpenActiveSelectDialogFragment.newInstance(
DRAWER_OPEN_ACTIVE_SELECT_DIALOG);
dialog.show(getChildFragmentManager());
}
else {
mDrawerOpenStatus = true;
registerPrinter();
getActivity().finish();
}
}
在Calback中找到MODEL_CONFIRM_DIALOG的LABEL
首先利用hasExtra( )來確認User按下YES
並取得SELECTED_MODEL
下面的mPortSettings & eEmulation
是不同型號產品,所需要的對應設定值
下一個 If 判斷我們選擇的產品有沒有現金抽屜
有的話再生產一個DrawerOpenActivitySelectDialogFragment
這個Dialog只有兩個選項
快速or慢速開啟抽屜,Callback回來ture or false
之後執行registerPrinter( )時會用得到
那接下來就讓我們看看registerPrinter( )做了什麼事吧
private void registerPrinter() {
PrinterSetting setting = new PrinterSetting(getActivity());
setting.write(mModelName
, mPortName
, mMacAddress
, mPortSettings
, mEmulation
, mDrawerOpenStatus);
}
這些全都是剛剛我們所選擇的ProtInfo、設定參數等
再來看看setting.write裡面有什麼
public void write(String modelName, String portName
, String macAddress, String portSettings
, Emulation emulation, Boolean drawerOpenActiveHigh) {
SharedPreferences prefs
= PreferenceManager.getDefaultSharedPreferences(mContext);
int emulationInt = -1;
for (Pair<Emulation, Integer> emulationPair : mEmulationList){
if (emulation == emulationPair.first) {
emulationInt = emulationPair.second;
break;
}
}
prefs.edit()
.putString(PREF_KEY_MODEL_NAME, modelName)
.putString(PREF_KEY_PORT_NAME, portName)
.putString(PREF_KEY_MAC_ADDRESS, macAddress)
.putString(PREF_KEY_PORT_SETTINGS, portSettings)
.putInt(PREF_KEY_EMULATION, emulationInt)
.putBoolean(PREF_KEY_DRAWER_OPEN_STATUS
, drawerOpenActiveHigh)
.apply();
}
write這個method其實就是把我們剛剛得到的參數寫進SharedPreferences裡
方便之後需要的時候可以調用
而第一個 For 迴圈是要找出產品所對應的模式
Example: StarPRNT,StarLINE,StarGraphic......
至此,我們找到了裝置
並且成功連線
接下來就是Print了
========================================================
========================================================
當我們在MainFragment 按下 Print下方的Sample時執行這段Code
case 3: { // Tapped Printer row
LanguageSelectDialogFragment dialog
= LanguageSelectDialogFragment
.newInstance(PRINTER_LANG_SELECT_DIALOG);
dialog.show(getChildFragmentManager());
break;
}
建立了一個LanguageSelectDialog
而return的是PrinterSetting.LANGUAGE的值,如下
int selectedLanguage = PrinterSetting.LANGUAGE_ENGLISH;
switch (which) {
case 0: selectedLanguage = PrinterSetting.LANGUAGE_ENGLISH;
break;
case 1: selectedLanguage = PrinterSetting.LANGUAGE_JAPANESE;
break;
case 2: selectedLanguage = PrinterSetting.LANGUAGE_FRENCH;
break;
以下省略。。。
}
然後CallBack回MainFragment的onDialogResult( )
再叫出紙張大小的選擇Dialog
case PRINTER_LANG_SELECT_DIALOG: {
。。。。。。
PaperSizeSelectDialogFragment dialog
= PaperSizeSelectDialogFragment
.newInstance(PRINTER_PAPER_SIZE_SELECT_DIALOG
, language);
dialog.show(getChildFragmentManager());
break;
PaperSizeSelectDialog設值、還有回傳的方式都與LanguageSelect相同
所以我們再次回到了 MainFragment的onDialogResult( )
case PRINTER_PAPER_SIZE_SELECT_DIALOG: {
int language = data.getIntExtra(
CommonActivity.BUNDLE_KEY_LANGUAGE
, PrinterSetting.LANGUAGE_ENGLISH);
int paperSize = data.getIntExtra(
CommonActivity.BUNDLE_KEY_PAPER_SIZE
, PrinterSetting.PAPER_SIZE_THREE_INCH);
Intent intent = new Intent(getActivity()
, CommonActivity.class);
intent.putExtra(CommonActivity.BUNDLE_KEY_ACTIVITY_LAYOUT
, R.layout.activity_printer);
intent.putExtra(CommonActivity.BUNDLE_KEY_TOOLBAR_TITLE
, "Printer");
intent.putExtra(CommonActivity.BUNDLE_KEY_LANGUAGE
, language);
intent.putExtra(CommonActivity.BUNDLE_KEY_PAPER_SIZE
, paperSize);
intent.putExtra(
CommonActivity.BUNDLE_KEY_SHOW_HOME_BUTTON, true);
startActivity(intent);
break;
}
和上次的print_search不同的是
這次我們要前往的是R.layout.activity_printer
給 Intent必要的Parameters後執行 startActivity( )
CommonActivity和之前SearchPort一樣
創建畫面時會導到PrinterFragment
也會建立一個通知User等待的ProgressDialog
值得一提的有兩個地方
第一:
PrinterSetting setting = new PrinterSetting(getActivity());
Emulation emulation = setting.getEmulation();
boolean canPrintTextReceipt
= emulation != Emulation.StarGraphic;
boolean canPrintUtf8TextReceipt
= emulation != Emulation.StarGraphic
&& emulation != Emulation.EscPos
&& emulation != Emulation.EscPosMobile;
boolean canPrintRasterReceipt
= emulation != Emulation.StarDotImpact;
addTitle("Like a StarIO-SDK Sample");
addMenu(languageCode + " " + paperSizeStr
+ " Text Receipt",canPrintTextReceipt);
addMenu(languageCode + " " + paperSizeStr
+ " Text Receipt (UTF8)",canPrintUtf8TextReceipt);
addMenu(languageCode + " " + paperSizeStr
+ " Raster Receipt",canPrintRasterReceipt);
這段程式碼先找從Setting中找出裝置的Emulation 模式
然後去比對,不一樣的模式可以印的東西也不一樣
建立 三個boolean的Parameter 並且設在addMenu( )中
設定false則此選項為 unclickable
第二:
ILocalizeReceipts localizeReceipts
= ILocalizeReceipts.createLocalizeReceipts(
mLanguage, mPaperSize);
String languageCode = localizeReceipts.getLanguageCode();
String paperSizeStr = localizeReceipts.getPaperSizeStr();
String scalePaperSizeStr
= localizeReceipts.getScalePaperSizeStr();
ILocalizeReceipts 是一個 abstract class
裡面會做一些語言、紙張大小的設定
還有一些abstract method 等著實作
不過傳入Language & PaperSize 後
return 出來的是繼承實作後對應語言的Class
Example:
switch (language) {
case PrinterSetting.LANGUAGE_ENGLISH:
localizeReceipts = new EnglishReceiptsImpl(); break;
case PrinterSetting.LANGUAGE_JAPANESE:
localizeReceipts = new JapaneseReceiptsImpl(); break;
由於我們這次選的是英文
所以會 return EnglishReceiptsImpl
EnglishReceiptsImpl 中會有各類型的Receipt、Coupon...等可做選擇
依我們剛剛的操作
點擊了第一項 Text Receipt 列印
PrintTask printTask = new PrintTask();
printTask.execute(position);
這時程式會建立一個 PrintTask
那這個Task做了什麼呢?
@Override
protected Communication.Result doInBackground(
Integer... params) {
byte[] commands;
int selectedIndex = params[0];
PrinterSetting setting = new PrinterSetting(getActivity());
Emulation emulation = setting.getEmulation();
ILocalizeReceipts localizeReceipts
= ILocalizeReceipts.createLocalizeReceipts(
mLanguage, mPaperSize);
switch (selectedIndex) {
default:
case 1:
commands
= PrinterFunctions.createTextReceiptData(
emulation, localizeReceipts, false);
break;
case 2:
commands
= PrinterFunctions.createTextReceiptData(
emulation, localizeReceipts, true);
break;
省略數行。。。
}
Communication.Result result;
result = Communication.sendCommands(commands
, setting.getPortName()
, setting.getPortSettings()
, 10000
, getActivity()); // 10000mS!!!
return result;
}
首先我們會從PrinterSetting取得我們的Enulation
public Emulation getEmulation() {
SharedPreferences prefs
= PreferenceManager.getDefaultSharedPreferences(mContext);
int emulationInt = prefs.getInt(PREF_KEY_EMULATION, -1);
for(Pair<Emulation, Integer> emulationPair : mEmulationList){
if (emulationInt == emulationPair.second) {
return emulationPair.first;
}
}
return Emulation.None;
}
還記得我們方才用了 write 寫進了 SharedPreferences 嗎?
這裡雖然是 new 了一個新的 Object
也沒傳遞任何 Parameter 進來
但實際上卻是從我們之前寫好的 SharedPreferences 讀的
所以可以順利得到我們連結裝置的 Emulation
再來的 switch 則是依我們剛剛的選擇來 Create Data
下面是我們選擇的第一個 Data 建立過程
public static byte[] createTextReceiptData(Emulation emulation
, ILocalizeReceipts localizeReceipts, boolean utf8) {
ICommandBuilder builder
= StarIoExt.createCommandBuilder(emulation);
builder.beginDocument();
localizeReceipts.appendTextReceiptData(builder, utf8);
builder.appendCutPaper(CutPaperAction.PartialCutWithFeed);
builder.endDocument();
return builder.getCommands();
}
依照Emulation來取得 ICommandBuilder
localizeReceipts來生產 Data
但 localizeReceipts 其實是 EnglishReceiptsImpl
所以生產 Data 的 Code 在這
public void appendTextReceiptData(ICommandBuilder builder
, boolean utf8) {
switch (mPaperSize) {
case PrinterSetting.PAPER_SIZE_TWO_INCH:
append2inchTextReceiptData(builder, utf8);
break;
case PrinterSetting.PAPER_SIZE_THREE_INCH:
append3inchTextReceiptData(builder, utf8);
break;
省略。。。
}
}
先依紙張大小選擇相應的 method
@Override
public void append2inchTextReceiptData(ICommandBuilder builder
, boolean utf8) {
Charset encoding;
if (utf8) {
encoding = Charset.forName("UTF-8");
builder.appendCodePage(CodePageType.UTF8);
}
else {
encoding = Charset.forName("US-ASCII");
builder.appendCodePage(CodePageType.CP998);
}
builder.appendInternational(InternationalType.USA);
builder.appendCharacterSpace(0);
builder.appendAlignment(AlignmentPosition.Center);
builder.append((
"Star Clothing Boutique\n" +
"123 Star Road\n" +
"City, State 12345\n" +
"\n").getBytes(encoding));
省略。。。
}
一開始會設定文字是UTF-8 or US-ASCII
再來為 Builder 設定 Location、文字間距,置中對齊
最後才開始加人文字
完成之後,就可以送出 Command 了
@Override
protected void onPostExecute(Communication.Result result){
if (!mIsForeground) {
return;
}
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
String msg;
switch (result) {
case Success:
msg = "Success!";
break;
case ErrorOpenPort:
msg = "Fail to openPort";
break;
省略。。。
}
CommonAlertDialogFragment dialog
= CommonAlertDialogFragment.newInstance(
"CommResultDialog");
dialog.setTitle("Communication Result");
dialog.setMessage(msg);
dialog.setPositiveButton("OK");
dialog.show(getChildFragmentManager());
}
}
Command 會 return 執行的狀態
把 msg Dialog 顯示給 User 看
就完成我們了這一次的列印了 !!!
參考資料:
www.starmicronics.com/support/sdkdocumentation.aspx#jpos
留言列表