public class LegacyCamera2Scanner extends Scanner ...
创建于:2025年3月18日
创建于:2025年3月18日
public class LegacyCamera2Scanner extends Scanner
implements BarcodeReader.DecodeCallback,
BarcodeReader.DecoderListener,
OverlayPreviewWindow.Listener {
textprivate static final String TAG = "LegacyCamera2Scanner"; private CaptureHandler mUiHandler; //进入休眠模式时延Sleep mode delay private static long InactivityTimer_DURATION_MS = 15 * 1000L; private static final long InactivityTimer__DURATION_CONTINUOUS_MODE_MS = 60 * 1000L; //预览开始当前实际时间 private long workPreviewTime = 0; //是否进入自动休眠预览模式 private boolean workDecodeTimeOutSleep = true; private int CONTINUOUS_MODE = 8; private int saveMode = SaveMode.NOTSAVE.ordinal(); private int openCameraId = 0; private int delayMillis = 0; private long decodeEndTime, decodeStartTime, decodeTime; //skip_first_frame private BarcodeReader mBarcodeReader; private String decoderVersion = ""; /** * 连扫模式下,固定时间内过滤相同条码输出,初始时间 */ private long waitRepeatStartTime = 0; /** * 是否重复解码 * {@link android.device.scanner.configuration.PropertyID#DEC_Multiple_Decode_MODE} */ private int ignoreRepeatMode = 0; /** * 间隔时间内,相同条码不重复解码 * {@link android.device.scanner.configuration.PropertyID#DEC_Multiple_Decode_TIMEOUT} */ private int waitRepeatTimeOut = 10 * 1000; /** * 连扫模式读取多个条码后输出.实现固定时间内不重复解码,配置同张图片最多解码个数 * {@link android.device.scanner.configuration.PropertyID#DEC_MaxMultiRead_COUNT} */ private int cotinuousMultiReadCount = 2; //连扫模式去重缓存条码数据 private byte[] preDecodeData = null; /** * 一次解多码模式。(注:连续扫描模式下不应该存在一次解多码) * {@link android.device.scanner.configuration.PropertyID#MULTI_DECODE_MODE} */ private int multiDecodeMode = 0;// flag for single or multiple decode /** * 一次解多码模式下,解出固定个数后才输出 * {@link android.device.scanner.configuration.PropertyID#FULL_READ_MODE} */ private int fullReadMode = 1; //当前解码的条码个数 private int g_nMultiReadResultCount = 0; // For tracking # of multiread results /** * 一次解多码模式下,最多要解码的个数 * {@link android.device.scanner.configuration.PropertyID#BAR_CODES_TO_READ} */ private int g_nMaxMultiReadCount = 1; // Maximum multiread count /** * {@link android.device.scanner.configuration.PropertyID#TRANSMIT_CODE_ID} */ private int transmitCodeID = 0; /** * {@link android.device.scanner.configuration.PropertyID#LABEL_SEPARATOR_ENABLE} * 应用标识符标示字符,默认() */ private int enableGroupSeperator = 0; /** * {@link android.device.scanner.configuration.PropertyID#LABEL_FORMAT_SEPARATOR_CHAR} * 应用标识符标示字符,默认() */ private String groupSeperatorCharacters = "()"; private boolean activateDecoder = false; private int activateDecoderTimes = 5; private boolean hasLicense = true; private boolean syncLicenseStrore = false; private byte[] headOfByte = null; private byte[] bitmapByte = null; private int[] bounds = new int[10]; private static int MAX_FRAME_WIDTH = 1280; private static int MAX_FRAME_HEIGHT = 720; private int aimerIllumMode = 3; private int intervalTime = 0; private OverlayPreviewWindow mWindow; /** * An additional thread for running tasks that shouldn't block the UI. * 本质继承Thread类&封装Handler类 */ private LegacyCamera2Scanner.DecodeHandlerThread mDecodeHandlerThread; private LegacyCamera2Scanner.DecodeHandler mDecodeHandler; private com.ubx.scanner.camera.CameraManager mCameraManager; private SurfaceHolder surfaceHolder; private static final int BCRDR_MSG_DECODE_COMPLETE = 1; private static final int BCRDR_MSG_DECODE_TIMEOUT = 2; private static final int BCRDR_MSG_DECODE_CANCELED = 3; private static final int BCRDR_MSG_DECODE = 4; private static final int BCRDR_MSG_DECODE_PREVIEW = 5; private static final int BCRDR_MSG_DECODE_REPREVIEW = 6; private static final int BCRDR_MSG_FRAME_ERROR = 7; private static final int BCRDR_MSG_INIT = 8; private static final int BCRDR_MSG_RELEASE = 9; private static final int BCRDR_MSG_INIT_DECODE = 10; private static final int BCRDR_MSG_TIMER_PREVIEW = 11; public static final int MESSAGE_CAPTURE_IMAGE = 12; public static final int MESSAGE_MULTI_DECODE_COMPLETE = 13; public static final int MESSAGE_CONTINUOUS_MULTI_DECODE_COMPLETE = 14; public static final int MESSAGE_RELEASE_QUIT = 15; private int orientationDegress = 90; BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (CAPTURE_IMAGE_ACTION.equals(intent.getAction())) { if (mUiHandler != null) { Message m = Message.obtain(mUiHandler, MESSAGE_CAPTURE_IMAGE); Bundle bundle = intent.getExtras(); m.obj = bundle; mUiHandler.sendMessage(m); } } else if (ENABLE_BROADCAST_IMAGE_ACTION.equals(intent.getAction())) { Bundle bundle = intent.getExtras(); BROADCAST_IMAGE_ACTION = bundle.getString("imageAction", BROADCAST_IMAGE_ACTION); BROADCAST_IMAGE_ACTION_EXTRAS = bundle.getString("imageExtras", BROADCAST_IMAGE_ACTION_EXTRAS); rotateImage = bundle.getInt("rotate", 0); enableSendBarcodeAndBitmap = bundle.getBoolean("enableSendImage", false); } } }; // 单例对象 private static LegacyCamera2Scanner sInstance; // 上下文 private Context mContext; private WindowManager mWindowManager; // 条码相关
// private BarcodeReader mBarcodeReader;
private BarcodeReader.DecodeCallback mDecodeCallback;
private DecoderListener mDecoderListener;
text// 状态标记 private boolean mIsOpened = false; // Scanner 是否打开 private boolean mIsDecoding = false; // 是否处于解码中 private boolean mIsCameraInitialized = false; // 相机是否初始化 // 相机2相关 private CameraDevice mCameraDevice; private CameraCaptureSession mCaptureSession; private CaptureRequest.Builder mPreviewRequestBuilder; private Size mPreviewSize; private ImageReader mImageReader; private HandlerThread mBackgroundThread; private Handler mBackgroundHandler; private String mCameraId; // 预览相关 private View mCameraPreviewLayout; private TextureView mPreviewView; private View mBlackView; private SurfaceTexture mSurfaceTexture; private SurfaceHolder mSurfaceHolder; private int mSurfaceWidth; private int mSurfaceHeight; // 浮动按钮相关 private View mFloatingButtonView; private WindowManager.LayoutParams mFloatingButtonParams; private boolean mIsDragging = false; private int mInitialX, mInitialY; private float mInitialTouchX, mInitialTouchY; // 解码结果 private HSMDecodeResult mLastDecodeResult; private byte[] mLastDecodeImage; // 多码解码参数(示例保留) private int mMultiDecodeMode = 0; private int mMaxMultiReadCount = 2; private final Runnable mShowRunnable = new Runnable() { @Override public void run() { OverlayPreviewWindow window = new OverlayPreviewWindow(mContextWrapper,MAX_FRAME_WIDTH, MAX_FRAME_HEIGHT, 0,orientationDegress, LegacyCamera2Scanner.this); window.show(aimerIllumMode); synchronized (mDecodeLock) { mWindow = window; } } }; // Runs on the UI thread. private final Runnable mDismissRunnable = new Runnable() { @Override public void run() { OverlayPreviewWindow window; synchronized (mDecodeLock) { window = mWindow; mWindow = null; } //mCameraManager.setTorch(false); surfaceHolder = null; if (window != null) { window.dismiss(); } } }; private final class DecodeHandlerThread extends HandlerThread { private Looper myLooper; public DecodeHandlerThread(String name) { super(name); start(); myLooper = this.getLooper(); } public void startThread() { mDecodeHandler = new DecodeHandler(myLooper); } } final class DecodeHandler extends Handler { private boolean running = true; DecodeHandler(Looper loop) { super(loop); } @Override public void handleMessage(Message message) { if (!running) { return; } if (message.what == BCRDR_MSG_DECODE) { decode((byte[]) message.obj, message.arg1, message.arg2); } else if (message.what == MESSAGE_RELEASE_QUIT) { running = false; Looper.myLooper().quit(); } } /** * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, * reuse the same reader objects from one decode to the next. * * @param data The YUV preview frame. * @param width The width of the preview frame. * @param height The height of the preview frame. */ private void decode(byte[] data, int width, int height) { Log.d(TAG, "decode width=" + width + " height = " + height); workPreviewTime = System.currentTimeMillis(); int ret = mBarcodeReader.decodeImage(data, width, height); if (mUiHandler != null) { HSMDecodeResult firstResult = null; if(ret > 0) { firstResult = mBarcodeReader.getDecodeData(); } Log.d(TAG, "decode: ret: " + ret + " firstResult == null: " + (firstResult == null)); long end = System.currentTimeMillis(); Log.i(TAG, " startDecode data dec="+ (end - workPreviewTime) + " ms"); if (ret > 0 && firstResult != null) { if(CONTINUOUS_MODE == 4) { if (ignoreRepeatMode == 1) { if (preDecodeData != null && System.currentTimeMillis() - waitRepeatStartTime < waitRepeatTimeOut && compareBytes(firstResult.getBarcodeDataBytes(), firstResult.getBarcodeDataLength())) { decodeStartTime = System.currentTimeMillis(); return; } } g_nMultiReadResultCount++; preDecodeData = firstResult.getBarcodeDataBytes(); waitRepeatStartTime = System.currentTimeMillis(); Message message = mUiHandler.obtainMessage(BCRDR_MSG_DECODE_COMPLETE); DecodeData decodeData = new DecodeData(); decodeData.setBarcodeDataBytes(firstResult.getBarcodeDataBytes()); decodeData.setBarcodeDataLength(firstResult.getBarcodeDataLength()); decodeData.setDecodeTime(end - workPreviewTime); decodeData.setSymbologyId(firstResult.getSymbologyId()); decodeData.setCodeId(firstResult.getCodeId()); decodeData.setAIMCodeLetter(firstResult.getAIMCodeLetter()); decodeData.setAIMModifier(firstResult.getAIMModifier()); message.obj = decodeData; message.sendToTarget(); } else { Message message = mUiHandler.obtainMessage(BCRDR_MSG_DECODE_COMPLETE); DecodeData decodeData = new DecodeData(); decodeData.setBarcodeDataBytes(firstResult.getBarcodeDataBytes()); decodeData.setBarcodeDataLength(firstResult.getBarcodeDataLength()); decodeData.setDecodeTime(workPreviewTime - decodeStartTime); decodeData.setSymbologyId(firstResult.getSymbologyId()); decodeData.setCodeId(firstResult.getCodeId()); decodeData.setAIMCodeLetter(firstResult.getAIMCodeLetter()); decodeData.setAIMModifier(firstResult.getAIMModifier()); message.obj = decodeData; message.sendToTarget(); if (enableSendBarcodeAndBitmap) { String barcode = new String(firstResult.getBarcodeDataBytes(), 0, firstResult.getBarcodeDataLength()); byte[] m_frameData = mLastDecodeImage;
// byte[] m_frameData = mBarcodeReader.getLastDecImage();
int[] bounds = firstResult.getBarcodeBounds();
sendBarcodeAndBitmapBroadcast(barcode, m_frameData, bounds);
}
}
/if (saveMode == SaveMode.SAVEDECODESUCCESSALLBMP.getVal()) {
Utils.saveYUVImage(mContext, data, width, height, end);
} else if (saveMode == SaveMode.SAVEDECODESUCCESSLASTBMP.getVal()) {
Utils.saveYUVImage(mContext, data, width, height, 0);
}/
}/* else {
Message message = Message.obtain(mUiHandler, BCRDR_MSG_FRAME_ERROR);
message.sendToTarget();
}*/
}
/if (saveMode == SaveMode.SAVEPREVIEWALLBMP.getVal()) {
Utils.saveYUVImage(mContext, data, width, height, start);
} else if (saveMode == SaveMode.SAVEPREVIEWLASTBMP.getVal()) {
Utils.saveYUVImage(mContext, data, width, height, 0);
}/
}
text} class CaptureHandler extends Handler { public CaptureHandler(Looper looper) { super(looper); state = State.DONE; } @Override public void handleMessage(Message message) { switch (message.what) { case BCRDR_MSG_DECODE_COMPLETE: workPreviewTime = System.currentTimeMillis(); if(CONTINUOUS_MODE == 4) { if(intervalTime > 0) { this.sendEmptyMessageDelayed(BCRDR_MSG_DECODE_PREVIEW, intervalTime); } else { /*if (mCameraManager != null) { mCameraManager.setDecodeHandler(mDecodeHandler); }*/ } } else { removeMessages(BCRDR_MSG_DECODE_TIMEOUT); state = State.DONE; mIsDecoding = false; //sendBroadcast((byte[])message.obj, message.arg2, message.arg1); /*if (mCameraManager != null) { mCameraManager.stopDecoding(); }*/
// stopDecode();
/if (mImageReader != null) {
mImageReader.close();
}/
stopDecode();
/stopPreview();
closeCamera();
new Handler(Looper.getMainLooper()).post(() -> {
mCameraPreviewLayout.setVisibility(View.INVISIBLE);
});
if (mBarcodeReader != null) {
Log.d(TAG, "handleMessage: BCR.stopDecode(), start: " + state);
mBarcodeReader.stopDecode();
state = State.PREVIEW;
}/
// mUiHandler.post(mDismissRunnable);
}
DecodeData decodeData = (DecodeData)message.obj;
int codeId = decodeData.getCodeId();
int SymbologyId = OEMSymbologyId.getHSMSymbologyId(codeId);
String symName = OEMSymbologyId.stringFromHSMSymbology(codeId);
aimCodeId[0] = ']';
aimCodeId[1] = decodeData.getAIMCodeLetter();
aimCodeId[2] = decodeData.getAIMModifier();
byte[] byteArraryData = decodeData.getBarcodeDataBytes();
String barcode = new String(decodeData.getBarcodeDataBytes(), 0, decodeData.getBarcodeDataLength());
Log.d(TAG, "onDecodeComplete: barcode: " + barcode);
Log.d(TAG, "handleMessage: transmitCodeID: " + transmitCodeID + " enableGroupSeperator: " + enableGroupSeperator);
if (transmitCodeID > 0 || enableGroupSeperator == 1) {
byte[] barcodeDataBytes = composeAISeparator(aimCodeId[1], aimCodeId[2], codeId, byteArraryData, decodeData.getBarcodeDataLength());
if (barcodeDataBytes != null) {
if (mOnScannedListener != null) {
mOnScannedListener.receivedDecodedData(barcodeDataBytes, barcodeDataBytes.length, aimCodeId, SymbologyId, symName, decodeTime);
}
} else {
if (mOnScannedListener != null) {
mOnScannedListener.receivedDecodedData(byteArraryData, byteArraryData.length, aimCodeId, SymbologyId, symName, decodeTime);
}
}
} else {
if (mOnScannedListener != null) {
mOnScannedListener.receivedDecodedData(byteArraryData, byteArraryData.length, aimCodeId, SymbologyId, symName, decodeTime);
}
}
break;
case BCRDR_MSG_DECODE_PREVIEW:
workPreviewTime = System.currentTimeMillis();
if (mCameraManager != null && state == State.DECODING) {
mCameraManager.setDecodeHandler(mDecodeHandler);
}
break;
case BCRDR_MSG_FRAME_ERROR:
if(state == State.DECODING) {
mCameraManager.setDecodeHandler(mDecodeHandler);
}
break;
case BCRDR_MSG_DECODE_TIMEOUT:
state = State.DONE;
stopDecode();
break;
case BCRDR_MSG_TIMER_PREVIEW:
long currentTime = System.currentTimeMillis();
Log.d(TAG, "timer_preview " + (currentTime - workPreviewTime));
if (currentTime - workPreviewTime >= (CONTINUOUS_MODE == 4 ? InactivityTimer__DURATION_CONTINUOUS_MODE_MS : InactivityTimer_DURATION_MS)) {
if (mBarcodeReader != null) {
mBarcodeReader.stopDecode();
}
if(mCameraManager != null) {
mCameraManager.closeDriver();
}
workPreviewTime = 0;
state = State.PREVIEW;
} else {
workPreviewTime = System.currentTimeMillis();
sendEmptyMessageDelayed(BCRDR_MSG_TIMER_PREVIEW, CONTINUOUS_MODE == 4 ? InactivityTimer__DURATION_CONTINUOUS_MODE_MS : InactivityTimer_DURATION_MS);
}
break;
case BCRDR_MSG_INIT:
connectDecoderLibrary();
break;
case BCRDR_MSG_RELEASE:
disconnectDecoderLibrary();
break;
case MESSAGE_CAPTURE_IMAGE:
Bundle bundle = (Bundle) message.obj;
int w = 0;
int h = 0;
int rotate = 0;
if (bundle != null) {
w = bundle.getInt("ImageWidth", 0);
h = bundle.getInt("ImageHeight", 0);
rotate = bundle.getInt("rotate", 0);
}
captureImage(w, h, rotate);
break;
case MESSAGE_MULTI_DECODE_COMPLETE:
receivedMultipleData();
break;
case MESSAGE_CONTINUOUS_MULTI_DECODE_COMPLETE:
if (mBarcodeReader != null && state == State.DECODING) {
mBarcodeReader.stopDecode();
}
state = State.PREVIEW;
receivedMultipleData();
break;
}
}
text} private void captureImage(int width, int height, int rotate) { if (mBarcodeReader != null) { try { Intent intent = new Intent(BROADCAST_IMAGE_ACTION); byte[] m_frameData = mBarcodeReader.getLastDecImage(); if (m_frameData != null && m_frameData.length > 0/* && bitmapByte != null*/) { Bitmap modBm = createBitmap(m_frameData, rotate); if (modBm != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); modBm.compress(Bitmap.CompressFormat.JPEG, 50, baos); modBm.recycle(); modBm = null; intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, baos.toByteArray()); } else { intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, new byte[1]); } if (bounds != null) { for (int i = 0; i < bounds.length; i++) { if (i % 2 == 0) { bounds[i] = MAX_FRAME_WIDTH - bounds[i]; } else { bounds[i] = MAX_FRAME_HEIGHT - bounds[i]; } } intent.putExtra("bounds", bounds); } } else { intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, new byte[1]); } mContextWrapper.sendBroadcastAsUser(intent, UserHandleEx.ALL); } catch (Exception e) { e.printStackTrace(); } } } private int connectDecoderLibrary() { /*final boolean enable = LicenseHelper.setWifiWnable(mContextWrapper, true); if (enable == false) { File wifiAddress = new File("sys/class/net/wlan0/address"); for (int i = 0; i < 8; i++) { try { Thread.sleep(100); } catch (Exception e) { } if (wifiAddress.exists()) { Log.d(TAG, " getwifiAddress sleep " + i); break; } } } try { new Thread(new Runnable() { @Override public void run() { //接口有访问远程服务器动作,避免服务器或网络异常需超时才退出,造成ANR //if(BarcodeReader.activateLicense(ActivationManager.Activation_DIR) != 0){ if (ActivationManager.activateAPIWithLocalFile(mContextWrapper, false) == false) { activateDecoder = false; } else { activateDecoder = true; if (!syncLicenseStrore) { syncLicenseStrore(true); syncLicenseStrore = true; } } if (enable == false && activateDecoder) LicenseHelper.setWifiWnable(mContextWrapper, enable); } }).start(); for (int i = 0; i < 2; i++) { Thread.sleep(70); if (activateDecoder) { Log.d(TAG, " activateDecoder sleep " + i); break; } Thread.sleep(30); } } catch (Exception e) { e.printStackTrace(); }*/ try{ mBarcodeReader = BarcodeReader.open(-1, mContextWrapper); } catch (ReaderException e) { e.printStackTrace(); mBarcodeReader = null; } if (mBarcodeReader != null) { byte[] version = new byte[256]; mBarcodeReader.reportDecoderVersion(version); decoderVersion = (new String(version)).trim(); Log.d(TAG, "version " + decoderVersion); mBarcodeReader.setDecodeCallback(LegacyCamera2Scanner.this); mBarcodeReader.setDecoderListeners(LegacyCamera2Scanner.this); new Thread(new Runnable() { @Override public void run() { openCamera(openCameraId); } }).start(); state = State.PREVIEW; preDecodeData = null; g_nMultiReadResultCount = 0; } else { return ERR_GENERAL_ERROR; } if (mUiHandler != null && workDecodeTimeOutSleep) { mUiHandler.removeMessages(BCRDR_MSG_TIMER_PREVIEW); mUiHandler.sendEmptyMessageDelayed(BCRDR_MSG_TIMER_PREVIEW, CONTINUOUS_MODE == 4 ? InactivityTimer__DURATION_CONTINUOUS_MODE_MS : InactivityTimer_DURATION_MS); workPreviewTime = System.currentTimeMillis(); } if (mBarcodeReader != null) { SparseArray<Property> mCacheProperties = mParametersHelper.getALLDecoderProperty(); for (int i = 0; i < mCacheProperties.size(); i++) { int keyForIndex = mCacheProperties.keyAt(i); setDecodeParameter(keyForIndex); } SparseArray<Property> mCacheStringProperties = mParametersHelper.getALLStringProperty(); for (int i = 0; i < mCacheStringProperties.size(); i++) { int keyForIndex = mCacheStringProperties.keyAt(i); setDecodeParameter(keyForIndex); } CONTINUOUS_MODE = mParametersHelper.getIntProperty(PropertyID.TRIGGERING_MODES); } return SUCCESS; } /** * Method used to disconnect from the camera engine and deinitialize the API. */ private void disconnectDecoderLibrary() { if (/*state != State.DONE && */mBarcodeReader != null) { mBarcodeReader.stopDecode(); mBarcodeReader.release(); } state = State.DONE; preDecodeData = null; g_nMultiReadResultCount = 0; mBarcodeReader = null; workPreviewTime = 0; } private void syncLicenseStrore(final boolean activate) { try { String buildDate = SystemPropertiesEx.get("ro.build.date.utc"); long defaultBuildTime = Long.parseLong(buildDate) * 1000; long currentTime = System.currentTimeMillis(); Log.d(TAG, " currentTime" + currentTime + " defaultBuildTime " + defaultBuildTime); if (currentTime < defaultBuildTime) { android.os.SystemClock.setCurrentTimeMillis(defaultBuildTime); } if (activate) { new Thread(new Runnable() { @Override public void run() { ActivationManager.syncLicenseStrore(activate); } }).start(); } } catch (Exception e) { e.printStackTrace(); } } public synchronized void openCamera(int cameraId) { state = State.DONE; if (mCameraManager != null && mCameraManager.isOpen()) { return; } try { Log.d(TAG, "openDriver = " + state); mCameraManager.openDriver(cameraId, new com.ubx.scanner.camera.CameraManager.StateCallback() { @Override public void onOpened(Camera camera) { synchronized (mDecodeLock) { if(camera !=null) { Point previewSize = mCameraManager.getPreviewResolution(); orientationDegress = mCameraManager.getDisplayOrientation(); MAX_FRAME_WIDTH = previewSize.x; MAX_FRAME_HEIGHT = previewSize.y; if(surfaceHolder != null) { try { mCameraManager.initializeCamera(surfaceHolder); mCameraManager.startPreview(); if(aimerIllumMode == 2 || aimerIllumMode == 3) { mCameraManager.setTorch(true); } } catch (IOException e) { e.printStackTrace(); } } } } } }); Log.d(TAG, "openDriver end ==================="); } catch (Exception e) { e.printStackTrace(); } } private void setDecodeParameter(int property) { int SDSProperty = -1; int val = 0; switch (property) { case PropertyID.TRIGGERING_MODES: CONTINUOUS_MODE = mParametersHelper.getIntProperty(PropertyID.TRIGGERING_MODES); break; case PropertyID.LOW_POWER_SLEEP_MODE: { int mode = mParametersHelper.getIntProperty(PropertyID.LOW_POWER_SLEEP_MODE); workDecodeTimeOutSleep = mode == 0 ? false : true; } break; case PropertyID.LOW_POWER_SLEEP_TIMEOUT: InactivityTimer_DURATION_MS = mParametersHelper.getIntProperty(PropertyID.LOW_POWER_SLEEP_TIMEOUT); if (InactivityTimer_DURATION_MS < 15 * 1000 || InactivityTimer_DURATION_MS > 60 * 1000) { InactivityTimer_DURATION_MS = 15 * 1000; } break; case PropertyID.TRANSMIT_CODE_ID: transmitCodeID = mParametersHelper.getIntProperty(PropertyID.TRANSMIT_CODE_ID); break; case PropertyID.LABEL_SEPARATOR_ENABLE: enableGroupSeperator = mParametersHelper.getIntProperty(PropertyID.LABEL_SEPARATOR_ENABLE); break; case PropertyID.LABEL_FORMAT_SEPARATOR_CHAR: groupSeperatorCharacters = mParametersHelper.getStringProperty(PropertyID.LABEL_FORMAT_SEPARATOR_CHAR); break; case PropertyID.LINEAR_CODE_TYPE_SECURITY_LEVEL: //delayDecode = property.get(keyForIndex); int seclevel = mParametersHelper.getIntProperty(PropertyID.LINEAR_CODE_TYPE_SECURITY_LEVEL); Log.d(TAG, "SECURITY_LEVEL= " + seclevel); if (mBarcodeReader != null) { if (seclevel > 1) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_C128_SECURITY, 1); mBarcodeReader.setParameter(SDSProperties.SD_PROP_UPC_SECURITY, 1); } else { mBarcodeReader.setParameter(SDSProperties.SD_PROP_C128_SECURITY, 0); mBarcodeReader.setParameter(SDSProperties.SD_PROP_UPC_SECURITY, 0); } } break; case PropertyID.MULTI_DECODE_MODE: case PropertyID.BAR_CODES_TO_READ: val = mParametersHelper.getIntProperty(PropertyID.MULTI_DECODE_MODE); Log.d(TAG, "setProperties property multiDecodeMode= " + val); multiDecodeMode = val; if (mBarcodeReader != null) { mBarcodeReader.setParameter(PropertyID.MULTI_DECODE_MODE, val); g_nMaxMultiReadCount = mParametersHelper.getIntProperty(PropertyID.BAR_CODES_TO_READ); if (g_nMaxMultiReadCount <= 0) { g_nMaxMultiReadCount = 1; } if (multiDecodeMode > 0) { if (g_nMaxMultiReadCount > 5) { //单张图片解码数量,增加搜索条码时间 mBarcodeReader.setParameter(PropertyID.LASER_ON_TIME, 1500); } mBarcodeReader.setMultiReadCount(g_nMaxMultiReadCount); } else { mBarcodeReader.setMultiReadCount(1); } } break; case PropertyID.DEC_PICKLIST_AIM_MODE: val = mParametersHelper.getIntProperty(PropertyID.DEC_PICKLIST_AIM_MODE); if (mBarcodeReader != null) { mBarcodeReader.setParameter(PropertyID.DEC_PICKLIST_AIM_MODE, val); } break; case PropertyID.DEC_PICKLIST_AIM_DELAY: val = mParametersHelper.getIntProperty(PropertyID.DEC_PICKLIST_AIM_DELAY); if (mBarcodeReader != null) { if (val >= 0 && val <= 4000) { val = val; } mBarcodeReader.setParameter(PropertyID.DEC_PICKLIST_AIM_DELAY, val); } break; case PropertyID.FULL_READ_MODE: val = mParametersHelper.getIntProperty(PropertyID.FULL_READ_MODE); Log.d(TAG, "setProperties property fullReadMode= " + val); fullReadMode = val; if (mBarcodeReader != null) mBarcodeReader.setParameter(PropertyID.FULL_READ_MODE, val); break; case PropertyID.DEC_2D_LIGHTS_MODE: aimerIllumMode = mParametersHelper.getIntProperty(PropertyID.DEC_2D_LIGHTS_MODE); if (mBarcodeReader != null) mBarcodeReader.setParameter(PropertyID.DEC_2D_LIGHTS_MODE, aimerIllumMode); break; case PropertyID.DEC_ILLUM_POWER_LEVEL: int level = mParametersHelper.getIntProperty(PropertyID.DEC_ILLUM_POWER_LEVEL); Log.d(TAG, "setProperties property level= " + level); //updateLightGrade(level); if (mBarcodeReader != null) mBarcodeReader.setParameter(PropertyID.DEC_ILLUM_POWER_LEVEL, level); break; case PropertyID.DEC_DECODE_DELAY: delayMillis = mParametersHelper.getIntProperty(PropertyID.DEC_DECODE_DELAY); Log.d(TAG, "setProperties property delay= " + delayMillis); if (mBarcodeReader != null) mBarcodeReader.setParameter(PropertyID.DEC_DECODE_DELAY, delayMillis); break; case PropertyID.DEC_DECODE_DEBUG_MODE: saveMode = mParametersHelper.getIntProperty(PropertyID.DEC_DECODE_DEBUG_MODE); Log.d(TAG, "setProperties property saveMode= " + saveMode); if (mBarcodeReader != null) { if (saveMode >= 0 && saveMode <= 4) { mBarcodeReader.setParameter(PropertyID.DEC_DECODE_DEBUG_MODE, saveMode); } else { mBarcodeReader.setParameter(PropertyID.DEC_DECODE_DEBUG_MODE, 0); } } break; case PropertyID.DEC_Multiple_Decode_INTERVAL: intervalTime = mParametersHelper.getIntProperty(PropertyID.DEC_Multiple_Decode_INTERVAL); if (mBarcodeReader != null) { mBarcodeReader.setParameter(PropertyID.DEC_Multiple_Decode_INTERVAL, intervalTime); } break; case PropertyID.DEC_MaxMultiRead_COUNT: cotinuousMultiReadCount = mParametersHelper.getIntProperty(PropertyID.DEC_MaxMultiRead_COUNT); if (cotinuousMultiReadCount <= 2 || cotinuousMultiReadCount > 10) { cotinuousMultiReadCount = 2; } Log.d(TAG, "setProperties property cotinuousMultiReadCount= " + cotinuousMultiReadCount); break; case PropertyID.DEC_Multiple_Decode_MODE: ignoreRepeatMode = mParametersHelper.getIntProperty(PropertyID.DEC_Multiple_Decode_MODE); break; case PropertyID.DEC_Multiple_Decode_TIMEOUT: try { waitRepeatTimeOut = mParametersHelper.getIntProperty(PropertyID.DEC_Multiple_Decode_TIMEOUT); if (waitRepeatTimeOut < 50) { waitRepeatTimeOut = 10 * 1000; } Log.d(TAG, "setProperties property g_waitRepeatTimeOut= " + waitRepeatTimeOut); } catch (Exception e) { // TODO Auto-generated catch block } break; case PropertyID.DEC_EachImageAttempt_TIME: try { int attemptLimitTime = mParametersHelper.getIntProperty(PropertyID.DEC_EachImageAttempt_TIME); if (attemptLimitTime <= 30) { attemptLimitTime = 125; } mBarcodeReader.setDecodeAttemptLimit(attemptLimitTime); } catch (Exception e) { // TODO Auto-generated catch block } break; case PropertyID.LOW_CONTRAST_IMPROVED_ALGORITHM: try { int ALGMode = mParametersHelper.getIntProperty(PropertyID.LOW_CONTRAST_IMPROVED_ALGORITHM); ALGMode = ALGMode != 1 ? 0 : ALGMode; mBarcodeReader.setParameter(PropertyID.LOW_CONTRAST_IMPROVED_ALGORITHM, ALGMode); } catch (Exception e) { // TODO Auto-generated catch block } break; case PropertyID.LOW_CONTRAST_IMPROVED: SDSProperty = SDSProperties.SD_PROP_MISC_LOW_CONTRAST_IMPROVEMENTS; val = mParametersHelper.getIntProperty(PropertyID.LOW_CONTRAST_IMPROVED); Log.d(TAG, "lowCONTRASTMode= " + val); if (val < 0 || val > 5) { val = 0; } if (val == 3) { val = 4; } else if (val == 4) { val = 3; } else if (val == 5) { val = 7; } break; case PropertyID.AZTEC_ENABLE: case PropertyID.AZTEC_INVERSE: /*The property value is a bit field defined as follows: b0: Enable normal video Aztec decoding //1 b1: Enable inverse video Aztec decoding //2 b2: Enable Compact Aztec Code decoding ////4 0010 b3: Enable Full-Size Aztec Code decoding*///8 SDSProperty = SDSProperties.SD_PROP_AZ_ENABLED; int aztec = mParametersHelper.getIntProperty(PropertyID.AZTEC_ENABLE);//1 int inverse = mParametersHelper.getIntProperty(PropertyID.AZTEC_INVERSE);//2 if (aztec == 1) val += 9; if (aztec == 1 && inverse == 1) { val = 2; } else if (aztec == 1 && inverse == 2) { val += 2; } if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_AZ_ENABLED, val); if (val != General.CONST_DISABLE) { val = mParametersHelper.getIntProperty(PropertyID.AZTEC_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } mBarcodeReader.setParameter(SDSProperties.SD_PROP_AZ_SYMBOL_SIZE, val); } SDSProperty = -1; } break; case PropertyID.AZTEC_SYMBOL_SIZE: SDSProperty = SDSProperties.SD_PROP_AZ_SYMBOL_SIZE; val = mParametersHelper.getIntProperty(PropertyID.AZTEC_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } break; case PropertyID.UPCA_ENABLE: SDSProperty = SDSProperties.UPCA_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.UPCA_ENABLE); break; case PropertyID.UPCA_SEND_CHECK: SDSProperty = SDSProperties.UPCA_SEND_CHECK; val = mParametersHelper.getIntProperty(PropertyID.UPCA_SEND_CHECK); break; case PropertyID.UPCA_SEND_SYS: SDSProperty = SDSProperties.UPCA_SEND_SYS; val = mParametersHelper.getIntProperty(PropertyID.UPCA_SEND_SYS); break; case PropertyID.UPCA_TO_EAN13: SDSProperty = SDSProperties.UPCA_TO_EAN13; val = mParametersHelper.getIntProperty(PropertyID.UPCA_TO_EAN13); break; case PropertyID.UPCE_ENABLE: SDSProperty = SDSProperties.UPCE_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.UPCE_ENABLE); break; case PropertyID.UPCE_TO_UPCA: SDSProperty = SDSProperties.UPCE_TO_UPCA; val = mParametersHelper.getIntProperty(PropertyID.UPCE_TO_UPCA); break; case PropertyID.UPCE_SEND_CHECK: SDSProperty = SDSProperties.UPCE_SEND_CHECK; val = mParametersHelper.getIntProperty(PropertyID.UPCE_SEND_CHECK); break; case PropertyID.UPCE_SEND_SYS: SDSProperty = SDSProperties.UPCE_SEND_SYS; val = mParametersHelper.getIntProperty(PropertyID.UPCE_SEND_SYS); break; case PropertyID.UPCE1_TO_UPCA: SDSProperty = SDSProperties.UPCE1_TO_UPCA; val = mParametersHelper.getIntProperty(PropertyID.UPCE1_TO_UPCA); break; case PropertyID.UPCE1_SEND_CHECK: SDSProperty = SDSProperties.UPCE1_SEND_CHECK; val = mParametersHelper.getIntProperty(PropertyID.UPCE1_SEND_CHECK); break; case PropertyID.UPCE1_SEND_SYS: SDSProperty = SDSProperties.UPCE1_SEND_SYS; val = mParametersHelper.getIntProperty(PropertyID.UPCE1_SEND_SYS); break; case PropertyID.UPCE1_ENABLE: SDSProperty = SDSProperties.UPCE1_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.UPCE1_ENABLE); break; case PropertyID.EAN13_BOOKLANDEAN: SDSProperty = SDSProperties.EAN13_BOOKLANDEAN; val = mParametersHelper.getIntProperty(PropertyID.EAN13_BOOKLANDEAN); break; case PropertyID.EAN13_BOOKLAND_FORMAT: SDSProperty = SDSProperties.EAN13_BOOKLAND_FORMAT; val = mParametersHelper.getIntProperty(PropertyID.EAN13_BOOKLAND_FORMAT); break; /*case PropertyID.EAN13_SEND_CHECK: SDSProperty = SDSProperties.EAN13_SEND_CHECK; val = mParametersHelper.getIntProperty(PropertyID.EAN13_SEND_CHECK); break;*/ case PropertyID.EAN13_ENABLE: SDSProperty = SDSProperties.EAN13_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.EAN13_ENABLE); break; case PropertyID.EAN8_ENABLE: SDSProperty = SDSProperties.EAN8_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.EAN8_ENABLE); break; case PropertyID.EAN8_TO_EAN13: SDSProperty = SDSProperties.EAN8_TO_EAN13; val = mParametersHelper.getIntProperty(PropertyID.EAN8_TO_EAN13); break; case PropertyID.EAN_EXT_ENABLE_2_5_DIGIT: SDSProperty = SDSProperties.SD_PROP_UPC_SUPPLEMENTALS; val = mParametersHelper.getIntProperty(PropertyID.EAN_EXT_ENABLE_2_5_DIGIT); break; case PropertyID.CODE39_ENABLE: SDSProperty = SDSProperties.SD_PROP_C39_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODE39_ENABLE); break; case PropertyID.CODE39_FULL_ASCII: //会默认发送check char SDSProperty = SDSProperties.SD_PROP_C39_FULL_ASCII; val = mParametersHelper.getIntProperty(PropertyID.CODE39_FULL_ASCII); break; case PropertyID.CODE39_ENABLE_CHECK: case PropertyID.CODE39_SEND_CHECK: { SDSProperty = SDSProperties.SD_PROP_C39_CHECKSUM; int enableCk = mParametersHelper.getIntProperty(PropertyID.CODE39_ENABLE_CHECK); int sendCk = mParametersHelper.getIntProperty(PropertyID.CODE39_SEND_CHECK); if (enableCk == 1 && sendCk == 1) { val = 1;//文档描述有误 } else if (enableCk == 1) { val = 2; } else { val = 0; } } break; case PropertyID.TRIOPTIC_ENABLE: SDSProperty = SDSProperties.SD_PROP_TRIOPTIC_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.TRIOPTIC_ENABLE); break; case PropertyID.CODE128_ENABLE: SDSProperty = SDSProperties.SD_PROP_C128_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODE128_ENABLE); break; case PropertyID.CODE_ISBT_128: SDSProperty = SDSProperties.ISBT128_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.CODE_ISBT_128); break; case PropertyID.CODE128_GS1_ENABLE: //gs1-128 SDSProperty = SDSProperties.GS1_128_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.CODE128_GS1_ENABLE); break; case PropertyID.I25_ENABLE: SDSProperty = SDSProperties.SD_PROP_I25_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.I25_ENABLE); break; case PropertyID.I25_ENABLE_CHECK: case PropertyID.I25_SEND_CHECK: { //0: Disable checksum checking. 1 //1: Enable checksum checking. 2 //2: Enable checksum checking and strip the checksum from the result string. 4 SDSProperty = SDSProperties.SD_PROP_I25_CHECKSUM; int enableCk = mParametersHelper.getIntProperty(PropertyID.I25_ENABLE_CHECK); int sendCk = mParametersHelper.getIntProperty(PropertyID.I25_SEND_CHECK); if (enableCk == 1 && sendCk == 1) { val = 1;//文档描述有误 } else if (enableCk == 1) { val = 2; } else { val = 0; } } break; case PropertyID.I25_TO_EAN13: SDSProperty = SDSProperties.I25_TO_EAN13; val = mParametersHelper.getIntProperty(PropertyID.I25_TO_EAN13); break; case PropertyID.CODABAR_ENABLE: SDSProperty = SDSProperties.SD_PROP_CB_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_ENABLE); break; case PropertyID.CODABAR_NOTIS: SDSProperty = SDSProperties.CODABAR_NOTIS_Editing; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_NOTIS); break; case PropertyID.CODABAR_CLSI: SDSProperty = SDSProperties.CODABAR_CLSI_Editing; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_CLSI); break; /*case PropertyID.CODABAR_ENABLE_CHECK: case PropertyID.CODABAR_SEND_CHECK: SDSProperty = SDSProperties.SD_PROP_CB_CHECKSUM; int enableCk = mParametersHelper.getIntProperty(PropertyID.CODABAR_ENABLE_CHECK); int sendCk = mParametersHelper.getIntProperty(PropertyID.CODABAR_SEND_CHECK); if(sendCk == 1) { val = 2; } else if(enableCk == 1) { val = 1; } else { val = 0; } break;*/ case PropertyID.GS1_14_ENABLE: case PropertyID.GS1_EXP_ENABLE: case PropertyID.GS1_LIMIT_ENABLE: SDSProperty = SDSProperties.SD_PROP_RSS_ENABLED; /*The property value is a bit field defined as follows: b0: Enable GS1 Databar Expanded decoding. 1 b1: Enable GS1 Databar Expanded Stacked decoding. 2 b2: Enable GS1 Databar Limited decoding. 4 b3: Enable GS1 Databar Omnidirectional and GS1 Databar truncated decoding. 8 b4: Enable GS1 Databar Stacked Omnidirectional and GS1 Databar Stacked decoding. 16*/ int gs1_trun = mParametersHelper.getIntProperty(PropertyID.GS1_14_ENABLE);//8//10 int exp_val = mParametersHelper.getIntProperty(PropertyID.GS1_EXP_ENABLE);//1 int limit_val = mParametersHelper.getIntProperty(PropertyID.GS1_LIMIT_ENABLE);//4 0010 if (gs1_trun == 1 && exp_val == 1 && limit_val == 1) { val = 0x1F;//31 } else { if (gs1_trun == 1) val += 24; if (exp_val == 1) val += 3; if (limit_val == 1) val += 4; } break; case PropertyID.GS1_14_TO_UPC_EAN: SDSProperty = SDSProperties.GS1_DATABAR14_TO_UPC; val = mParametersHelper.getIntProperty(PropertyID.GS1_14_TO_UPC_EAN); break; case PropertyID.MSI_ENABLE: case PropertyID.MSI_CHECK_2_MOD_11: case PropertyID.MSI_REQUIRE_2_CHECK: case PropertyID.MSI_SEND_CHECK: { /* default 0 文档有误 0: Disable checksum checking. 1: Enable a single mod 10 checksum check. 2: Enable a mod 11 and a mod 10 checksum check. 3: Enable two mod 10 checksum checks. 5: Enable a single mod 10 checksum check and strip the checksum 6: Enable a mod 11 and a mod 10 checksum check and strip the checksums 7: Enable two mod 10 checksum checks and strip the checksums */ if (mBarcodeReader != null) { int msiEnable = mParametersHelper.getIntProperty(PropertyID.MSI_ENABLE); mBarcodeReader.setParameter(SDSProperties.SD_PROP_MSIP_ENABLED, msiEnable); if (msiEnable == 1) { SDSProperty = SDSProperties.SD_PROP_MSIP_CHECKSUM; int ckDigitAlg = mParametersHelper.getIntProperty(PropertyID.MSI_CHECK_2_MOD_11); int ckDigitMode = mParametersHelper.getIntProperty(PropertyID.MSI_REQUIRE_2_CHECK); int sendCK = mParametersHelper.getIntProperty(PropertyID.MSI_SEND_CHECK); if (sendCK == 1) { if (ckDigitMode == 0) { //one check; val = 1; } else { //two check; if (ckDigitAlg == 0) { //MOD 10/MOD 11 val = 2; } else { //MOD 10/MOD 10 val = 3; } } } else { if (ckDigitMode == 0) { //one check; val = 5; } else { //two check; if (ckDigitAlg == 0) { //MOD 10/MOD 11 val = 6; } else { //MOD 10/MOD 10 val = 7; } } } //value is ignored if MSI Plessey is disable mBarcodeReader.setParameter(SDSProperties.SD_PROP_MSIP_CHECKSUM, val); } SDSProperty = -1; } } break; case PropertyID.PDF417_ENABLE: SDSProperty = SDSProperties.SD_PROP_PDF_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.PDF417_ENABLE); break; case PropertyID.MICROPDF417_ENABLE: SDSProperty = SDSProperties.SD_PROP_MICROPDF_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.MICROPDF417_ENABLE); break; case PropertyID.MAXICODE_ENABLE: SDSProperty = SDSProperties.SD_PROP_MC_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.MAXICODE_ENABLE); if (val == 1) { val = 0x3f; } if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_MC_ENABLED, val); if (val != General.CONST_DISABLE) { val = mParametersHelper.getIntProperty(PropertyID.MAXICODE_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } mBarcodeReader.setParameter(SDSProperties.SD_PROP_MC_SYMBOL_SIZE, val); } SDSProperty = -1; } break; case PropertyID.MAXICODE_SYMBOL_SIZE: SDSProperty = SDSProperties.SD_PROP_MC_SYMBOL_SIZE; val = mParametersHelper.getIntProperty(PropertyID.MAXICODE_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } break; case PropertyID.QRCODE_ENABLE: case PropertyID.QRCODE_INVERSE: case PropertyID.MICROQRCODE_ENABLE: SDSProperty = SDSProperties.SD_PROP_QR_ENABLED; int qr = mParametersHelper.getIntProperty(PropertyID.QRCODE_ENABLE);//1 int qrInverse = mParametersHelper.getIntProperty(PropertyID.QRCODE_INVERSE);//2/3 int micqr = mParametersHelper.getIntProperty(PropertyID.MICROQRCODE_ENABLE);//2/3 val = General.CONST_DISABLE; /*b0: Enable normal video QR Code decoding. qr + 1 b1: Enable inverse video QR Code decoding. qr +2 b2: Enable normal video Micro QR Code decoding. qr + 4 b3: Enable inverse video Micro QR Code decoding. qr + 8*/ if (qr == 1) { val = General.CONST_ENABLE; } if (qrInverse == 1 && qr == 1) { val = 2; } else if (qrInverse == 2 && qr == 1) { val += 2; } if (micqr == 1) { val += 4; if (qrInverse > 0) { val += 8; } } if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_QR_ENABLED, val); if (val != General.CONST_DISABLE) { val = mParametersHelper.getIntProperty(PropertyID.QRCODE_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } mBarcodeReader.setParameter(SDSProperties.SD_PROP_QR_SYMBOL_SIZE, val); } SDSProperty = -1; } break; case PropertyID.QRCODE_SYMBOL_SIZE: SDSProperty = SDSProperties.SD_PROP_QR_SYMBOL_SIZE; val = mParametersHelper.getIntProperty(PropertyID.QRCODE_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } break; case PropertyID.DATAMATRIX_SYMBOL_SIZE: SDSProperty = SDSProperties.SD_PROP_DM_SYMBOL_SIZE; val = mParametersHelper.getIntProperty(PropertyID.DATAMATRIX_SYMBOL_SIZE); if(val < 0 || val > 2) { val = 1; } break; case PropertyID.DATAMATRIX_ENABLE: case PropertyID.DATAMATRIX_INVERSE: SDSProperty = SDSProperties.SD_PROP_DM_ENABLED; int dm = mParametersHelper.getIntProperty(PropertyID.DATAMATRIX_ENABLE);//1 int dmInverse = mParametersHelper.getIntProperty(PropertyID.DATAMATRIX_INVERSE);//2/3 //b0: Enable normal video Data Matrix decoding //b1: Enable inverse video Data Matrix decoding if (dm == 1) { val = 1; } if (dm == 1 && dmInverse == 1) { val = 2; } else if (dm == 1 && dmInverse == 2) { val += 2; } if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_DM_ENABLED, val); if (val != General.CONST_DISABLE) { val = mParametersHelper.getIntProperty(PropertyID.DATAMATRIX_SYMBOL_SIZE); if(val < 0 || val > 2) { val = 1; } mBarcodeReader.setParameter(SDSProperties.SD_PROP_DM_SYMBOL_SIZE, val); } SDSProperty = -1; } break; case PropertyID.DPM_DECODE_MODE: SDSProperty = SDSProperties.SD_PROP_DPM_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.DPM_DECODE_MODE); break; case PropertyID.HANXIN_ENABLE: SDSProperty = SDSProperties.SD_PROP_HX_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.HANXIN_ENABLE); break; case PropertyID.M25_ENABLE: SDSProperty = SDSProperties.SD_PROP_M25_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.M25_ENABLE); break; case PropertyID.D25_ENABLE: //Straight 2 of 5 (with 2 bar start/stop codes) is also known as: Standard 2 of 5, IATA 2 of 5, and Airline 2 of 5 SDSProperty = SDSProperties.SD_PROP_S25_2SS_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.D25_ENABLE); try { if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperty, val); } } catch (Exception e) { // TODO Auto-generated catch block } //Straight 2 of 5 (with 3 bar start/stop codes) is also known as: Industrial 2 of 5, Code 2 of 5, and Discrete 2 of 5 SDSProperty = SDSProperties.SD_PROP_S25_3SS_ENABLED; break; case PropertyID.CODE11_ENABLE: SDSProperty = SDSProperties.SD_PROP_C11_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODE11_ENABLE); break; case PropertyID.CODE11_ENABLE_CHECK: case PropertyID.CODE11_SEND_CHECK: SDSProperty = SDSProperties.SD_PROP_C11_CHECKSUM; //0 two check digits;1 one check; 2 two check and stripped form result data; 3 one check and stripped form result data /*boolean enableCK = mParametersHelper.getIntProperty(PropertyID.CODE11_ENABLE_CHECK) == 1; if(enableCK) { val = mParametersHelper.getIntProperty(PropertyID.CODE11_SEND_CHECK); } else { val = 4; }*/ val = mParametersHelper.getIntProperty(PropertyID.CODE11_SEND_CHECK); break; case PropertyID.CODE93_ENABLE: SDSProperty = SDSProperties.SD_PROP_C93_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODE93_ENABLE); break; case PropertyID.DOTCODE_ENABLE: SDSProperty = SDSProperties.SD_PROP_DOTCODE_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.DOTCODE_ENABLE); break; case PropertyID.DEC_OCR_MODE: case PropertyID.DEC_OCR_TEMPLATE: case PropertyID.DEC_OCR_USER_TEMPLATE: { setOcrSettings(); break; } case PropertyID.C25_ENABLE: SDSProperty = SDSProperties.SD_PROP_HK25_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.C25_ENABLE); break; case PropertyID.US_POSTNET_ENABLE: case PropertyID.US_PLANET_ENABLE: case PropertyID.US_POSTAL_SEND_CHECK: case PropertyID.USPS_4STATE_ENABLE: case PropertyID.UPU_FICS_ENABLE: case PropertyID.ROYAL_MAIL_ENABLE: case PropertyID.ROYAL_MAIL_SEND_CHECK: case PropertyID.AUSTRALIAN_POST_ENABLE: case PropertyID.KIX_CODE_ENABLE: case PropertyID.JAPANESE_POST_ENABLE: SDSProperty = -1; disablePostalSymbologies(); break; case PropertyID.KOREA_POST_ENABLE: SDSProperty = SDSProperties.SD_PROP_KP_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.KOREA_POST_ENABLE); break; case PropertyID.CODABAR_LENGTH1: SDSProperty = SDSProperties.CODABAR_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_LENGTH1); break; case PropertyID.CODABAR_LENGTH2: SDSProperty = SDSProperties.CODABAR_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_LENGTH2); break; case PropertyID.CODE11_LENGTH1: SDSProperty = SDSProperties.CODE11_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODE11_LENGTH1); break; case PropertyID.CODE11_LENGTH2: SDSProperty = SDSProperties.CODE11_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODE11_LENGTH2); break; case PropertyID.CODE39_LENGTH1: SDSProperty = SDSProperties.CODE39_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODE39_LENGTH1); break; case PropertyID.CODE39_LENGTH2: SDSProperty = SDSProperties.CODE39_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODE39_LENGTH2); break; case PropertyID.CODE93_LENGTH1: SDSProperty = SDSProperties.CODE93_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODE93_LENGTH1); break; case PropertyID.CODE93_LENGTH2: SDSProperty = SDSProperties.CODE93_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODE93_LENGTH2); break; case PropertyID.I25_LENGTH1: SDSProperty = SDSProperties.I25_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.I25_LENGTH1); break; case PropertyID.I25_LENGTH2: SDSProperty = SDSProperties.I25_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.I25_LENGTH2); break; case PropertyID.CODE128_LENGTH1: SDSProperty = SDSProperties.CODE128_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODE128_LENGTH1); break; case PropertyID.CODE128_LENGTH2: SDSProperty = SDSProperties.CODE128_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODE128_LENGTH2); break; case PropertyID.D25_LENGTH1: SDSProperty = SDSProperties.D25_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.D25_LENGTH1); break; case PropertyID.D25_LENGTH2: SDSProperty = SDSProperties.D25_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.D25_LENGTH2); break; case PropertyID.MSI_LENGTH1: SDSProperty = SDSProperties.MSI_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.MSI_LENGTH1); break; case PropertyID.MSI_LENGTH2: SDSProperty = SDSProperties.MSI_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.MSI_LENGTH2); break; case PropertyID.GS1_EXP_LENGTH1: SDSProperty = SDSProperties.GS1_EXP_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.GS1_EXP_LENGTH1); break; case PropertyID.GS1_EXP_LENGTH2: SDSProperty = SDSProperties.GS1_EXP_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.GS1_EXP_LENGTH2); break; case PropertyID.DEC_2D_CENTERING_ENABLE: case PropertyID.DEC_2D_CENTERING_MODE: case PropertyID.DEC_2D_WINDOW_UPPER_LX: case PropertyID.DEC_2D_WINDOW_UPPER_LY: case PropertyID.DEC_2D_WINDOW_LOWER_RX: case PropertyID.DEC_2D_WINDOW_LOWER_RY: setDecodeWindowSettings(); break; default: SDSProperty = -1; break; } if (SDSProperty != -1 && mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperty, val); SDSProperty = -1; } } /** * Sets the Decoder settings based on user preferences * (412, 316), (420,324),Field of View,(一定要选择这个) */ private void setDecodeWindowSettings() { if (mBarcodeReader != null) { // Windowing boolean enable_windowing = mParametersHelper.getIntProperty(PropertyID.DEC_2D_CENTERING_ENABLE) == 1; try { if (enable_windowing) { int mode = mParametersHelper.getIntProperty(PropertyID.DEC_2D_CENTERING_MODE); mode = mode >= 0 && mode <= 2 ? mode : 0; mBarcodeReader.setParameter(PropertyID.DEC_2D_CENTERING_MODE, mode); if (mode == DecodeWindowMode.DECODE_WINDOW_MODE_FIELD_OF_VIEW.toInt()) { int UpperLeftX = mParametersHelper.getIntProperty(PropertyID.DEC_2D_WINDOW_UPPER_LX); int UpperLeftY = mParametersHelper.getIntProperty(PropertyID.DEC_2D_WINDOW_UPPER_LY); int LowerRightX = mParametersHelper.getIntProperty(PropertyID.DEC_2D_WINDOW_LOWER_RX); int LowerRightY = mParametersHelper.getIntProperty(PropertyID.DEC_2D_WINDOW_LOWER_RY); if (UpperLeftX < 0) UpperLeftX = 0; if (UpperLeftY < 0) UpperLeftY = 0; if (LowerRightX < 20) LowerRightX = 1280; if (LowerRightY < 20) LowerRightY = 720; Log.d(TAG, "nMode = " + mode + " upX " + UpperLeftX + " upY " + UpperLeftY + " lwX " + LowerRightX + " lwY " + LowerRightY); // enable the mode //镜像处理坐标转换 int X = MAX_FRAME_WIDTH - UpperLeftX; int Y = MAX_FRAME_HEIGHT - UpperLeftY; UpperLeftX = MAX_FRAME_WIDTH - LowerRightX; UpperLeftY = MAX_FRAME_HEIGHT - LowerRightY; LowerRightX = X; LowerRightY = Y; mBarcodeReader.setParameter(SDSProperties.UpperLeftX, UpperLeftX); mBarcodeReader.setParameter(SDSProperties.UpperLeftY, UpperLeftY); mBarcodeReader.setParameter(SDSProperties.LowerRightX, LowerRightX); mBarcodeReader.setParameter(SDSProperties.LowerRightY, LowerRightY); } } else { // disable windowing mBarcodeReader.setParameter(PropertyID.DEC_2D_CENTERING_MODE, DecodeWindowMode.DECODE_WINDOW_MODE_OFF.toInt()); } } catch (Exception e) { } } } /** * Sets the OCR settings based on user preferences * The property value is a bit field defined as follows: * b0: User * b1: Passport * b2: ISBN * b3: Price Field * b4: MICR E-13B * enable theUser template along with ISBN * SD_Set(Handle, SD_PROP_OCR_ACTIVE_TEMPLATES, (void *)(1+4)); */ private void setOcrSettings() { Log.d(TAG, "SetOcrSettings++"); if (mBarcodeReader != null) { int ocr_mode = 0; int ocr_template = 0; byte[] ocr_user_defined_template; ocr_mode = mParametersHelper.getIntProperty(PropertyID.DEC_OCR_MODE); ocr_template = mParametersHelper.getIntProperty(PropertyID.DEC_OCR_TEMPLATE); String userDefinedTemplate = mParametersHelper.getStringProperty(PropertyID.DEC_OCR_USER_TEMPLATE); Log.d(TAG, "SetOcrSettings++ ocr_mode " + ocr_mode); if (ocr_mode > 0) { mBarcodeReader.setOCRMode(ocr_mode); if (ocr_template == 0) ocr_template = OCRActiveTemplate.USER; else if (ocr_template == 1) ocr_template = OCRActiveTemplate.PASSPORT; else if (ocr_template == 2) ocr_template = OCRActiveTemplate.ISBN; else if (ocr_template == 3) ocr_template = OCRActiveTemplate.PRICE_FIELD; else if (ocr_template == 4) ocr_template = OCRActiveTemplate.MICR;//MICRE13B Log.d(TAG, "SetOcrSettings++ ocr_template " + ocr_template); mBarcodeReader.setOCRTemplates(ocr_template); //default Template "13777777770" to bytearrary 01 03 07 07 07 07 00 //1,2,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,8,8,0 if (ocr_template == OCRActiveTemplate.USER) { try { //userDefinedTemplate = "1.3777778E10"; if (userDefinedTemplate != null && !userDefinedTemplate.equals("")) { ocr_user_defined_template = userDefinedTemplate.getBytes(); //char[] templateChar = userDefinedTemplate.toCharArray(); for (int i = 0; i < ocr_user_defined_template.length; i++) { if (ocr_user_defined_template[i] >= 48) ocr_user_defined_template[i] = (byte) (ocr_user_defined_template[i] - 48); } mBarcodeReader.setOCRUserTemplate(ocr_user_defined_template); } } catch (Exception e) { } } } else { mBarcodeReader.setParameter(SDSProperties.SD_PROP_OCR_ENABLED, 0); } Log.d(TAG, "SetOcrSettings--"); } } private void disablePostalSymbologies() { if (mBarcodeReader != null) { int SDSProperty = SDSProperties.SD_PROP_POSTAL_ENABLED; int PLANET = mParametersHelper.getIntProperty(PropertyID.US_PLANET_ENABLE); int POSTNET = mParametersHelper.getIntProperty(PropertyID.US_POSTNET_ENABLE); int UPS4 = mParametersHelper.getIntProperty(PropertyID.USPS_4STATE_ENABLE); int UPU_FICS = mParametersHelper.getIntProperty(PropertyID.UPU_FICS_ENABLE); int ROYAL = mParametersHelper.getIntProperty(PropertyID.ROYAL_MAIL_ENABLE); int AUSTRALIAN = mParametersHelper.getIntProperty(PropertyID.AUSTRALIAN_POST_ENABLE); int KIX = mParametersHelper.getIntProperty(PropertyID.AUSTRALIAN_POST_ENABLE); int JAPANESE = mParametersHelper.getIntProperty(PropertyID.AUSTRALIAN_POST_ENABLE); if (PLANET == 0 && POSTNET == 0 && UPS4 == 0 && UPU_FICS == 0 && AUSTRALIAN == 0 && KIX == 0 && ROYAL == 0 && JAPANESE == 0) { mBarcodeReader.setParameter(SDSProperty, 0); } else { if (PLANET == 1 && POSTNET == 1) { mBarcodeReader.setParameter(SDSProperty, 0x2081); } else if (PLANET == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_PL); } else if (POSTNET == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_PN); } if (UPS4 == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_UPU); } if (UPU_FICS == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_USPS4CB); } if (ROYAL == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_RM); } if (AUSTRALIAN == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_AP); } if (KIX == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_RM + 1); } if (JAPANESE == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_JP); } //暂时开启这几种postal mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_PL + General.SD_CONST_PN + General.SD_CONST_USPS4CB + General.SD_CONST_UPU + 1); } } } boolean compareBytes(byte[] data, int length) { int i = 0; if (length != preDecodeData.length) return false; for (i = 0; i < length && i < preDecodeData.length; i++) { if (preDecodeData[i] != data[i]) { return false; } } return true; } boolean compareBytes(byte[] preData, byte[] data, int length) { int i = 0; if (length != preData.length) return false; for (i = 0; i < length && i < preData.length; i++) { if (preData[i] != data[i]) { return false; } } return true; } private void sendBarcodeAndBitmapBroadcast(String barcode, byte[] m_frameData, int[] bounds) { try { Intent intent = new Intent(BROADCAST_IMAGE_ACTION); if (barcode != null) { //Log.d(TAG , "onDecodeComplete , --------- barcode:" + barcode); intent.putExtra("barcode", barcode); } if (bounds != null) { //镜像坐标转换 for (int i = 0; i < bounds.length; i++) { if (i % 2 == 0) { bounds[i] = MAX_FRAME_WIDTH - bounds[i]; } else { bounds[i] = MAX_FRAME_HEIGHT - bounds[i]; } } if (rotateImage == 90) { /*旋转-90*/ for (int i = 0; i < bounds.length; ) { int temp = bounds[i]; bounds[i] = MAX_FRAME_HEIGHT - bounds[i + 1]; bounds[i + 1] = temp; i = i + 2; } } intent.putExtra("bounds", bounds); intent.putExtra("x1", bounds[0]); intent.putExtra("y1", bounds[1]); intent.putExtra("x2", bounds[2]); intent.putExtra("y2", bounds[3]); intent.putExtra("x3", bounds[4]); intent.putExtra("y3", bounds[5]); intent.putExtra("x4", bounds[6]); intent.putExtra("y4", bounds[7]); intent.putExtra("cx", bounds[8]); intent.putExtra("cy", bounds[9]); } if (m_frameData != null) { if (m_frameData != null && m_frameData.length > 0) { Bitmap modBm = createBitmap(m_frameData, rotateImage); if (modBm != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); modBm.compress(Bitmap.CompressFormat.JPEG, 50, baos); modBm.recycle(); modBm = null; intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, baos.toByteArray()); } else { intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, new byte[1]); } } else { intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, new byte[1]); } } mContextWrapper.sendBroadcastAsUser(intent, UserHandleEx.ALL); } catch (Exception e) { e.printStackTrace(); } } private Bitmap createBitmap(byte[] m_frameData, int rotate) { try { if (headOfByte == null) { InputStream localInputStream = new FileInputStream(new File("etc/bh_1280_720.bin")); ; //InputStream localInputStream =mContextWrapper.getResources().openRawResource(R.raw.bh_1280_720); headOfByte = Utils.readFileHead(localInputStream); if (headOfByte != null) { bitmapByte = new byte[headOfByte.length + MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT]; } } if (headOfByte != null) { if (m_frameData != null && m_frameData.length > 0 && bitmapByte != null) { System.arraycopy(headOfByte, 0, bitmapByte, 0, headOfByte.length); System.arraycopy(m_frameData, 0, bitmapByte, headOfByte.length, MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT); Bitmap yuvbitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(bitmapByte)).copy(Bitmap.Config.RGB_565, true); if (yuvbitmap != null) { Log.d(TAG, "createBitmap "); Bitmap modBm = Bitmap.createBitmap(yuvbitmap.getWidth(), yuvbitmap.getHeight(), yuvbitmap.getConfig()); Matrix matrix = new Matrix(); Canvas canvas = new Canvas(modBm); Paint paint = new Paint(); //绘制矩阵 Matrix主要用于对平面进行平移(Translate),缩放(Scale),旋转(Rotate)以及斜切(Skew)操作。 //matrix.setRotate(90,modBm.getWidth()/2,modBm.getHeight()/2); // matrix.setTranslate(20,20); //镜子效果: matrix.setScale(-1, 1);//翻转 matrix.postTranslate(yuvbitmap.getWidth(), 0); canvas.drawBitmap(yuvbitmap, matrix, paint); yuvbitmap.recycle(); yuvbitmap = null; /*旋转-90*/ if (rotate > 0 && rotate <= 270) { matrix.reset(); matrix.setRotate(rotate); // 重新绘制Bitmap Bitmap rotateBm = Bitmap.createBitmap(modBm, 0, 0, modBm.getWidth(), modBm.getHeight(), matrix, true); modBm.recycle(); modBm = null; return rotateBm; } else { return modBm; } } } } } catch (Exception e) { e.printStackTrace(); } return null; } private void receivedMultipleData() { if (transmitCodeID > 0 || enableGroupSeperator == 1) { ArrayList<DecodeData> composeDecodeDataArray = new ArrayList<DecodeData>(); for (DecodeData firstResult : decodeDataArray) { byte[] byteArraryData = firstResult.getBarcodeDataBytes(); byte[] barcodeDataBytes = composeAISeparator(firstResult.getAIMCodeLetter(), firstResult.getAIMModifier(), firstResult.getCodeId(), byteArraryData, firstResult.getBarcodeDataLength()); if (barcodeDataBytes != null) { firstResult.setBarcodeDataBytes(barcodeDataBytes); firstResult.setBarcodeDataLength(barcodeDataBytes.length); } composeDecodeDataArray.add(firstResult); } decodeDataArray.clear(); if (mOnScannedListener != null) { mOnScannedListener.receivedMultipleDecodedData(composeDecodeDataArray); } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedMultipleDecodedData(decodeDataArray); } } } private byte[] composeAISeparator(byte aimID, byte aimModifier, int codeID, byte[] barcodeByteData, int dataLength) { try { byte[] aiSeparatorData = null; if (enableGroupSeperator == 1) { byte[] sepBytes = null; if (!TextUtils.isEmpty(groupSeperatorCharacters)) { sepBytes = groupSeperatorCharacters.getBytes(); } aiSeparatorData = GS1DataParser.addAISeparator(aimID, aimModifier, barcodeByteData, sepBytes); } if (transmitCodeID > 0) { try { byte[] byteArraryData; if (transmitCodeID == 1) { if (aiSeparatorData != null && aiSeparatorData.length > 0) { byteArraryData = new byte[3 + aiSeparatorData.length]; System.arraycopy(aiSeparatorData, 0, byteArraryData, 3, aiSeparatorData.length); } else { byteArraryData = new byte[3 + barcodeByteData.length]; System.arraycopy(barcodeByteData, 0, byteArraryData, 3, barcodeByteData.length); } byteArraryData[0] = ']'; byteArraryData[1] = aimID; byteArraryData[2] = aimModifier; } else { if (aiSeparatorData != null && aiSeparatorData.length > 0) { byteArraryData = new byte[1 + aiSeparatorData.length]; System.arraycopy(aiSeparatorData, 0, byteArraryData, 1, aiSeparatorData.length); } else { byteArraryData = new byte[1 + barcodeByteData.length]; System.arraycopy(barcodeByteData, 0, byteArraryData, 1, barcodeByteData.length); } byteArraryData[0] = (byte)codeID; } return byteArraryData; } catch (Exception e) { e.printStackTrace(); } } else { return aiSeparatorData; } } catch (Exception e) { e.printStackTrace(); } return null; } private void quitHandlerThreadLocked() { try { if (mDecodeHandlerThread != null) { if (mDecodeHandler != null) { Message m = Message.obtain(mDecodeHandler,MESSAGE_RELEASE_QUIT); mDecodeHandler.sendMessage(m); } mDecodeHandlerThread.getLooper().quitSafely(); try { // Wait for the thread to quit. An ANR waiting to happen. mDecodeHandlerThread.join(); } catch (InterruptedException ex) { ex.printStackTrace(); } mDecodeHandler = null; mDecodeHandlerThread.interrupt(); mDecodeHandlerThread = null; } } catch (Exception e) { Log.e(TAG, "quitHandlerThread " + e.getMessage()); e.printStackTrace(); } mDecodeHandlerThread = null; mDecodeHandler = null; } /** * 私有构造函数,禁止外部直接实例化。 */ private LegacyCamera2Scanner(Context context) throws ReaderException { super(context); mContext = context.getApplicationContext(); // 建议使用应用级Context mContextWrapper = context; mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mUiHandler = new LegacyCamera2Scanner.CaptureHandler(mContextWrapper.getMainLooper()); // 这里不主动 open(),由外部决定什么时候打开/初始化 // 仅做一些基础的初始化(如后台线程) startBackgroundThread(); mDecodeHandlerThread = new DecodeHandlerThread("Overlay-scan"); mDecodeHandlerThread.startThread(); open(-1); // 初始化UI布局:预览布局 & 悬浮按钮 createCameraPreviewLayout(); createFloatingButton(); } public LegacyCamera2Scanner(Context context, int type) { super(context); mScannerType = type; mContextWrapper = context; mContext = context; mUiHandler = new CaptureHandler(mContextWrapper.getMainLooper()); syncLicenseStrore(false); IntentFilter filter = new IntentFilter(); filter.addAction(CAPTURE_IMAGE_ACTION); filter.addAction(ENABLE_BROADCAST_IMAGE_ACTION); mContextWrapper.registerReceiver(mReceiver, filter); mDecodeHandlerThread = new DecodeHandlerThread("Overlay-scan"); mDecodeHandlerThread.startThread(); mCameraManager = com.ubx.scanner.camera.CameraManager.sharedObject(mContextWrapper); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); startBackgroundThread(); mDecodeHandlerThread = new DecodeHandlerThread("Overlay-scan"); mDecodeHandlerThread.startThread(); open(-1); // 初始化UI布局:预览布局 & 悬浮按钮 createCameraPreviewLayout(); createFloatingButton(); } /** * 单例获取方法,需线程安全。 */ public static synchronized LegacyCamera2Scanner getInstance(Context context) throws ReaderException { if (sInstance == null) { sInstance = new LegacyCamera2Scanner(context); } return sInstance; } /** * 创建相机预览布局(包含 TextureView)。 */ private void createCameraPreviewLayout() { mCameraPreviewLayout = LayoutInflater.from(mContext).inflate(R.layout.scanner_layout, null); mPreviewView = mCameraPreviewLayout.findViewById(R.id.previewView); mBlackView = mCameraPreviewLayout.findViewById(R.id.blackMaskView); // 若布局中不是TextureView,动态替换 if (!(mPreviewView instanceof TextureView)) { mPreviewView = new TextureView(mContext); ViewGroup viewGroup = (ViewGroup) mCameraPreviewLayout; viewGroup.removeAllViews(); viewGroup.addView(mPreviewView); } // 设置布局参数 WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); // 默认先设置为不可见(根据业务需求可自行调整) new Handler(Looper.getMainLooper()).post(() -> { mBlackView.setVisibility(View.VISIBLE); mCameraPreviewLayout.setVisibility(View.INVISIBLE); }); mWindowManager.addView(mCameraPreviewLayout, params); // 添加TextureView监听 mPreviewView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mSurfaceTexture = surface; mSurfaceWidth = width; mSurfaceHeight = height; Log.d(TAG, "onSurfaceTextureAvailable: " + width + "x" + height); setupCamera(width, height); openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { Log.d(TAG, "onSurfaceTextureSizeChanged: " + width + "x" + height); configureTransform(width, height); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }); // 若TextureView此时已经可用 if (mPreviewView.isAvailable()) { mSurfaceTexture = mPreviewView.getSurfaceTexture(); mSurfaceWidth = mPreviewView.getWidth(); mSurfaceHeight = mPreviewView.getHeight(); if (mIsOpened && !mIsCameraInitialized) { initCamera(); } } } /** * 创建悬浮按钮示例。 */ private void createFloatingButton() { mFloatingButtonView = LayoutInflater.from(mContext).inflate(R.layout.floating_scan_button, null); // 配置布局参数 mFloatingButtonParams = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); // 设置初始位置(示例) mFloatingButtonParams.gravity = Gravity.RIGHT | Gravity.TOP; mFloatingButtonParams.x = 0; mFloatingButtonParams.y = 200; // 触摸事件,用于拖动和触发解码 mFloatingButtonView.setOnTouchListener((v, event) -> { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 记录初始位置 mInitialX = mFloatingButtonParams.x; mInitialY = mFloatingButtonParams.y; mInitialTouchX = event.getRawX(); mInitialTouchY = event.getRawY(); // 短按触发开始解码 startDecode(500); // 延迟判断是否拖动 v.postDelayed(() -> { mIsDragging = true; // 放大按钮 mFloatingButtonView.setScaleX(1.2f); mFloatingButtonView.setScaleY(1.2f); }, 300); return true; case MotionEvent.ACTION_MOVE: if (mIsDragging) { float deltaX = event.getRawX() - mInitialTouchX; float deltaY = event.getRawY() - mInitialTouchY; // RIGHT|TOP 方向的修正 int newX = mInitialX - (int) deltaX; int newY = mInitialY + (int) deltaY; mFloatingButtonParams.x = newX; mFloatingButtonParams.y = newY; mWindowManager.updateViewLayout(mFloatingButtonView, mFloatingButtonParams); } return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: v.removeCallbacks(null); mIsDragging = false; // 恢复按钮缩放 mFloatingButtonView.setScaleX(1.0f); mFloatingButtonView.setScaleY(1.0f); // 停止解码 stopDecode(); return true; } return false; }); mWindowManager.addView(mFloatingButtonView, mFloatingButtonParams); } /** * 打开扫描器接口 */ @Override public int open(int id) { Log.d(TAG, "open: 打开扫描器 ID=" + id); synchronized (mUiHandler) { if (mBarcodeReader != null && state != State.DONE) { Log.d(TAG, "open State=" + state); return BUSY; } /*mUiHandler.removeMessages(BCRDR_MSG_INIT); Message m = Message.obtain(mUiHandler, BCRDR_MSG_INIT); mUiHandler.sendMessage(m);*/ return connectDecoderLibrary(); } } @Override public boolean isOpened() { return (mBarcodeReader != null && state != State.DONE); } @Override public int close() { Log.d(TAG, "close: 关闭扫描器"); if (!mIsOpened) { return 0; } // 停止解码 stopDecode(); // 关闭相机 closeCamera(); // 停止后台线程(可视情况决定是否要常驻) stopBackgroundThread(); synchronized (mUiHandler) { mUiHandler.post(mDismissRunnable); //mUiHandler.removeMessages(BCRDR_MSG_RELEASE); mUiHandler.removeMessages(BCRDR_MSG_TIMER_PREVIEW); //Message m = Message.obtain(mUiHandler, BCRDR_MSG_RELEASE); //mUiHandler.sendMessage(m); disconnectDecoderLibrary(); } mUiHandler.removeMessages(BCRDR_MSG_FRAME_ERROR); mUiHandler.removeMessages(BCRDR_MSG_RELEASE); if (mCameraManager != null) { mCameraManager.stopDecoding(); mCameraManager.closeDriver(); } state = State.DONE; surfaceHolder = null; g_nMultiReadResultCount = 0; decodeDataArray.clear(); return SUCCESS; } // ----------------- 解码控制 ------------------- @Override public int startDecode(int timeout) { Log.d(TAG, "startDecode: 开始解码,超时=" + timeout); synchronized (mUiHandler) { // 标记处于解码中 mIsDecoding = true; // 显示预览布局 if (mIsCameraInitialized) { // 如果相机已初始化,只需开始预览 new Handler(Looper.getMainLooper()).post(() -> { mCameraPreviewLayout.setVisibility(View.VISIBLE); }); startPreview(); Log.d(TAG, "Decoding started (preview only)"); } else { // 如果相机未初始化,需要初始化相机 startCamera(); Log.d(TAG, "Decoding started (full camera initialization)"); } if (mBarcodeReader != null) { if (state != State.DECODING) { //开机后存在无法激活 /*if (activateDecoder == false && activateDecoderTimes > 0) { activateDecoderTimes = activateDecoderTimes - 1; if (ActivationManager.checkActivateStroreFile()) { try { disconnectDecoderLibrary(); Thread.sleep(50); connectDecoderLibrary(openCameraId); } catch (Exception e) { e.printStackTrace(); } } }*/ decodeDataArray.clear(); g_nMultiReadResultCount = 0; preDecodeData = null; decodeStartTime = System.currentTimeMillis(); /*if (CONTINUOUS_MODE == Triggering.CONTINUOUS.toInt()) { mBarcodeReader.startHandsFreeDecode(1); } else { mBarcodeReader.startDecode(timeout); }*/ /*if(mCameraDevice == null) { openCamera(); }*/
// mUiHandler.post(mShowRunnable);
state = State.DECODING;
// mCameraManager.setDecodeHandler(mDecodeHandler);
if(CONTINUOUS_MODE == 2) {
mUiHandler.removeMessages(BCRDR_MSG_DECODE_TIMEOUT);
mUiHandler.sendEmptyMessageDelayed(BCRDR_MSG_DECODE_TIMEOUT, timeout > 1000 ? timeout: 1500);
}
if (mUiHandler != null && workDecodeTimeOutSleep && workPreviewTime == 0) {
mUiHandler.removeMessages(BCRDR_MSG_TIMER_PREVIEW);
mUiHandler.sendEmptyMessageDelayed(BCRDR_MSG_TIMER_PREVIEW, CONTINUOUS_MODE == 4 ? InactivityTimer__DURATION_CONTINUOUS_MODE_MS : InactivityTimer_DURATION_MS);
workPreviewTime = System.currentTimeMillis();
}
return SUCCESS;
}
return BUSY;
} else {
state = State.DONE;
Log.d(TAG, "startDecode mBarcodeReader");
return NONE;
}
}
}
text@Override public int stopDecode() { Log.d(TAG, "stopDecode: 停止解码"); mIsDecoding = false; // 停止预览 stopPreview(); // 关闭摄像头 closeCamera(); // 隐藏预览布局 if (mCameraPreviewLayout != null) { new Handler(Looper.getMainLooper()).post(() -> { /*Canvas canvas = mPreviewView.lockCanvas(); if (canvas != null) { try { // 覆盖整个区域 canvas.drawColor(Color.BLACK); } finally { mPreviewView.unlockCanvasAndPost(canvas); } }*/ Log.d(TAG, "stopDecode: state: " + state); mCameraPreviewLayout.setVisibility(View.INVISIBLE); mBlackView.setVisibility(View.VISIBLE); }); } synchronized (mUiHandler) { Log.d(TAG, "stopDecode state=" + state); if (mBarcodeReader != null) { if (state == State.DECODING) { mBarcodeReader.stopDecode(); } /*if(mCameraManager != null) { mCameraManager.setTorch(false); mCameraManager.stopDecoding(); mCameraManager.stopPreview(); } mUiHandler.removeCallbacks(mShowRunnable); mUiHandler.post(mDismissRunnable); mUiHandler.removeMessages(BCRDR_MSG_DECODE_TIMEOUT);*/ state = State.PREVIEW; return SUCCESS; } else { state = State.DONE; } g_nMultiReadResultCount = 0; return NONE; } } // ----------------- 回调 & 属性设置 ------------------- @Override public void onDecodeComplete(int event, HSMDecodeResult result, BarcodeReader reader) { Log.d(TAG, "onDecodeComplete: event=" + event); workPreviewTime = System.currentTimeMillis(); if (event == BarcodeReader.BCR_SUCCESS) { decodeTime =workPreviewTime - decodeStartTime; if (CONTINUOUS_MODE != Triggering.CONTINUOUS.toInt()) { state = State.PREVIEW; if (result != null) { if (result.getBarcodeBounds() != null) { System.arraycopy(result.getBarcodeBounds(), 0, bounds, 0, result.getBarcodeBounds().length >= 10 ? 10 : result.getBarcodeBounds().length); } if (saveMode == 5) { final byte[] m_frameData = mBarcodeReader.getLastDecImage(); new Thread(new Runnable() { @Override public void run() { Bitmap modBm = createBitmap(m_frameData, 0); Utils.saveImageAsPNG(modBm, "sdcard/temp/", "temp"); modBm.recycle(); modBm = null; } }).start(); } byte codeId = result.getCodeId(); int SymbologyId = OEMSymbologyId.getHSMSymbologyId(codeId); String symName = OEMSymbologyId.stringFromHSMSymbology(codeId); aimCodeId[0] = ']'; aimCodeId[1] = result.getAIMCodeLetter(); aimCodeId[2] = result.getAIMModifier(); byte[] byteArraryData = result.getBarcodeDataBytes(); if (transmitCodeID > 0 || enableGroupSeperator == 1) { byte[] barcodeDataBytes = composeAISeparator(aimCodeId[1], aimCodeId[2], codeId, byteArraryData, result.getBarcodeDataLength()); if (barcodeDataBytes != null) { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(barcodeDataBytes, barcodeDataBytes.length, aimCodeId, SymbologyId, symName, decodeTime); } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(byteArraryData, byteArraryData.length, aimCodeId, SymbologyId, symName, decodeTime); } } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(byteArraryData, byteArraryData.length, aimCodeId, SymbologyId, symName, decodeTime); } } if (enableSendBarcodeAndBitmap) { String barcode = new String(result.getBarcodeDataBytes(), 0, result.getBarcodeDataLength()); Log.d(TAG, "onDecodeComplete: barcode: " + barcode); byte[] m_frameData = mBarcodeReader.getLastDecImage(); int[] bounds = result.getBarcodeBounds(); sendBarcodeAndBitmapBroadcast(barcode, m_frameData, bounds); } } } else { if (ignoreRepeatMode == 2) {//解多码,已移除次设置项 if (g_nMultiReadResultCount > 0 && decodeDataArray.size() > 0) { for (DecodeData firstResult : decodeDataArray) { if (result.getBarcodeDataBytes() != null && compareBytes(firstResult.getBarcodeDataBytes(), result.getBarcodeDataBytes(), result.getBarcodeDataLength())) { //decodeDataArray.clear(); return; } } } g_nMultiReadResultCount++; DecodeData data = new DecodeData(); data.setBarcodeDataBytes(result.getBarcodeDataBytes()); data.setBarcodeDataLength(result.getBarcodeDataLength()); data.setDecodeTime(result.getDecodeTime()); data.setSymbologyId(result.getSymbologyId()); data.setCodeId(result.getCodeId()); data.setAIMCodeLetter(result.getAIMCodeLetter()); data.setAIMModifier(result.getAIMModifier()); decodeDataArray.add(data); if (g_nMultiReadResultCount >= g_nMaxMultiReadCount) { Log.d(TAG, "MAX onMultiReadCallback " + g_nMultiReadResultCount); //receivedMultipleDecodedData(decodeDataArray); if (mUiHandler != null) { mUiHandler.sendEmptyMessage(MESSAGE_CONTINUOUS_MULTI_DECODE_COMPLETE); } } } else { if (ignoreRepeatMode == 1) { if (preDecodeData != null && System.currentTimeMillis() - waitRepeatStartTime < waitRepeatTimeOut && compareBytes(result.getBarcodeDataBytes(), result.getBarcodeDataLength())) { decodeStartTime = System.currentTimeMillis(); return; } } //g_nMultiReadResultCount++; preDecodeData = result.getBarcodeDataBytes(); decodeStartTime = waitRepeatStartTime = System.currentTimeMillis(); byte codeId = result.getCodeId(); int SymbologyId = OEMSymbologyId.getHSMSymbologyId(codeId); String symName = OEMSymbologyId.stringFromHSMSymbology(codeId); aimCodeId[0] = ']'; aimCodeId[1] = result.getAIMCodeLetter(); aimCodeId[2] = result.getAIMModifier(); if (transmitCodeID > 0 || enableGroupSeperator == 1) { byte[] barcodeDataBytes = composeAISeparator(aimCodeId[1], aimCodeId[2], codeId, preDecodeData, result.getBarcodeDataLength()); if (barcodeDataBytes != null) { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(barcodeDataBytes, barcodeDataBytes.length, aimCodeId, SymbologyId, symName, decodeTime); } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(preDecodeData, preDecodeData.length, aimCodeId, SymbologyId, symName, decodeTime); } } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(preDecodeData, preDecodeData.length, aimCodeId, SymbologyId, symName, decodeTime); } } } } } else if (BarcodeReader.DECODE_STATUS_MULTI_DEC_COUNT == event) { //解码超时,未读取固定个数,输出已解码数据 Log.i(TAG, "onDecodeComplete multiDecodeMode=" + multiDecodeMode + " fullReadMode=" + fullReadMode + " decodeDataArray=" + decodeDataArray.size()); if (multiDecodeMode == 1 && fullReadMode == 0 && decodeDataArray.size() > 0) { //receivedMultipleDecodedData(decodeDataArray); if (mUiHandler != null) { if (mUiHandler != null) { mUiHandler.sendEmptyMessage(MESSAGE_MULTI_DECODE_COMPLETE); } } } state = State.PREVIEW; } else { state = State.PREVIEW; } } @Override public void onEvent(int event, int info, byte[] data, BarcodeReader reader) { Log.d(TAG, "onEvent: event=" + event + ", info=" + info); if (BarcodeReader.DECODE_STATUS_MULTI_DEC_COUNT == event) { //单张图片解码超时,未清空decodeDataArray if (g_nMultiReadResultCount > 0 && decodeDataArray.size() > 0) { g_nMultiReadResultCount = 0; decodeDataArray.clear(); } } } @Override public boolean onKeepGoingCallback() { return state == State.DECODING; } @Override public boolean onMultiReadCallback() { //Log.i(TAG, "onMultiReadCallback"); //final long startdec = System.currentTimeMillis(); final HSMDecodeResult firstResult = mBarcodeReader.getDecodeData(); if (firstResult != null) { workPreviewTime = System.currentTimeMillis(); if (multiDecodeMode > 0) { //单张图片解码超时,未清空decodeDataArray /*if(g_nMultiReadResultCount > 0 && decodeDataArray.size() > 0) { for(DecodeData result: decodeDataArray) { if(result.getBarcodeDataBytes() != null && comparabytes(firstResult.getBarcodeDataBytes(), result.getBarcodeDataBytes(), result.getBarcodeDataLength())) { //decodeDataArray.clear(); return true; } } }*/ g_nMultiReadResultCount++; DecodeData data = new DecodeData(); data.setBarcodeDataBytes(firstResult.getBarcodeDataBytes()); //if(firstResult.getBarcodeDataBytes() != null) //Log.d(TAG, "onMultiReadCallback " + (new String(firstResult.getBarcodeDataBytes()))); data.setBarcodeDataLength(firstResult.getBarcodeDataLength()); data.setDecodeTime(firstResult.getDecodeTime()); data.setSymbologyId(firstResult.getSymbologyId()); data.setCodeId(firstResult.getCodeId()); data.setAIMCodeLetter(firstResult.getAIMCodeLetter()); data.setAIMModifier(firstResult.getAIMModifier()); decodeDataArray.add(data); if (g_nMultiReadResultCount >= g_nMaxMultiReadCount) { Log.d(TAG, "MAX onMultiReadCallback " + g_nMultiReadResultCount); //receivedMultipleDecodedData(decodeDataArray); if (mUiHandler != null) { mUiHandler.sendEmptyMessage(MESSAGE_MULTI_DECODE_COMPLETE); } return false; } } else { preDecodeData = firstResult.getBarcodeDataBytes(); waitRepeatStartTime = System.currentTimeMillis(); decodeTime = waitRepeatStartTime -decodeStartTime; if (mOnScannedListener != null) { byte codeId = firstResult.getCodeId(); int symId = OEMSymbologyId.getHSMSymbologyId(codeId); aimCodeId[0] = ']'; aimCodeId[1] = firstResult.getAIMCodeLetter(); aimCodeId[2] = firstResult.getAIMModifier(); String symName = OEMSymbologyId.stringFromHSMSymbology(codeId); mOnScannedListener.receivedDecodedData(preDecodeData, preDecodeData.length, aimCodeId, symId, symName, decodeTime); } return false; } } return true; } @Override public void setDefaults(boolean enable) { if (mBarcodeReader != null) { if(enable) { mParametersHelper.setDefaultProperty(); } SparseArray<Property> mCacheProperties = mParametersHelper.getALLDecoderProperty(); for (int i = 0; i < mCacheProperties.size(); i++) { int keyForIndex = mCacheProperties.keyAt(i); setDecodeParameter(keyForIndex); } SparseArray<Property> mCacheStringProperties = mParametersHelper.getALLStringProperty(); for (int i = 0; i < mCacheStringProperties.size(); i++) { int keyForIndex = mCacheStringProperties.keyAt(i); setDecodeParameter(keyForIndex); } CONTINUOUS_MODE = mParametersHelper.getIntProperty(PropertyID.TRIGGERING_MODES); } } @Override public int setProperties(SparseArray<Integer> property) { if (mBarcodeReader != null) { if (property != null) { int size = property.size(); Log.d(TAG, "setProperties property size= " + size); for (int i = 0; i < size; i++) { int keyForIndex = property.keyAt(i); int value = property.get(keyForIndex); int ret = mParametersHelper.setIntProperty(keyForIndex, value); if(ret == 0) { setDecodeParameter(keyForIndex); } else { Log.d(TAG, "setProperties error property= " + keyForIndex); } } return SUCCESS; } } return NONE; } @Override public int setStringProperties(SparseArray<String> property) { if (mBarcodeReader != null) { if (property != null) { int size = property.size(); Log.d(TAG, "setStringProperties property size= " + size); for (int i = 0; i < size; i++) { int keyForIndex = property.keyAt(i); String value = property.get(keyForIndex); int ret = mParametersHelper.setStringProperty(keyForIndex, value); if(ret == 0) { setDecodeParameter(keyForIndex); } else { Log.d(TAG, "setStringProperties error property= " + keyForIndex); } } return SUCCESS; } } return NONE; } @Override protected void release() { close(); mContext = null; sInstance = null; // 若希望下次重新创建,则可释放单例 Log.d(TAG, "release"); synchronized (mUiHandler) { mUiHandler.removeMessages(BCRDR_MSG_TIMER_PREVIEW); if (mBarcodeReader != null) { //mBarcodeReader.stopDecode(); mBarcodeReader.release(); mBarcodeReader = null; } state = State.DONE; quitHandlerThreadLocked(); } } // ----------------- 相机操作 ------------------- private void initCamera() { if (mSurfaceTexture == null) { Log.e(TAG, "initCamera: SurfaceTexture 为空,无法初始化相机"); return; } setupCamera(mSurfaceWidth, mSurfaceHeight); openCamera(); } private void setupCamera(int width, int height) { Log.d(TAG, "setupCamera: " + width + "x" + height); CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); try { String[] cameraIds = cameraManager.getCameraIdList(); String foundCameraId = null; for (String id : cameraIds) { CameraCharacteristics cc = cameraManager.getCameraCharacteristics(id); Integer facing = cc.get(CameraCharacteristics.LENS_FACING); if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) { foundCameraId = id; break; } } if (foundCameraId == null) { Log.e(TAG, "未找到后置摄像头"); return; } mCameraId = foundCameraId; CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(mCameraId); StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map == null) { throw new RuntimeException("无法获取可用预览尺寸"); } // 选一个合适的预览大小 mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height); Log.d(TAG, "setupCamera: mPreviewSize.getWidth()" + mPreviewSize.getWidth() + "mPreviewSize.getHeight" + mPreviewSize.getHeight()); // 创建ImageReader,用于获取图像帧 mImageReader = ImageReader.newInstance( mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, 2 ); mImageReader.setOnImageAvailableListener( mOnImageAvailableListener, mBackgroundHandler ); configureTransform(width, height); } catch (CameraAccessException e) { Log.e(TAG, "setupCamera: CameraAccessException", e); } } private Size chooseOptimalSize(Size[] choices, int width, int height) { // 示例:简单找个和3:2最接近的 double targetRatio = 3.0 / 2.0; Size optimalSize = null; double minDiff = Double.MAX_VALUE; int maxWidth = 1080; int maxHeight = 720; for (Size size : choices) { // 筛选出小于等于最大尺寸的选项 if (size.getWidth() <= maxWidth && size.getHeight() <= maxHeight) { double ratio = (double) size.getWidth() / size.getHeight(); double diff = Math.abs(ratio - targetRatio); if (diff < minDiff) { optimalSize = size; minDiff = diff; } } } // 如果没有符合条件的尺寸,可以返回默认尺寸(选择最接近的) return optimalSize == null ? choices[0] : optimalSize; } private void openCamera() { CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); try { // 权限检查 if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { Log.e(TAG, "openCamera: 没有CAMERA权限"); return; } cameraManager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); } catch (CameraAccessException e) { Log.e(TAG, "openCamera: CameraAccessException", e); } } private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { mCameraDevice = camera; createCameraPreviewSession(); mIsCameraInitialized = true; } @Override public void onDisconnected(@NonNull CameraDevice camera) { camera.close(); mCameraDevice = null; mIsCameraInitialized = false; } @Override public void onError(@NonNull CameraDevice camera, int error) { camera.close(); mCameraDevice = null; mIsCameraInitialized = false; Log.e(TAG, "相机错误: " + error); } }; private void createCameraPreviewSession() { try { if (mCameraDevice == null || mPreviewView == null || mPreviewView.getSurfaceTexture() == null) { Log.e(TAG, "createCameraPreviewSession: 条件不满足"); return; } SurfaceTexture texture = mPreviewView.getSurfaceTexture(); texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); Surface previewSurface = new Surface(texture); Surface readerSurface = mImageReader.getSurface(); mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewRequestBuilder.addTarget(previewSurface); mPreviewRequestBuilder.addTarget(readerSurface); mCameraDevice.createCaptureSession( Arrays.asList(previewSurface, readerSurface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { if (mCameraDevice == null) return; mCaptureSession = session; // 自动对焦 mPreviewRequestBuilder.set( CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE ); // 若此时处于解码状态,开始预览 if (mIsDecoding) { startPreview(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { Log.e(TAG, "onConfigureFailed: 配置失败"); } }, mBackgroundHandler ); } catch (CameraAccessException e) { Log.e(TAG, "createCameraPreviewSession: ", e); } } private void startPreview() { if (mCaptureSession == null || mPreviewRequestBuilder == null) { Log.e(TAG, "startPreview: CaptureSession或RequestBuilder为空"); return; } try { mCaptureSession.setRepeatingRequest( mPreviewRequestBuilder.build(), null, mBackgroundHandler ); new Handler(Looper.getMainLooper()).post(() -> { mBlackView.setVisibility(View.INVISIBLE); }); Log.d(TAG, "startPreview: 预览开始"); } catch (CameraAccessException e) { Log.e(TAG, "startPreview: 失败", e); } } private void stopPreview() { if (mCaptureSession == null) return; try { mCaptureSession.stopRepeating(); Log.d(TAG, "stopPreview: 预览已停止"); } catch (CameraAccessException e) { Log.e(TAG, "stopPreview: 失败", e); } } private void closeCamera() { try { if (mCaptureSession != null) { mCaptureSession.close(); mCaptureSession = null; } if (mCameraDevice != null) { mCameraDevice.close(); mCameraDevice = null; } if (mImageReader != null) { mImageReader.close(); mImageReader = null; } } catch (Exception e) { Log.e(TAG, "closeCamera: ", e); } mIsCameraInitialized = false; } private void configureTransform(int viewWidth, int viewHeight) { if (mPreviewSize == null || mPreviewView == null) { return; } int rotation = mWindowManager.getDefaultDisplay().getRotation(); Matrix matrix = new Matrix(); RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); float centerX = viewRect.centerX(); float centerY = viewRect.centerY(); // 根据屏幕方向进行必要的变换 if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); float scale = Math.max( (float) viewHeight / mPreviewSize.getHeight(), (float) viewWidth / mPreviewSize.getWidth() ); matrix.postScale(scale, scale, centerX, centerY); matrix.postRotate(90 * (rotation - 2), centerX, centerY); } else if (rotation == Surface.ROTATION_180) { matrix.postRotate(180, centerX, centerY); } new Handler(Looper.getMainLooper()).post(() -> { mPreviewView.setTransform(matrix); }); } // ----------------- 图像处理与解码示例 ------------------- private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { try (Image image = reader.acquireLatestImage()) { if (image == null || !mIsDecoding) { return; } processImageForDecode(image); } /*try { image = reader.acquireLatestImage(); if (image == null || !mIsDecoding) { return; } processImageForDecode(image); } finally { if (image != null) { image.close(); } }*/ } }; private void processImageForDecode(Image image) { // 这里示例使用Zxing解码 // 获取图像数据并解码 byte[] imageData = imageToYUV(image); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); int width = image.getWidth(); int height = image.getHeight(); Log.d(TAG, "processImageForDecode: data.length(): " + imageData.length + " width: " + width + " height: " + height); if (mIsDecoding) { Log.d(TAG, "processImageForDecode: startDecode"); mDecodeHandler.decode(imageData, width, height); } /*PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource( data, width, height, 0, 0, width, height, false ); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); String decodeResult = decodeBarCode(bitmap); if (decodeResult != null) { Log.d(TAG, "Decode成功: " + decodeResult); // 解码成功示例:直接stopDecode() stopDecode(); }*/ // 保存最后帧YUV数据 mLastDecodeImage = imageData; } /*private String decodeBarCode(BinaryBitmap bitmap) { try { MultiFormatReader multiFormatReader = new MultiFormatReader(); Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class); Vector<BarcodeFormat> decodeFormats = new Vector<>(); decodeFormats.add(BarcodeFormat.QR_CODE); decodeFormats.add(BarcodeFormat.CODE_128); decodeFormats.add(BarcodeFormat.EAN_13); hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats); hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); multiFormatReader.setHints(hints); Result result = multiFormatReader.decodeWithState(bitmap); return result.getText(); } catch (NotFoundException e) { // 没有检测到码 return null; } catch (Exception e) { Log.e(TAG, "decodeBarCode error", e); return null; } }*/ private byte[] imageToYUV(Image image) { int width = image.getWidth(); // 1080 int height = image.getHeight(); // 720 Log.d(TAG, "imageToYUV: image.width: " + width + " image.height: " + height); Image.Plane[] planes = image.getPlanes(); ByteBuffer yBuffer = planes[0].getBuffer(); int rowStride = planes[0].getRowStride(); int pixelStride = planes[0].getPixelStride(); byte[] yuvData = new byte[width * height]; // 只提取 Y 分量 // 逐行拷贝 Y 数据,去除填充 for (int row = 0; row < height; row++) { yBuffer.position(row * rowStride); // 移动到该行的起始位置 yBuffer.get(yuvData, row * width, width); // 读取有效数据 } return yuvData; } // ----------------- 其它Scanner抽象方法实现示例 ------------------- @Override public int aimerOn(boolean on) { Log.d(TAG, "aimerOn: " + on + " (本示例不支持)"); return 0; } @Override public int illuminationOn(boolean on) { // 闪光灯开关示例 try { if (mCameraDevice != null && mPreviewRequestBuilder != null && mCaptureSession != null) { mPreviewRequestBuilder.set( CaptureRequest.FLASH_MODE, on ? CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF ); mCaptureSession.setRepeatingRequest( mPreviewRequestBuilder.build(), null, mBackgroundHandler ); return 0; } } catch (CameraAccessException e) { Log.e(TAG, "illuminationOn: 闪光灯设置失败", e); } return -1; } @Override public byte[] getLastDecImage(int rotate) { // 如果需要根据 rotate 进行旋转,这里可以自行实现 if (mBarcodeReader != null) { byte[] m_frameData = mBarcodeReader.getLastDecImage(); Bitmap modBm = createBitmap(m_frameData, rotate >= 0 ? rotate : rotateImage); if (modBm != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); modBm.compress(Bitmap.CompressFormat.JPEG, 50, baos); modBm.recycle(); modBm = null; return baos.toByteArray(); } } return null; } @Override public String getScanEngineInfo() { if(decoderVersion != null) { decoderVersion = decoderVersion.trim(); } return decoderVersion; } @Override public int setNumParameter(int paramNum, int paramVal) { if (mBarcodeReader != null) { return mBarcodeReader.setParameter(paramNum, paramVal); } return NONE; } @Override public int setStrParameter(int paramNum, String paramVal) { if (mBarcodeReader != null && !TextUtils.isEmpty(paramVal)) { return mBarcodeReader.setParameter(paramNum, paramVal); } return NONE; } @Override public int getNumParameter(int paramNum) { if (mBarcodeReader != null) { return mBarcodeReader.getNumParameter(paramNum); } return NONE; } @Override public String getStrParameter(int paramNum) { // 示例中如果SDK没有相应的getStr接口,可以自行封装 return null; } @Override public boolean lockHwTriggler(boolean lock) { Log.d(TAG, "lockHwTriggler: " + lock + " (本示例不支持)"); return false; } // OverlayPreviewWindow.Listener 相关(如无实际逻辑可不实现) @Override public void onWindowCreated(SurfaceTexture surfaceTexture, int width, int height) { // 示例中实际由TextureView的Listener处理 } @Override public void onWindowCreated(SurfaceHolder holder) { Log.d(TAG, "onWindowCreated"); if(holder != null) { surfaceHolder = holder; synchronized (mDecodeLock) { if(mCameraManager != null && mCameraManager.isOpen()) { try { mCameraManager.initializeCamera(surfaceHolder); mCameraManager.startPreview(); if(aimerIllumMode == 2 || aimerIllumMode == 3) { mCameraManager.setTorch(true); } } catch (IOException e) { e.printStackTrace(); } } } } } @Override public void onWindowDestroyed() { // 示例中可视情况决定是否closeCamera Log.d(TAG, "onWindowDestroyed"); if(mCameraManager != null) { mCameraManager.setTorch(false); mCameraManager.stopPreview(); } surfaceHolder = null; } @Override public void onStateChanged(int state) { Log.d(TAG, "onStateChanged = " + state); if(mCameraManager != null) { if (state == 1000) { mCameraManager.setTorch(true); } else if (state == -1000) { mCameraManager.setTorch(false); } } } // ----------------- 后台线程控制 ------------------- private void startCamera() { // 启动后台线程 startBackgroundThread(); // 如果 TextureView 已经可用,直接设置相机 if (mPreviewView.isAvailable()) { setupCamera(mPreviewView.getWidth(), mPreviewView.getHeight()); openCamera(); } // 显示预览窗口 new Handler(Looper.getMainLooper()).post(() -> { mCameraPreviewLayout.setVisibility(View.VISIBLE); mBlackView.setVisibility(View.INVISIBLE); }); Log.d(TAG, "Camera started successfully"); } private void startBackgroundThread() { if (mBackgroundThread == null) { mBackgroundThread = new HandlerThread("CameraBackground"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } } private void stopBackgroundThread() { if (mBackgroundThread != null) { mBackgroundThread.quitSafely(); try { mBackgroundThread.join(); } catch (InterruptedException e) { Log.e(TAG, "stopBackgroundThread: ", e); } mBackgroundThread = null; mBackgroundHandler = null; } }
}
这里有个LegacyCamera2Scanner, 我想要在 停止预览 时就「彻底清空或销毁」与 TextureView 相关的所有内容,下次再启动预览时「重新创建一个全新的 TextureView」,请使用中文分析并回答我应该修改哪几处代码,直接使用markdown格式给出代码
以下给出一个示例思路,演示如何在「停止预览」时真正销毁与 TextureView 相关的所有布局和引用;然后下次再启动时重新创建一个全新的 TextureView。这个示例做了如下关键修改:
destroyCameraPreviewLayout()
,里面移除并清空与预览相关的所有引用,尤其是从 WindowManager
中 remove 掉 mCameraPreviewLayout
;stopPreview()
或 stopDecode()
里 调用 destroyCameraPreviewLayout()
,确保真正销毁 UI;startDecode()
或 startCamera()
里),先检查 mCameraPreviewLayout
是否为 null
。如果为空,就重新调用 createCameraPreviewLayout()
来生成一个全新的 TextureView
以及对应的布局;这样就能保证每次“停止预览”后都彻底清空,下一次启动时再全新创建。下面用 Markdown 形式给出相对精简的修改示例(请根据你项目里实际的方法调用顺序、成员变量及业务逻辑进行适配):
javapublic class LegacyCamera2Scanner extends Scanner implements BarcodeReader.DecodeCallback, BarcodeReader.DecoderListener, OverlayPreviewWindow.Listener { private static final String TAG = "LegacyCamera2Scanner"; // ...(其他字段略)... // 预览相关 private View mCameraPreviewLayout; private TextureView mPreviewView; private View mBlackView; private WindowManager mWindowManager; private SurfaceTexture mSurfaceTexture; private int mSurfaceWidth; private int mSurfaceHeight; // ============================== // 1) 新增“销毁”预览布局的方法 // ============================== /** * 真正从 WindowManager 中移除 CameraPreviewLayout, * 并清空所有与 TextureView / 布局相关的引用。 */ private void destroyCameraPreviewLayout() { if (mCameraPreviewLayout != null) { try { mWindowManager.removeView(mCameraPreviewLayout); } catch (Exception e) { Log.w(TAG, "destroyCameraPreviewLayout: removeView failed or already removed.", e); } // 将所有引用置空,确保下次可以重新创建 mCameraPreviewLayout = null; mPreviewView = null; mBlackView = null; mSurfaceTexture = null; mSurfaceWidth = 0; mSurfaceHeight = 0; } } // ============================== // 2) 创建预览布局的方法(保持原逻辑), // 但注意下次需要重新 new // ============================== private void createCameraPreviewLayout() { mCameraPreviewLayout = LayoutInflater.from(mContext).inflate(R.layout.scanner_layout, null); mPreviewView = mCameraPreviewLayout.findViewById(R.id.previewView); mBlackView = mCameraPreviewLayout.findViewById(R.id.blackMaskView); // 如果布局文件里不是 TextureView,动态替换 if (!(mPreviewView instanceof TextureView)) { mPreviewView = new TextureView(mContext); ViewGroup viewGroup = (ViewGroup) mCameraPreviewLayout; viewGroup.removeAllViews(); viewGroup.addView(mPreviewView); } WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); // 默认先设置为不可见 new Handler(Looper.getMainLooper()).post(() -> { if (mBlackView != null) { mBlackView.setVisibility(View.VISIBLE); } if (mCameraPreviewLayout != null) { mCameraPreviewLayout.setVisibility(View.INVISIBLE); } }); // 加到 WindowManager 中 mWindowManager.addView(mCameraPreviewLayout, params); // 设置 TextureView 回调 mPreviewView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mSurfaceTexture = surface; mSurfaceWidth = width; mSurfaceHeight = height; Log.d(TAG, "onSurfaceTextureAvailable: " + width + "x" + height); setupCamera(width, height); openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { configureTransform(width, height); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }); // 若已可用直接处理 if (mPreviewView.isAvailable()) { mSurfaceTexture = mPreviewView.getSurfaceTexture(); mSurfaceWidth = mPreviewView.getWidth(); mSurfaceHeight = mPreviewView.getHeight(); if (mIsOpened && !mIsCameraInitialized) { initCamera(); } } } // ============================== // 3) 修改停止预览相关的代码 // ============================== private void stopPreview() { if (mCaptureSession == null) return; try { mCaptureSession.stopRepeating(); Log.d(TAG, "stopPreview: 预览已停止"); } catch (CameraAccessException e) { Log.e(TAG, "stopPreview: 失败", e); } } // ============================== // 4) 在 stopDecode() 中,停止预览后, // 调用 destroyCameraPreviewLayout() // ============================== @Override public int stopDecode() { Log.d(TAG, "stopDecode: 停止解码"); mIsDecoding = false; // 先停止硬件层的预览 stopPreview(); // 关闭摄像头 closeCamera(); // ============ 关键:真正销毁布局 ============= // destroyCameraPreviewLayout(); // 下面是原有的逻辑,只是把“隐藏布局”等操作换成了上面的 destroy synchronized (mUiHandler) { if (mBarcodeReader != null) { if (state == State.DECODING) { mBarcodeReader.stopDecode(); } state = State.PREVIEW; return SUCCESS; } else { state = State.DONE; } g_nMultiReadResultCount = 0; return NONE; } } // ============================== // 5) 在 startDecode() 或 startCamera() 里, // 如果布局已被销毁,就重新创建 // ============================== private void startCamera() { // 启动后台线程 startBackgroundThread(); // 如果上次 stopDecode() 已经把布局销毁了,这里要重新创建 if (mCameraPreviewLayout == null) { createCameraPreviewLayout(); } // 如果 TextureView 已可用则走相机初始化 if (mPreviewView.isAvailable()) { setupCamera(mPreviewView.getWidth(), mPreviewView.getHeight()); openCamera(); } // 显示预览窗口 new Handler(Looper.getMainLooper()).post(() -> { if (mCameraPreviewLayout != null) { mCameraPreviewLayout.setVisibility(View.VISIBLE); } if (mBlackView != null) { mBlackView.setVisibility(View.INVISIBLE); } }); Log.d(TAG, "Camera started successfully"); } @Override public int startDecode(int timeout) { Log.d(TAG, "startDecode: 开始解码, 超时=" + timeout); synchronized (mUiHandler) { mIsDecoding = true; // 如果相机没初始化或布局销毁, 需重新创建布局并打开相机 if (!mIsCameraInitialized || mCameraPreviewLayout == null) { startCamera(); } else { // 如果相机已初始化, 直接显示并开预览 if (mCameraPreviewLayout != null) { new Handler(Looper.getMainLooper()).post(() -> { mCameraPreviewLayout.setVisibility(View.VISIBLE); }); } startPreview(); } // ... 以下是原有解码的逻辑 ... if (mBarcodeReader != null) { if (state != State.DECODING) { decodeDataArray.clear(); g_nMultiReadResultCount = 0; preDecodeData = null; decodeStartTime = System.currentTimeMillis(); state = State.DECODING; // ... 设置超时之类 ... return SUCCESS; } return BUSY; } else { state = State.DONE; return NONE; } } } // ...后续其他代码不变... // ========================================================= }
创建/销毁预览布局
createCameraPreviewLayout()
里只是在 stopDecode()
中把布局设置成不可见 (View.INVISIBLE
);这样并不会真正销毁 TextureView
。destroyCameraPreviewLayout()
,调用 mWindowManager.removeView(mCameraPreviewLayout)
来把它从窗口中移除,并将引用全部置空。在 stopDecode()
或 stopPreview()
里销毁布局
destroyCameraPreviewLayout()
,使上一次的 TextureView
得到彻底清理。下次启动预览时再“重新创建”
startDecode()
或专门的 startCamera()
方法时,先判断 mCameraPreviewLayout == null
,如果为 null
,说明上一次已经销毁,需要重新调用 createCameraPreviewLayout()
;TextureView
,从而实现“下次预览全新创建”的效果。请根据项目的实际初始化流程(例如是否在 startDecode()
内就创建布局,或者在外部先调用某些方法)进行微调。核心思路就是:真正 removeView + 置空,然后 下一次需要时,再重新 new 一个布局。这样就能在“停止预览”时彻底销毁 TextureView 相关内容,下次启动再拿到一个全新的 TextureView。
// 加到 WindowManager 中
mWindowManager.addView(mCameraPreviewLayout, params);
这里报错了:
java.lang.RuntimeException: Can't create handler inside thread Thread[Binder:27006_3,5,main] that has not called Looper.prepare()
你遇到的错误:
java.lang.RuntimeException: Can't create handler inside thread Thread[Binder:27006_3,5,main] that has not called Looper.prepare()
说明当前调用 mWindowManager.addView(...)
的线程 并不是 主线程(UI线程),而是一个 Binder 线程或者后台线程。但 调用 WindowManager
去添加 View 必须在主线程 执行,否则就会出现类似错误。
mWindowManager.addView(...)
。Handler(Looper.getMainLooper())
或其他方式切回主线程,然后再调用 UI 操作。比如,你可以这样写(示例):
javaprivate void createCameraPreviewLayout() { // 创建你的布局 mCameraPreviewLayout = LayoutInflater.from(mContext).inflate(R.layout.scanner_layout, null); mPreviewView = mCameraPreviewLayout.findViewById(R.id.previewView); mBlackView = mCameraPreviewLayout.findViewById(R.id.blackMaskView); // 如果布局文件里不是 TextureView, 动态替换 if (!(mPreviewView instanceof TextureView)) { mPreviewView = new TextureView(mContext); ViewGroup viewGroup = (ViewGroup) mCameraPreviewLayout; viewGroup.removeAllViews(); viewGroup.addView(mPreviewView); } // 布局参数 WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); // 确保在主线程调用 addView Handler mainHandler = new Handler(Looper.getMainLooper()); mainHandler.post(() -> { try { mWindowManager.addView(mCameraPreviewLayout, params); } catch (Exception e) { e.printStackTrace(); } // 这里可以再做可见性等其他UI相关操作 if (mBlackView != null) { mBlackView.setVisibility(View.VISIBLE); } if (mCameraPreviewLayout != null) { mCameraPreviewLayout.setVisibility(View.INVISIBLE); } }); // 之后给 TextureView 设置监听(可在上面的 mainHandler.post() 里也行) mPreviewView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mSurfaceTexture = surface; mSurfaceWidth = width; mSurfaceHeight = height; // ...后续操作... } // 其余回调省略 ... }); }
这样做的关键点在于:
mWindowManager.addView(...)
;必须把它切回主线程去执行。new Handler(Looper.getMainLooper()).post(...)
,把对 WindowManager
的操作包装进去。WindowManager.addView
,同样需要切回主线程(或者直接在 onCreate()
里就已经是主线程);Handler
或 runOnUiThread()
(如果有 Activity 上下文的话)把 UI 操作切到主线程。总之:所有 UI 相关的操作都必须在 UI 线程执行。你的异常正是因为在非UI线程中“创建了一个 Handler”并执行了 UI 操作引起的。
我现在在做条码识别,如何使用OpenCV来优化我的条码识别功能(解码速度,污码,错码识别)或者还有没有其他方式可以优化我的条码识别功能。使用中文回答我的问题
在实际做条码识别(含一维码、二维码等)时,为了提升解码速度、提升对模糊、污损、失真条码的识别率,可以从以下几个方面入手进行优化。下面着重介绍 OpenCV 在图像预处理阶段的一些思路,以及其他可行的优化方法。
很多解码库(如 Zxing、ZBar 等)在高质量、对比度适中的图像上识别速度和准确度都比较好。但是在真实场景中,可能会存在污损、失焦、运动模糊、对比度低等问题。此时可以先用 OpenCV 做一系列预处理,再把处理后的图像交给解码引擎,提高成功率。
常见预处理手段包括:
去噪/平滑(Denoise/Smoothing)
图像增强 (Image Enhancement)
阈值化 (Thresholding) 或自适应阈值 (Adaptive Threshold)
形态学操作 (Morphological Operations)
图像校正 (Deskew / Perspective Transform)
ROI 提取 (Region of Interest)
小结:以上思路的核心就是“让条码图像变得更纯净、更高对比度、更不失真”再交给解码库。这样能显著提高对污码、破损码以及拍摄质量不佳情况下的识别率和识别速度。
除了借助 OpenCV 的预处理,你还可以从以下方向做进一步优化:
硬件层面
多帧合成/智能选帧
调整解码库参数
多算法/多库融合
神经网络/深度学习
先从简单的 OpenCV 预处理开始
如果是视频流
注意性能平衡
测试环境 & 条码多样性
希望这些思路能帮助你更好地利用 OpenCV(或其他图像处理手段)来优化条码识别的速度和准确度。祝开发顺利!