A comprehensive desktop barcode scanner application built with Java Swing and popular barcode scanning libraries.
Technologies Used
- Java Swing - Desktop GUI
- ZXing (Zebra Crossing) - Barcode scanning library
- Webcam Capture API - Camera integration
- JavaCV/OpenCV - Advanced image processing
- Maven - Dependency management
Dependencies
<dependencies> <!-- ZXing Core Barcode Library --> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.5.1</version> </dependency> <!-- Webcam Capture --> <dependency> <groupId>com.github.sarxos</groupId> <artifactId>webcam-capture</artifactId> <version>0.3.12</version> </dependency> <!-- JavaCV for advanced image processing --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.7</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <!-- Logging --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.7</version> </dependency> </dependencies>
Core Application Structure
Example 1: Main Application Class
package com.barcodescanner; import com.barcodescanner.gui.MainFrame; import com.formdev.flatlaf.FlatLightLaf; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; public class BarcodeScannerApp { private static final Logger logger = LoggerFactory.getLogger(BarcodeScannerApp.class); public static void main(String[] args) { // Set modern look and feel setupLookAndFeel(); // Run the application SwingUtilities.invokeLater(() -> { try { createAndShowGUI(); } catch (Exception e) { logger.error("Failed to start application", e); showErrorDialog("Failed to start application: " + e.getMessage()); } }); } private static void setupLookAndFeel() { try { UIManager.setLookAndFeel(new FlatLightLaf()); } catch (Exception e) { logger.warn("Failed to set FlatLaf, using system look and feel"); try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeel()); } catch (Exception ex) { logger.error("Failed to set system look and feel", ex); } } } private static void createAndShowGUI() { MainFrame mainFrame = new MainFrame(); mainFrame.setVisible(true); logger.info("Barcode Scanner application started successfully"); } private static void showErrorDialog(String message) { JOptionPane.showMessageDialog( null, message, "Application Error", JOptionPane.ERROR_MESSAGE ); } } Example 2: Main Application Frame
package com.barcodescanner.gui; import com.barcodescanner.config.AppConfig; import com.barcodescanner.scanner.BarcodeScanner; import com.barcodescanner.scanner.ScannerListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class MainFrame extends JFrame implements ScannerListener { private static final Logger logger = LoggerFactory.getLogger(MainFrame.class); private BarcodeScanner barcodeScanner; private CameraPanel cameraPanel; private ResultsPanel resultsPanel; private ControlPanel controlPanel; private StatusBar statusBar; public MainFrame() { initializeComponents(); setupLayout(); setupListeners(); setupScanner(); } private void initializeComponents() { setTitle("Barcode Scanner - " + AppConfig.APP_VERSION); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setPreferredSize(new Dimension(1200, 800)); setMinimumSize(new Dimension(800, 600)); // Initialize components cameraPanel = new CameraPanel(); resultsPanel = new ResultsPanel(); controlPanel = new ControlPanel(); statusBar = new StatusBar(); // Initialize scanner barcodeScanner = new BarcodeScanner(); barcodeScanner.addListener(this); } private void setupLayout() { setLayout(new BorderLayout()); // Create split pane for camera and results JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); splitPane.setLeftComponent(createCameraContainer()); splitPane.setRightComponent(resultsPanel); splitPane.setDividerLocation(600); splitPane.setResizeWeight(0.6); add(splitPane, BorderLayout.CENTER); add(controlPanel, BorderLayout.NORTH); add(statusBar, BorderLayout.SOUTH); pack(); setLocationRelativeTo(null); // Center on screen } private JPanel createCameraContainer() { JPanel container = new JPanel(new BorderLayout()); container.setBorder(BorderFactory.createTitledBorder("Camera View")); container.add(cameraPanel, BorderLayout.CENTER); return container; } private void setupListeners() { addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { confirmAndExit(); } }); // Control panel listeners controlPanel.setScanButtonListener(e -> toggleScanning()); controlPanel.setSettingsButtonListener(e -> showSettings()); controlPanel.setExportButtonListener(e -> exportResults()); controlPanel.setClearButtonListener(e -> clearResults()); } private void setupScanner() { barcodeScanner.addListener(cameraPanel); barcodeScanner.addListener(resultsPanel); barcodeScanner.addListener(statusBar); } private void toggleScanning() { if (barcodeScanner.isScanning()) { barcodeScanner.stopScanning(); controlPanel.setScanButtonText("Start Scanning"); statusBar.setStatus("Scanning stopped", StatusBar.INFO); } else { if (barcodeScanner.startScanning()) { controlPanel.setScanButtonText("Stop Scanning"); statusBar.setStatus("Scanning started", StatusBar.INFO); } else { statusBar.setStatus("Failed to start camera", StatusBar.ERROR); } } } private void showSettings() { SettingsDialog settingsDialog = new SettingsDialog(this); settingsDialog.setVisible(true); } private void exportResults() { resultsPanel.exportResults(); } private void clearResults() { resultsPanel.clearResults(); } private void confirmAndExit() { int result = JOptionPane.showConfirmDialog( this, "Are you sure you want to exit?", "Confirm Exit", JOptionPane.YES_NO_OPTION ); if (result == JOptionPane.YES_OPTION) { barcodeScanner.stopScanning(); dispose(); System.exit(0); } } @Override public void onBarcodeDetected(BarcodeResult result) { // Main frame can handle barcode results if needed logger.info("Barcode detected: {}", result.getText()); } @Override public void onScannerStarted() { statusBar.setStatus("Scanner started", StatusBar.INFO); } @Override public void onScannerStopped() { statusBar.setStatus("Scanner stopped", StatusBar.INFO); } @Override public void onError(String errorMessage) { statusBar.setStatus("Error: " + errorMessage, StatusBar.ERROR); JOptionPane.showMessageDialog(this, errorMessage, "Scanner Error", JOptionPane.ERROR_MESSAGE); } } Barcode Scanning Engine
Example 3: Core Barcode Scanner
package com.barcodescanner.scanner; import com.github.sarxos.webcam.Webcam; import com.github.sarxos.webcam.WebcamPanel; import com.google.zxing.*; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import java.awt.image.BufferedImage; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class BarcodeScanner { private static final Logger logger = LoggerFactory.getLogger(BarcodeScanner.class); private Webcam webcam; private WebcamPanel webcamPanel; private ScheduledExecutorService executor; private boolean scanning = false; private List<ScannerListener> listeners = new CopyOnWriteArrayList<>(); private Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class); // Statistics private int totalScans = 0; private int successfulScans = 0; private long startTime; public BarcodeScanner() { setupHints(); initializeWebcam(); } private void setupHints() { // Supported barcode formats List<BarcodeFormat> formats = Arrays.asList( BarcodeFormat.UPC_A, BarcodeFormat.UPC_E, BarcodeFormat.EAN_8, BarcodeFormat.EAN_13, BarcodeFormat.CODE_39, BarcodeFormat.CODE_93, BarcodeFormat.CODE_128, BarcodeFormat.QR_CODE, BarcodeFormat.DATA_MATRIX, BarcodeFormat.PDF_417 ); hints.put(DecodeHintType.POSSIBLE_FORMATS, formats); hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); } private void initializeWebcam() { try { List<Webcam> webcams = Webcam.getWebcams(); if (webcams.isEmpty()) { throw new RuntimeException("No webcams found"); } // Use first available webcam webcam = webcams.get(0); webcam.setViewSize(WebcamResolution.VGA.getSize()); // Create webcam panel for display webcamPanel = new WebcamPanel(webcam); webcamPanel.setFPSLimited(true); webcamPanel.setFPS(10); webcamPanel.setImageSizeDisplayed(true); webcamPanel.setMirrored(true); logger.info("Webcam initialized: {}", webcam.getName()); } catch (Exception e) { logger.error("Failed to initialize webcam", e); notifyError("Failed to initialize webcam: " + e.getMessage()); } } public boolean startScanning() { if (scanning) { logger.warn("Scanner is already running"); return true; } try { if (!webcam.isOpen()) { webcam.open(); } executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(this::captureAndDecode, 0, 100, TimeUnit.MILLISECONDS); scanning = true; startTime = System.currentTimeMillis(); notifyScannerStarted(); logger.info("Barcode scanning started"); return true; } catch (Exception e) { logger.error("Failed to start scanning", e); notifyError("Failed to start scanning: " + e.getMessage()); return false; } } public void stopScanning() { if (!scanning) { return; } scanning = false; if (executor != null) { executor.shutdown(); try { executor.awaitTermination(2, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } if (webcam != null && webcam.isOpen()) { webcam.close(); } notifyScannerStopped(); logger.info("Barcode scanning stopped. Stats: {}/{} successful scans in {}ms", successfulScans, totalScans, (System.currentTimeMillis() - startTime)); } private void captureAndDecode() { if (!scanning || !webcam.isOpen()) { return; } try { BufferedImage image = webcam.getImage(); if (image == null) { return; } totalScans++; BarcodeResult result = decodeBarcode(image); if (result != null) { successfulScans++; notifyBarcodeDetected(result); // Optional: Add delay after successful scan to prevent multiple reads try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } catch (Exception e) { logger.error("Error during barcode capture/decode", e); } } private BarcodeResult decodeBarcode(BufferedImage image) { try { LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); MultiFormatReader reader = new MultiFormatReader(); Result zxingResult = reader.decode(bitmap, hints); if (zxingResult != null) { return new BarcodeResult( zxingResult.getText(), zxingResult.getBarcodeFormat(), new Date(), image ); } } catch (NotFoundException e) { // No barcode found - this is normal } catch (Exception e) { logger.warn("Error decoding barcode", e); } return null; } public BufferedImage captureSingleImage() { if (webcam == null || !webcam.isOpen()) { return null; } return webcam.getImage(); } public BarcodeResult scanSingleImage(BufferedImage image) { return decodeBarcode(image); } // Listener management public void addListener(ScannerListener listener) { listeners.add(listener); } public void removeListener(ScannerListener listener) { listeners.remove(listener); } private void notifyBarcodeDetected(BarcodeResult result) { SwingUtilities.invokeLater(() -> { for (ScannerListener listener : listeners) { listener.onBarcodeDetected(result); } }); } private void notifyScannerStarted() { SwingUtilities.invokeLater(() -> { for (ScannerListener listener : listeners) { listener.onScannerStarted(); } }); } private void notifyScannerStopped() { SwingUtilities.invokeLater(() -> { for (ScannerListener listener : listeners) { listener.onScannerStopped(); } }); } private void notifyError(String errorMessage) { SwingUtilities.invokeLater(() -> { for (ScannerListener listener : listeners) { listener.onError(errorMessage); } }); } // Getters public boolean isScanning() { return scanning; } public WebcamPanel getWebcamPanel() { return webcamPanel; } public int getTotalScans() { return totalScans; } public int getSuccessfulScans() { return successfulScans; } public double getSuccessRate() { return totalScans > 0 ? (double) successfulScans / totalScans : 0.0; } } Example 4: Barcode Result Model
package com.barcodescanner.scanner; import com.google.zxing.BarcodeFormat; import java.awt.image.BufferedImage; import java.util.Date; public class BarcodeResult { private final String text; private final BarcodeFormat format; private final Date timestamp; private final BufferedImage image; private String type; private boolean validated; public BarcodeResult(String text, BarcodeFormat format, Date timestamp, BufferedImage image) { this.text = text; this.format = format; this.timestamp = timestamp; this.image = image; this.type = determineType(format); this.validated = validateBarcode(); } private String determineType(BarcodeFormat format) { switch (format) { case UPC_A: case UPC_E: case EAN_8: case EAN_13: return "Product"; case QR_CODE: return "QR Code"; case CODE_128: case CODE_39: case CODE_93: return "Code"; case PDF_417: return "PDF417"; case DATA_MATRIX: return "Data Matrix"; default: return "Unknown"; } } private boolean validateBarcode() { if (text == null || text.trim().isEmpty()) { return false; } // Basic validation based on format switch (format) { case EAN_13: return validateEAN13(text); case UPC_A: return validateUPC(text); case CODE_128: return text.length() >= 2; default: return true; } } private boolean validateEAN13(String code) { if (code.length() != 13 || !code.matches("\\d+")) { return false; } // EAN-13 checksum validation int sum = 0; for (int i = 0; i < 12; i++) { int digit = Character.getNumericValue(code.charAt(i)); sum += (i % 2 == 0) ? digit : digit * 3; } int checksum = (10 - (sum % 10)) % 10; return checksum == Character.getNumericValue(code.charAt(12)); } private boolean validateUPC(String code) { return code.length() == 12 && code.matches("\\d+"); } // Getters public String getText() { return text; } public BarcodeFormat getFormat() { return format; } public Date getTimestamp() { return timestamp; } public BufferedImage getImage() { return image; } public String getType() { return type; } public boolean isValidated() { return validated; } @Override public String toString() { return String.format("BarcodeResult{text='%s', format=%s, type=%s, validated=%s}", text, format, type, validated); } } GUI Components
Example 5: Camera Panel
package com.barcodescanner.gui; import com.barcodescanner.scanner.BarcodeResult; import com.barcodescanner.scanner.ScannerListener; import com.github.sarxos.webcam.WebcamPanel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; public class CameraPanel extends JPanel implements ScannerListener { private static final Logger logger = LoggerFactory.getLogger(CameraPanel.class); private WebcamPanel webcamPanel; private JLabel statusLabel; private BarcodeResult lastResult; public CameraPanel() { initializeComponents(); setupLayout(); } public void setWebcamPanel(WebcamPanel webcamPanel) { if (this.webcamPanel != null) { remove(this.webcamPanel); } this.webcamPanel = webcamPanel; if (webcamPanel != null) { add(webcamPanel, BorderLayout.CENTER); revalidate(); repaint(); } } private void initializeComponents() { setLayout(new BorderLayout()); setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); statusLabel = new JLabel("Camera not initialized"); statusLabel.setHorizontalAlignment(SwingConstants.CENTER); // Create placeholder when no camera is available JLabel placeholder = new JLabel("No Camera Available", JLabel.CENTER); placeholder.setForeground(Color.GRAY); placeholder.setFont(new Font("SansSerif", Font.BOLD, 16)); add(placeholder, BorderLayout.CENTER); } private void setupLayout() { add(statusLabel, BorderLayout.SOUTH); } @Override public void onBarcodeDetected(BarcodeResult result) { lastResult = result; statusLabel.setText("Barcode detected: " + result.getText()); statusLabel.setForeground(Color.GREEN); // Flash effect Timer timer = new Timer(500, e -> { statusLabel.setForeground(Color.BLACK); }); timer.setRepeats(false); timer.start(); // Optional: Display captured image displayCapturedImage(result.getImage()); } private void displayCapturedImage(BufferedImage image) { if (image != null) { // Could show a popup or overlay with the captured image logger.debug("Captured image for barcode: {}x{}", image.getWidth(), image.getHeight()); } } @Override public void onScannerStarted() { statusLabel.setText("Scanner active - looking for barcodes..."); statusLabel.setForeground(Color.BLUE); } @Override public void onScannerStopped() { statusLabel.setText("Scanner stopped"); statusLabel.setForeground(Color.BLACK); } @Override public void onError(String errorMessage) { statusLabel.setText("Error: " + errorMessage); statusLabel.setForeground(Color.RED); } public BarcodeResult getLastResult() { return lastResult; } } Example 6: Results Panel
package com.barcodescanner.gui; import com.barcodescanner.scanner.BarcodeResult; import com.barcodescanner.scanner.ScannerListener; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import javax.swing.table.DefaultTableModel; import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; public class ResultsPanel extends JPanel implements ScannerListener { private static final Logger logger = LoggerFactory.getLogger(ResultsPanel.class); private DefaultTableModel tableModel; private JTable resultsTable; private JTextArea rawTextArea; private List<BarcodeResult> results; private final String[] columnNames = { "Timestamp", "Type", "Format", "Content", "Valid", "Length" }; public ResultsPanel() { results = new ArrayList<>(); initializeComponents(); setupLayout(); } private void initializeComponents() { setLayout(new BorderLayout()); setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // Create table model and table tableModel = new DefaultTableModel(columnNames, 0) { @Override public boolean isCellEditable(int row, int column) { return false; // Make table read-only } }; resultsTable = new JTable(tableModel); resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); resultsTable.getSelectionModel().addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { showSelectedResult(); } }); JScrollPane tableScrollPane = new JScrollPane(resultsTable); tableScrollPane.setPreferredSize(new Dimension(400, 300)); // Raw text area rawTextArea = new JTextArea(10, 30); rawTextArea.setEditable(false); rawTextArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); JScrollPane textScrollPane = new JScrollPane(rawTextArea); // Create split pane JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); splitPane.setTopComponent(createTablePanel()); splitPane.setBottomComponent(createDetailsPanel()); splitPane.setDividerLocation(300); add(splitPane, BorderLayout.CENTER); add(createToolbar(), BorderLayout.NORTH); } private JPanel createTablePanel() { JPanel panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createTitledBorder("Scanned Barcodes")); JScrollPane scrollPane = new JScrollPane(resultsTable); panel.add(scrollPane, BorderLayout.CENTER); return panel; } private JPanel createDetailsPanel() { JPanel panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createTitledBorder("Barcode Details")); rawTextArea = new JTextArea(8, 30); rawTextArea.setEditable(false); rawTextArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); JScrollPane scrollPane = new JScrollPane(rawTextArea); panel.add(scrollPane, BorderLayout.CENTER); return panel; } private JPanel createToolbar() { JPanel toolbar = new JPanel(new FlowLayout(FlowLayout.LEFT)); JButton copyButton = new JButton("Copy Selected"); copyButton.addActionListener(e -> copySelectedToClipboard()); JButton exportButton = new JButton("Export All"); exportButton.addActionListener(e -> exportResults()); JButton clearButton = new JButton("Clear All"); clearButton.addActionListener(e -> clearResults()); toolbar.add(copyButton); toolbar.add(exportButton); toolbar.add(clearButton); return toolbar; } private void setupLayout() { // Layout is already set in initializeComponents } @Override public void onBarcodeDetected(BarcodeResult result) { results.add(result); SwingUtilities.invokeLater(() -> { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); Object[] rowData = { sdf.format(result.getTimestamp()), result.getType(), result.getFormat().toString(), result.getText(), result.isValidated() ? "Yes" : "No", result.getText().length() }; tableModel.addRow(rowData); // Auto-select the new row int lastRow = tableModel.getRowCount() - 1; resultsTable.setRowSelectionInterval(lastRow, lastRow); resultsTable.scrollRectToVisible(resultsTable.getCellRect(lastRow, 0, true)); }); } private void showSelectedResult() { int selectedRow = resultsTable.getSelectedRow(); if (selectedRow >= 0 && selectedRow < results.size()) { BarcodeResult result = results.get(selectedRow); displayResultDetails(result); } } private void displayResultDetails(BarcodeResult result) { StringBuilder sb = new StringBuilder(); sb.append("Timestamp: ").append(result.getTimestamp()).append("\n"); sb.append("Type: ").append(result.getType()).append("\n"); sb.append("Format: ").append(result.getFormat()).append("\n"); sb.append("Valid: ").append(result.isValidated() ? "Yes" : "No").append("\n"); sb.append("Length: ").append(result.getText().length()).append("\n\n"); sb.append("Content:\n").append(result.getText()); rawTextArea.setText(sb.toString()); rawTextArea.setCaretPosition(0); } private void copySelectedToClipboard() { int selectedRow = resultsTable.getSelectedRow(); if (selectedRow >= 0 && selectedRow < results.size()) { BarcodeResult result = results.get(selectedRow); StringSelection selection = new StringSelection(result.getText()); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(selection, null); JOptionPane.showMessageDialog(this, "Barcode content copied to clipboard", "Copy Successful", JOptionPane.INFORMATION_MESSAGE); } } public void exportResults() { if (results.isEmpty()) { JOptionPane.showMessageDialog(this, "No results to export", "Export", JOptionPane.WARNING_MESSAGE); return; } JFileChooser fileChooser = new JFileChooser(); fileChooser.setDialogTitle("Export Barcode Results"); fileChooser.setSelectedFile(new File("barcode_results.json")); int userSelection = fileChooser.showSaveDialog(this); if (userSelection == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); try { exportToJson(file); JOptionPane.showMessageDialog(this, "Results exported successfully to: " + file.getAbsolutePath(), "Export Successful", JOptionPane.INFORMATION_MESSAGE); } catch (IOException e) { logger.error("Failed to export results", e); JOptionPane.showMessageDialog(this, "Failed to export results: " + e.getMessage(), "Export Error", JOptionPane.ERROR_MESSAGE); } } } private void exportToJson(File file) throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); List<BarcodeExport> exportList = new ArrayList<>(); for (BarcodeResult result : results) { exportList.add(new BarcodeExport(result)); } mapper.writeValue(file, exportList); } public void clearResults() { int result = JOptionPane.showConfirmDialog(this, "Are you sure you want to clear all results?", "Clear Results", JOptionPane.YES_NO_OPTION); if (result == JOptionPane.YES_OPTION) { results.clear(); tableModel.setRowCount(0); rawTextArea.setText(""); } } @Override public void onScannerStarted() { // Not used } @Override public void onScannerStopped() { // Not used } @Override public void onError(String errorMessage) { // Not used } // Helper class for JSON export private static class BarcodeExport { public String content; public String format; public String type; public String timestamp; public boolean valid; public BarcodeExport(BarcodeResult result) { this.content = result.getText(); this.format = result.getFormat().toString(); this.type = result.getType(); this.timestamp = result.getTimestamp().toString(); this.valid = result.isValidated(); } } } Configuration System
Example 7: Application Configuration
package com.barcodescanner.config; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class AppConfig { private static final Logger logger = LoggerFactory.getLogger(AppConfig.class); // Application info public static final String APP_NAME = "Barcode Scanner"; public static final String APP_VERSION = "1.0.0"; // Default settings private static final String CONFIG_DIR = System.getProperty("user.home") + "/.barcodescanner"; private static final String CONFIG_FILE = CONFIG_DIR + "/config.json"; // Configurable properties private String webcamName; private int scanInterval = 100; // ms private boolean beepOnScan = true; private boolean autoCopyToClipboard = false; private boolean saveScannedImages = false; private String imageSavePath = CONFIG_DIR + "/images"; private String defaultExportFormat = "JSON"; private static AppConfig instance; private final ObjectMapper mapper; private AppConfig() { mapper = new ObjectMapper(); loadConfig(); } public static AppConfig getInstance() { if (instance == null) { instance = new AppConfig(); } return instance; } private void loadConfig() { try { Path configPath = Paths.get(CONFIG_FILE); if (Files.exists(configPath)) { AppConfig loaded = mapper.readValue(configPath.toFile(), AppConfig.class); copyFrom(loaded); logger.info("Configuration loaded from: {}", CONFIG_FILE); } else { logger.info("No configuration file found, using defaults"); } } catch (IOException e) { logger.warn("Failed to load configuration, using defaults", e); } } public void saveConfig() { try { // Create config directory if it doesn't exist Path configDir = Paths.get(CONFIG_DIR); if (!Files.exists(configDir)) { Files.createDirectories(configDir); } // Save configuration mapper.writerWithDefaultPrettyPrinter().writeValue(new File(CONFIG_FILE), this); logger.info("Configuration saved to: {}", CONFIG_FILE); } catch (IOException e) { logger.error("Failed to save configuration", e); } } private void copyFrom(AppConfig other) { this.webcamName = other.webcamName; this.scanInterval = other.scanInterval; this.beepOnScan = other.beepOnScan; this.autoCopyToClipboard = other.autoCopyToClipboard; this.saveScannedImages = other.saveScannedImages; this.imageSavePath = other.imageSavePath; this.defaultExportFormat = other.defaultExportFormat; } // Getters and Setters public String getWebcamName() { return webcamName; } public void setWebcamName(String webcamName) { this.webcamName = webcamName; } public int getScanInterval() { return scanInterval; } public void setScanInterval(int scanInterval) { this.scanInterval = scanInterval; } public boolean isBeepOnScan() { return beepOnScan; } public void setBeepOnScan(boolean beepOnScan) { this.beepOnScan = beepOnScan; } public boolean isAutoCopyToClipboard() { return autoCopyToClipboard; } public void setAutoCopyToClipboard(boolean autoCopyToClipboard) { this.autoCopyToClipboard = autoCopyToClipboard; } public boolean isSaveScannedImages() { return saveScannedImages; } public void setSaveScannedImages(boolean saveScannedImages) { this.saveScannedImages = saveScannedImages; } public String getImageSavePath() { return imageSavePath; } public void setImageSavePath(String imageSavePath) { this.imageSavePath = imageSavePath; } public String getDefaultExportFormat() { return defaultExportFormat; } public void setDefaultExportFormat(String defaultExportFormat) { this.defaultExportFormat = defaultExportFormat; } } Building and Running
Example 8: Maven Assembly Descriptor
<!-- src/assembly/package.xml --> <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd"> <id>package</id> <formats> <format>dir</format> <format>zip</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <fileSets> <fileSet> <directory>${project.basedir}/src/main/resources</directory> <outputDirectory>/</outputDirectory> </fileSet> <fileSet> <directory>${project.build.directory}</directory> <includes> <include>*.jar</include> </includes> <outputDirectory>/</outputDirectory> </fileSet> </fileSets> <dependencySets> <dependencySet> <outputDirectory>/lib</outputDirectory> <scope>runtime</scope> </dependencySet> </dependencySets> </assembly> Example 9: Launch Script
#!/bin/bash # barcode-scanner.sh APP_HOME="$(cd "$(dirname "$0")" && pwd)" JAVA_CMD="java" # Check if Java is installed if ! command -v $JAVA_CMD &> /dev/null; then echo "Java is not installed or not in PATH" exit 1 fi # Set JVM options JVM_OPTS="-Xmx512m -Dfile.encoding=UTF-8" # Find the main JAR MAIN_JAR=$(ls $APP_HOME/barcode-scanner-*.jar 2>/dev/null | head -n 1) if [ -z "$MAIN_JAR" ]; then echo "Main JAR file not found in $APP_HOME" exit 1 fi # Run the application echo "Starting Barcode Scanner..." $JAVA_CMD $JVM_OPTS -jar "$MAIN_JAR" "$@"
Features Summary
This Barcode Scanner Desktop App provides:
- Real-time Barcode Scanning - Live camera feed with continuous scanning
- Multiple Barcode Formats - Support for UPC, EAN, Code 128, QR codes, etc.
- Results Management - Table view with detailed information
- Export Capabilities - JSON export of scanned results
- Clipboard Integration - Copy barcode contents to clipboard
- Configurable Settings - Customizable scan interval, audio feedback, etc.
- Cross-platform - Runs on Windows, macOS, and Linux
- Modern UI - Clean, professional interface with Swing and FlatLaf
The application is extensible and can be enhanced with features like:
- Database integration for result storage
- Network sharing of scanned results
- Batch image scanning
- Barcode generation
- Advanced image processing filters
- Plugin system for custom barcode formats