# Como ler um QR Code ou código de barras

> Escanear QR/código de barras com a câmera usando BarcodePicker, ou decodificar uma Image com CameraBarcode.

Source: https://crossfly.dev/pt/docs/cookbook/device/ler-codigo-de-barras — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Como ler um QR Code ou código de barras

**Propósito.** Abrir a câmera e ler um **QR Code** ou **código de barras** ao vivo com o
`BarcodePicker`, ou decodificar um código a partir de uma `Image` já capturada com `CameraBarcode`.

## APIs usadas
- `BarcodePicker(String title, BarcodeFormat... formats)` — janela **abstrata** de leitura ao vivo; você **estende** e implementa `onBarcode(BarcodeFormat, String)`. `formats` vazio = aceita todos.
- `abstract void onBarcode(BarcodeFormat format, String code)` — chamado a cada código novo decodificado que passa no filtro de formatos.
- `BarcodePicker.formats1D` / `formats2D` (arrays estáticos) — conjuntos prontos de formatos; `formats2D` contém `QR_CODE`.
- `BarcodePicker.beep` / `BarcodePicker.vibrate` (estáticos `boolean`) — feedback ao decodificar (default `true`).
- `String getAnswer()` — último código decodificado, ou `null` se cancelado.
- `CameraBarcode.readBarcode(Image img)` → `com.google.zxing.Result` (ou `null`) — decodifica de uma imagem estática. **NÃO é thread-safe.**

> **Divergência importante.** `BarcodePicker` **é `abstract`** e usa o método `onBarcode(...)`, não um
> callback do tipo `RunnableWith`. Instancie uma subclasse (anônima ou nomeada). O picker já emite
> `beep()`/`vibrate(20)` ao decodificar e **suprime códigos repetidos** (só chama `onBarcode` quando o
> texto muda). Ele **não fecha sozinho** ao ler — chame `hide()` dentro de `onBarcode` se quiser
> encerrar na primeira leitura. Toque no "X" (canto inferior direito) cancela.

## Exemplo completo (copiar, colar e rodar)

```java
package com.exemplo;

import com.google.zxing.*;                  // BarcodeFormat
import dev.crossfly.ui.*;                   // Window, Component, Button, Label
import dev.crossfly.ui.layout.*;            // VerticalLayout
import dev.crossfly.ui.picker.*;            // BarcodePicker

/** Tela principal executável. Aponte o launcher (classname) e rode: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Label resultado = new Label("");
      add(LAY_AUTO, resultado);

      Button acao = new Button("Ler QR Code");
      acao.onAction(c ->
      {
         // Leitura AO VIVO — só QR Code; fecha na primeira leitura
         BarcodePicker picker = new BarcodePicker("Aponte para o QR", BarcodePicker.formats2D)
         {
            protected void onBarcode(BarcodeFormat format, String code)
            {
               // Chamado FORA da paint thread → volte à UI com onUI(...)
               Component.onUI(() ->
               {
                  resultado.setText("Lido: " + code);
                  hide();            // encerra o scanner na primeira leitura (opcional)
               });
            }
         };
         picker.show();
      });
      add(LAY_AUTO, acao);
   }
}
```

> O trecho abaixo mostra **só o miolo**. Para rodar, coloque-o no esqueleto `MainWindow` acima.

## Trecho mínimo

```java
// verificado na fonte: BarcodePicker.java (constructor, onBarcode, formats2D, getAnswer); CameraBarcode.java (readBarcode)
import com.google.zxing.*;                 // BarcodeFormat, Result
import dev.crossfly.ui.picker.*;           // BarcodePicker
import dev.crossfly.ui.*;                  // Component (onUI)

// 1) Leitura AO VIVO — só QR Code; fecha na primeira leitura
BarcodePicker picker = new BarcodePicker("Aponte para o QR", BarcodePicker.formats2D) {
   protected void onBarcode(BarcodeFormat format, String code) {
      // Chamado FORA da paint thread → volte à UI com onUI(...)
      Component.onUI(() -> {
         processar(code);   // ex.: preencher um campo, navegar, etc.
         hide();            // encerra o scanner na primeira leitura (opcional)
      });
   }
};
picker.show();             // deixe os formats vazios no construtor p/ aceitar todos os formatos

// 2) Decodificar de uma Image estática (uma câmera/thread por vez — não é thread-safe)
Result r = CameraBarcode.readBarcode(minhaImagem);
if (r != null) {
   String texto = r.getText();
   BarcodeFormat fmt = r.getBarcodeFormat();
}
```

## Aprofunde-se
- Guia da câmera: [Câmera do dispositivo](/docs/data-and-services/device-camera) · Widget: [BarcodePicker](/docs/ui/widgets/barcodepicker)
- Receita irmã: [tirar-foto.md](/docs/cookbook/device/tirar-foto)
