[FIX] 優化點選商品之出貨口狀態判斷與防重複點擊機制

1. 重構 checkSlotHaveProduct 方法以支援安全回呼與 3000ms 超時保護。
2. 修正 DevCheckSlotByVMC 串口讀取未重置 haveThings 導致的狀態殘留 Bug。
3. 加入 isCheckingSlot 點選鎖,防呆避免非同步檢查期間重複點擊商品。
4. 於異常 AlertDialog 手動與自動關閉路徑中,正確釋放點選鎖。
5. 更新中英日三語系取貨口有物文字為「取貨口有東西,請先取出」,並將 Dialog 字體放大至 24sp。
6. 將 checkSlot 的串口讀取延遲由 2000ms 降至 800ms,大幅加速點選商品的反應時間。
This commit is contained in:
sky121113 2026-05-19 16:35:13 +08:00
parent ab8b828aa2
commit d13546b379
4 changed files with 71 additions and 15 deletions

View File

@ -4,9 +4,11 @@ import android.app.AlertDialog;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
@ -38,6 +40,7 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
private final List<SlotStructure> SlotList;
private FragmentAbstract fragment;
private boolean haveThings, doorError;
private volatile boolean isCheckingSlot = false; // 點擊鎖防止非同步檢查期間重複點擊
public RecyclerVmcBuyListAdpter(FragmentAbstract fragment, List<SlotStructure> slotList) {
this.logs = new Logs(this.getClass());
@ -155,6 +158,9 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
return;
if (MyApp.getInstance().isShowDialog())
return;
// 防呆正在檢查出貨口狀態時不允許再次點擊
if (isCheckingSlot)
return;
// 即時庫存檢查防止 MQTT 更新後 UI 尚未刷新的時間差
try {
@ -171,19 +177,37 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
// 查詢失敗時不阻擋讓原流程繼續
}
BuyStructure buyProduct = new BuyStructure(slotProduct.getField(), slotProduct.getSlot(),
slotProduct.getProduct(), 1);
MyApp.getInstance().getBuyList().add(buyProduct);
fragment.getSrcHandlerMain().start(getClass().getSimpleName(),
FontendActivity.Option.STOP_COUNTDOWN.getOption(), "stop countdown");
new MemberCardPickupDialog(context, fragment.getSrcHandlerMain()).show();
// 點選時即時檢查出貨口狀態上鎖防止重複點擊
isCheckingSlot = true;
checkSlotHaveProduct(slotProduct.getSlot(), 1, () -> {
isCheckingSlot = false;
BuyStructure buyProduct = new BuyStructure(slotProduct.getField(), slotProduct.getSlot(),
slotProduct.getProduct(), 1);
MyApp.getInstance().getBuyList().add(buyProduct);
fragment.getSrcHandlerMain().start(getClass().getSimpleName(),
FontendActivity.Option.STOP_COUNTDOWN.getOption(), "stop countdown");
new MemberCardPickupDialog(context, fragment.getSrcHandlerMain()).show();
});
}
}
public void checkSlotHaveProduct(int slotnum, int slotcount) {
public void checkSlotHaveProduct(int slotnum, int slotcount, Runnable onSafe) {
haveThings = false;
doorError = false;
java.util.concurrent.atomic.AtomicBoolean isHandled = new java.util.concurrent.atomic.AtomicBoolean(false);
// 設定 3000ms 超時機制如果機器沒反應視為正常並繼續避免程式卡住
if (onSafe != null) {
new Handler(Looper.getMainLooper()).postDelayed(() -> {
if (!isHandled.getAndSet(true)) {
logs.info("VMC Check Slot Timeout - Approving Safe (Timeout Safeguard)");
onSafe.run();
}
}, 3000);
}
logs.info("RecyclerDialogBuyListAdpter 111111111111111" + slotnum);
// MyApp.getInstance().getDevXinYuanController().getReadBufferByNow(new
// DevCheckSlotByVMC(handlerMain));
logs.info("RecyclerDialogBuyListAdpter 222222222222222" + slotcount);
MyApp.getInstance().getDevXinYuanController().addSendBuffer(
MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlotPre(slotnum, slotcount));
@ -191,13 +215,19 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
MyApp.getInstance().getDevXinYuanController()
.addSendBuffer(MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlot(slotnum));
logs.info("RecyclerDialogBuyListAdpter 444444444444444");
// 使用較短的延遲800ms來加速讀取回覆預設 2000ms checkSlot 查詢來說太慢
MyApp.getInstance().getDevXinYuanController()
.getReadBufferByNow(new DevCheckSlotByVMC(fragment.getSrcHandlerMain()));
.getReadBufferByNow(new DevCheckSlotByVMC(fragment.getSrcHandlerMain(), onSafe, isHandled), 800);
}
public class DevCheckSlotByVMC extends DevController.ReadBufferOnScheduler {
public DevCheckSlotByVMC(HandlerMain handlerMain) {
private Runnable onSafe;
private java.util.concurrent.atomic.AtomicBoolean isHandled;
public DevCheckSlotByVMC(HandlerMain handlerMain, Runnable onSafe, java.util.concurrent.atomic.AtomicBoolean isHandled) {
super(handlerMain);
this.onSafe = onSafe;
this.isHandled = isHandled;
}
@Override
@ -212,8 +242,9 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
@Override
public void readBufferOnScheduler(DevController devController, HandlerMain handlerMain) {
// 每次檢查前先重置狀態旗標
// 每次檢查前先重置狀態旗標修正 haveThings 狀態殘留 Bug
doorError = false;
haveThings = false;
for (byte[] buffer : devController.getReadBufferByMax()) {
if (buffer[0] == 0x02) {
getLogs().info("xxxxxxxxxxxxxxxxxxxxxxxxXinYuan Read:" + PortTools.showHex(buffer));
@ -231,6 +262,7 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
}
if (haveThings) {
if (isHandled.getAndSet(true)) return;
// 使用主執行緒顯示 AlertDialog
new Handler(Looper.getMainLooper()).post(() -> {
AlertDialog dialog = new AlertDialog.Builder(context)
@ -239,6 +271,7 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
.setCancelable(false)
.setPositiveButton(R.string.i_knew, (d, which) -> {
d.dismiss();
isCheckingSlot = false;
handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(),
"force close");
})
@ -246,16 +279,24 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
dialog.show();
// 放大提示文字
TextView messageView = dialog.findViewById(android.R.id.message);
if (messageView != null) {
messageView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
}
// 加入 10 秒後自動觸發點擊確定
new Handler(Looper.getMainLooper()).postDelayed(() -> {
if (dialog.isShowing()) {
dialog.dismiss();
isCheckingSlot = false;
handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(),
"force close");
}
}, 10_000); // 10 10000 毫秒
});
} else if (doorError) {
if (isHandled.getAndSet(true)) return;
// 取貨口門未關閉提示使用者並阻止後續購買流程
doorError = false;
handlerMain.start("RecycleFlowerBuyListAdpter", "取貨口門未正常關閉,請先確認門已關好");
@ -267,6 +308,7 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
.setCancelable(false)
.setPositiveButton(R.string.i_knew, (d, which) -> {
d.dismiss();
isCheckingSlot = false;
handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(),
"force close");
})
@ -274,15 +316,29 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
dialog.show();
// 放大提示文字
TextView messageView = dialog.findViewById(android.R.id.message);
if (messageView != null) {
messageView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
}
// 加入 10 秒後自動觸發點擊確定
new Handler(Looper.getMainLooper()).postDelayed(() -> {
if (dialog.isShowing()) {
dialog.dismiss();
isCheckingSlot = false;
handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(),
"force close");
}
}, 10_000); // 10 10000 毫秒
});
} else {
// 狀態安全且無異常執行購買回呼
if (!isHandled.getAndSet(true)) {
if (onSafe != null) {
new Handler(Looper.getMainLooper()).post(onSafe);
}
}
}
}
}

View File

@ -387,7 +387,7 @@
<string name="abnormal_response_message_error">Abnormal response, message error</string>
<string name="product_delivery_issues_contact_the_counter_window">For product delivery issues, please contact the counter window!</string>
<string name="abnormal_reminder">Abnormal reminder</string>
<string name="there_is_something_at_the_pickup_port">There is something at the pickup port, please contact the service staff to handle it!</string>
<string name="there_is_something_at_the_pickup_port">There is something at the pickup port, please remove it first</string>
<string name="i_knew">knew</string>
<string name="input_vehicle">Input Vehicle</string>
<string name="reload_product">Reload Products</string>

View File

@ -387,7 +387,7 @@
<string name="abnormal_response_message_error">異常応答、メッセージエラー</string>
<string name="product_delivery_issues_contact_the_counter_window">商品配送に関するトラブルはカウンター窓口までお問い合わせください!</string>
<string name="abnormal_reminder">異常なリマインダー</string>
<string name="there_is_something_at_the_pickup_port">ピックアップポートに何かある場合は、サービススタッフに連絡して対応してください!</string>
<string name="there_is_something_at_the_pickup_port">受取口に物があります、先に取り出してください</string>
<string name="i_knew">知っていた</string>
<string name="input_vehicle">スマホバーコード への入力</string>
<string name="sync_product">商品データを同期</string>

View File

@ -297,7 +297,7 @@
<string name="abnormal_response_message_error">回應異常,電文錯誤</string>
<string name="product_delivery_issues_contact_the_counter_window">商品出貨問題,請洽櫃台窗口!</string>
<string name="abnormal_reminder">異常提醒</string>
<string name="there_is_something_at_the_pickup_port">取貨口有東西,請聯繫服務人員處理!</string>
<string name="there_is_something_at_the_pickup_port">取貨口有東西,請先取出</string>
<string name="i_knew">知道了</string>
<string name="input_vehicle">輸入載具</string>
<string name="member_card_pickup_title">領取確認</string>