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