package com.example.tyre; import androidx.appcompat.app.AppCompatActivity; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.android.hdhe.uhf.reader.UhfReader; import com.android.hdhe.uhf.readerInterface.TagModel; import com.example.tyre.entity.AjaxResult; import com.example.tyre.entity.BaseCar; import com.example.tyre.entity.BaseTyre; import com.example.tyre.entity.EPC; import com.example.tyre.util.CarSelectionDialog; import com.example.tyre.util.CommonDialog; import com.example.tyre.util.MyUrl; import com.example.tyre.util.SharedPreferencesUtils; import com.example.tyre.util.Util; import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputLayout; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import com.lzy.okgo.OkGo; import com.lzy.okgo.callback.StringCallback; import com.lzy.okgo.model.Response; import java.util.ArrayList; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import cn.pda.serialport.Tools; public class TyreLayoutActivity extends AppCompatActivity { // 1. 使用 @BindView 绑定控件 // 注意:变量名可以自定义,但 ID 必须对应 XML 中的 id @BindView(R.id.et_plate_number) TextInputEditText etPlateNumber; @BindView(R.id.btn_retrieve) Button btnRetrieve; // 因为使用的是 MaterialButton,这里类型要对应 @BindView(R.id.til_plate_number) TextInputLayout tilPlateNumber; private ProgressDialog progressDialog; private boolean isStart = true; private boolean runFlag = true; private boolean startFlag = false; private UhfReader manager; // UHF manager,UHF Operating handle private ArrayList listEPC; private ArrayList listepc = new ArrayList(); long lastTime; long nextTime; private Toast mToast; private Toast toast; private KeyReceiver keyReceiver; String min = "EC0001012026010100000001"; // 左边界(包含) String max = "EC0001012026010100100000"; // 右边界(包含) // 1. 定义数据模型类 (对应后台返回的字段) public static class TireData { public String position; // 轮位名称 public String brand; // 品牌 public String spec; // 规格 public String installDate; // 安装日期 public TireData(String position, String brand, String spec, String installDate) { this.position = position; this.brand = brand; this.spec = spec; this.installDate = installDate; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tyre_layout); ButterKnife.bind(this); manager = MyApplication.getManager(); listEPC = new ArrayList(); Thread thread = new InventoryThread(); thread.start(); Util.initSoundPool(this); tilPlateNumber = findViewById(R.id.til_plate_number); etPlateNumber.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(Editable s) { // 核心代码:只要用户输入了内容,就清除错误提示 tilPlateNumber.setError(null); // 可选:如果你使用了 errorEnabled="true",可能还需要调用下面这行来彻底清除空间占用 // tilPlateNumber.setErrorEnabled(false); } }); tilPlateNumber.setEndIconOnClickListener(v -> { // 在这里处理搜索逻辑 String plateNumber = etPlateNumber.getText().toString().trim(); if (!plateNumber.isEmpty()) { // 执行搜索 showLoadingDialog(); performSearch(plateNumber); } else { tilPlateNumber.setError("车牌号不能为空"); } }); } @Override public void onResume() { super.onResume(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } registerReceiver(); } @Override public void onPause() { startFlag = false; super.onPause(); unregisterReceiver(); } @Override protected void onDestroy() { startFlag = false; runFlag = false; super.onDestroy(); } private void performSearch(String carNo) { OkGo.post(MyUrl.url + "/tyre/car/PdaQueryCarList") .tag(this).params("carNo", carNo) .execute(new StringCallback() { @Override public void onSuccess(Response response) { hideLoadingDialog(); String body = response.body(); Log.d("CarQuery", "Response: " + body); Gson gson = new Gson(); try { // 1. 将响应体解析为 JsonObject (通用 JSON 对象) // 这样可以绕过 AjaxResult 类对 data 字段的 String 类型限制 com.google.gson.JsonObject jsonObject = gson.fromJson(body, com.google.gson.JsonObject.class); // 2. 获取 code 字段 (注意:你的日志显示 code 是数字 0,但 AjaxResult 定义是 String,这里做兼容处理) String code = ""; if (jsonObject.has("code")) { // 尝试获取 code,无论是 "0" 还是 0 都能处理 code = jsonObject.get("code").getAsString(); } // 3. 判断成功状态 (根据你的日志,0 代表成功) if ("0".equals(code) || "200".equals(code)) { // 4. 获取 data 元素 if (jsonObject.has("data")) { com.google.gson.JsonElement dataElement = jsonObject.get("data"); // 5. 将 data 元素转换为 List // 使用 TypeToken 指定泛型类型 java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>() {}.getType(); List carList = gson.fromJson(dataElement, type); if (carList != null && !carList.isEmpty()) { // 6. 展示弹窗列表 showCarSelectionDialog(carList); } else { // 7. 无数据处理 new CommonDialog(TyreLayoutActivity.this).setMessage("未查询到相关车辆信息").show(); } } } else { // 业务错误处理 String msg = jsonObject.has("msg") ? jsonObject.get("msg").getAsString() : "未知错误"; new CommonDialog(TyreLayoutActivity.this).setMessage(msg).show(); } } catch (Exception e) { e.printStackTrace(); new CommonDialog(TyreLayoutActivity.this).setMessage("数据解析异常").show(); } } }); } /** * 展示车辆选择弹窗 * @param carList 车辆列表 */ private void showCarSelectionDialog(List carList) { if (carList.size() == 1) { // 如果只有一辆车,直接填充,无需弹窗 BaseCar car = carList.get(0); fillCarInfoToUI(car); //Util.play(1, 0); // 提示音 Toast.makeText(this, "自动匹配车辆:" + car.getCarNo(), Toast.LENGTH_SHORT).show(); } else { // 如果有多辆车,展示弹窗供用户选择 // 提取车牌号列表用于显示 List licensePlates = new ArrayList<>(); for (BaseCar car : carList) { licensePlates.add(car.getCarNo() != null ? car.getCarNo() : "未知车牌"); } // 创建并显示弹窗 CarSelectionDialog dialog = new CarSelectionDialog(this, licensePlates); dialog.setOnCarSelectedListener((selectedItem) -> { // selectedItem 是 Object,转成 String String selectedPlate = selectedItem.toString().trim(); // 遍历列表找到对应的对象 for (BaseCar car : carList) { if (selectedPlate.equals(car.getCarNo())) { fillCarInfoToUI(car); break; } } }); dialog.show(); } } /** * 将车辆信息填充到界面控件 * @param car 车辆对象 */ private void fillCarInfoToUI(BaseCar car) { // 填充车牌号 etPlateNumber.setText(safeGetString(car.getCarNo())); // 如果有其他字段,例如: // startEdit.setText(safeGetString(car.getDefaultMileage())); // 默认里程 // 视觉反馈 etPlateNumber.setTextColor(getColor(R.color.green500)); // Util.play(1, 0); } @OnClick({R.id.btn_retrieve}) public void onViewClicked(View view) { String PlateNumber = etPlateNumber.getText().toString(); switch (view.getId()) { case R.id.btn_retrieve: if (PlateNumber == null || PlateNumber.isEmpty()) { new CommonDialog(TyreLayoutActivity.this).setMessage("请输入车牌号!").show(); return; } carBangding(PlateNumber); break; } } //绑定车牌与RFID信息 private void carBangding(String PlateNumber) { OkGo.post(MyUrl.url + "/tyre/tyre/getCarBingTire") .tag(this).params("carNo", PlateNumber) .execute(new StringCallback() { @Override public void onSuccess(Response response) { //hideLoadingDialog(); String body = response.body(); Gson gson=new Gson(); // List baseTyreList = gson.fromJson(body, BaseTyre.class); List baseTyreList = gson.fromJson(body, new com.google.gson.reflect.TypeToken>(){}.getType()); bindDataToViews(baseTyreList); } }); } private void findCar(String carNumber) { OkGo.post(MyUrl.url + "/tyre/car/queryCarByRfid").tag(this).params("rfid", carNumber).execute(new StringCallback() { @Override public void onSuccess(Response response) { String body = response.body(); hideLoadingDialog(); try { Gson gson = new Gson(); BaseCar baseCar = gson.fromJson(body, BaseCar.class); if (baseCar != null) { // 空值处理:若字段为 null 则显示空字符串 String carNo = safeGetString(baseCar.getCarNo()); etPlateNumber.setText(carNo); }else { new CommonDialog(TyreLayoutActivity.this).setMessage("请检查车辆芯片绑定数据!").show(); } } catch (JsonSyntaxException e) { return; } } }); } private String safeGetString(String value) { return value == null || "null".equals(value) ? "" : value; } /** * 核心方法:将数据绑定到具体的 Include 视图上 */ private void bindDataToViews(List dataList) { if (dataList == null) return; // 遍历列表,根据具体位置找到对应的 View for (BaseTyre tyre : dataList) { View targetView = null; // 根据后端返回的位置字段判断要更新哪个布局 // 假设 tyre.getPosition() 返回 "LF" (左前), "RF" (右前) 等 String pos = tyre.getWheelPostion(); if ("左前轮".equals(pos) || "1".equals(pos)) { targetView = findViewById(R.id.tire_left_front); } else if ("右前轮".equals(pos) || "2".equals(pos)) { targetView = findViewById(R.id.tire_right_front); } else if ("左内轮".equals(pos)) { targetView = findViewById(R.id.tire_middle_left); }else if ("右内轮".equals(pos)) { targetView = findViewById(R.id.tire_middle_right); }else if ("左外轮".equals(pos)) { targetView = findViewById(R.id.tire_bottom_left); }else if ("右外轮".equals(pos)) { targetView = findViewById(R.id.tire_bottom_right); } // ... 其他位置 if (targetView != null) { updateTireView(targetView, tyre); } } } // private void bindDataToViews(List dataList) { // // 假设 dataList.get(0) 是左前轮,dataList.get(1) 是右前轮... // // 请根据你实际的列表顺序调整索引 // // if (dataList.size() >= 2) { // // 绑定左前轮 // updateTireView(findViewById(R.id.tire_left_front), dataList.get(0)); // // // 绑定右前轮 // updateTireView(findViewById(R.id.tire_right_front), dataList.get(1)); // // // 绑定其他轮胎... // // updateTireView(findViewById(R.id.tire_left_mid), dataList.get(2)); // } // } /** * 通用方法:更新单个轮胎卡片的 UI * @param container include 标签对应的根布局 View * @param data 该轮胎的数据对象 */ private void updateTireView(View container, BaseTyre data) { if (container == null) return; // 【关键点】:必须在 container 内部查找控件,而不是在 Activity 中直接查找 TextView tvBrand = container.findViewById(R.id.tv_brand); TextView tvSpec = container.findViewById(R.id.tv_spec); TextView tvSelfNo = container.findViewById(R.id.tv_self_no); // 或者是 tv_install_date TextView tvDate = container.findViewById(R.id.tv_install_date); // 或者是 tv_install_date // 设置数据 if (tvBrand != null) tvBrand.setText("品牌: " + data.getTyreBrand()); if (tvSpec != null) tvSpec.setText("规格: " + data.getTyreModel()); if (tvSelfNo != null) tvSelfNo.setText("自编号: " + data.getSelfNo()); if (tvDate != null) tvDate.setText("日期: " + data.getCreateTime()); } private void addToList(final List list, final String epc, final byte rssi) { runOnUiThread(new Runnable() { @Override public void run() { // The epc for the first time if (list.isEmpty()) { EPC epcTag = new EPC(); epcTag.setEpc(epc); epcTag.setCount(1); epcTag.setRssi(rssi); list.add(epcTag); listepc.add(epc); }else { for (int i = 0; i < list.size(); i++) { EPC mEPC = list.get(i); // list contain this epc if (epc.equals(mEPC.getEpc())) { mEPC.setCount(mEPC.getCount() + 1); mEPC.setRssi(rssi); list.set(i, mEPC); break; } else if (i == (list.size() - 1)) { // list doesn't contain this epc EPC newEPC = new EPC(); newEPC.setEpc(epc); newEPC.setCount(1); newEPC.setRssi(rssi); list.add(newEPC); listepc.add(epc); } } } // play sound lastTime = SystemClock.elapsedRealtime(); long time = lastTime - nextTime; if (time >= 60) { Util.play(1, 0); nextTime = lastTime; Log.e("TAG", "run: " + time); } if (listepc != null && !listepc.isEmpty()){ startFlag = false; String currentEpc = listepc.get(0); if (isInRange(currentEpc, min, max)) { //请求后台 查询车辆信息 // car.setText(currentEpc); //请求后台 debounceHandler.removeMessages(1); lastScannedEpc = currentEpc; debounceHandler.sendEmptyMessageDelayed(1, DEBOUNCE_DELAY); } } clearData(); } }); Log.e("EPC", "listepc:+ " + listepc); } /** * 判断字符串是否在指定的区间内(包含边界) * @param target 要判断的目标字符串 * @param min 区间左边界 * @param max 区间右边界 * @return true=在区间内,false=不在 */ public static boolean isInRange(String target, String min, String max) { // 空值校验,避免空指针异常 if (target == null || min == null || max == null) { return false; } // compareTo规则: // 1. 字符串相等返回0; // 2. 目标字符串 > 对比字符串 返回正数; // 3. 目标字符串 < 对比字符串 返回负数。 // 因此:target >= min 等价于 target.compareTo(min) >= 0; // target <= max 等价于 target.compareTo(max) <= 0。 return target.compareTo(min) >= 0 && target.compareTo(max) <= 0; } // 防抖延迟时间 (毫秒) private static final long DEBOUNCE_DELAY = 500; // 用于存储最后一次扫描到的 EPC private String lastScannedEpc; // 用于处理防抖逻辑的 Handler private Handler debounceHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == 1 && lastScannedEpc != null) { // 延迟时间到,执行查询 showLoadingDialog(); //查询车辆数据方法 findCar(lastScannedEpc); lastScannedEpc = null; // 清空,等待下一次扫描 } return true; } }); private void clearData() { listEPC.removeAll(listEPC); listepc.removeAll(listepc); } private class KeyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int keyCode = intent.getIntExtra("keyCode", 0); if (keyCode == 0) { keyCode = intent.getIntExtra("keycode", 0); } boolean keyDown = intent.getBooleanExtra("keydown", false); if (keyDown) { if (toast == null) { // toast = Toast.makeText(OutStoreHouseActivity.this, "KeyReceiver:keyCode = down" + keyCode, Toast.LENGTH_SHORT); } else { // toast.setText("KeyReceiver:keyCode = down" + keyCode); } // toast.show(); switch (keyCode) { case KeyEvent.KEYCODE_F1: case KeyEvent.KEYCODE_F2: case KeyEvent.KEYCODE_F3: case KeyEvent.KEYCODE_F4: case KeyEvent.KEYCODE_F5: //扫描 startFlag = true; break; } } } } private void registerReceiver() { keyReceiver = new KeyReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("android.rfid.FUN_KEY"); filter.addAction("android.intent.action.FUN_KEY"); registerReceiver(keyReceiver, filter); } private void unregisterReceiver() { unregisterReceiver(keyReceiver); } class InventoryThread extends Thread { private List tagList; byte[] accessPassword = Tools.HexString2Bytes("00000000"); @Override public void run() { super.run(); while (runFlag) { if (startFlag) { tagList = manager.inventoryRealTime(); //实时盘存 if (tagList != null && !tagList.isEmpty()) { //播放提示音 Util.play(1, 0); for (TagModel tag : tagList) { if (tag == null) { String epcStr = ""; // String epcStr = new String(epc); addToList(listEPC, epcStr, (byte) -1); } else { String epcStr = Tools.Bytes2HexString(tag.getmEpcBytes(), tag.getmEpcBytes().length); // String epcStr = new String(epc); byte rssi = tag.getmRssi(); addToList(listEPC, epcStr, rssi); } } } tagList = null; try { Thread.sleep(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } private void showLoadingDialog() { progressDialog = new ProgressDialog(this); progressDialog.setMessage("数据加载中..."); progressDialog.setCancelable(false); progressDialog.show(); } private void hideLoadingDialog() { if (progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } } }