# BarcodePicker

> Lectura de códigos de barras/QR mediante la cámara (clase abstracta).

Source: https://crossfly.dev/es/docs/ui/widgets/barcodepicker — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# BarcodePicker

`BarcodePicker` lee **códigos de barras/QR** a partir de fotogramas de la cámara en vivo, filtrando por formato. Es una **clase abstracta**: el consumidor implementa `onBarcode(BarcodeFormat format, String code)`. Sigue el patrón de los pickers (ver [`Picker`](/docs/ui/widgets/picker)) — devuelve el payload leído vía `getAnswer()`.

> 🔴 · **Paquete:** `dev.crossfly.ui.picker` · **Extiende:** `Picker implements GdxCamera.Listener` (abstracta) · **Hardware:** cámara

> ℹ️ Este `BarcodePicker` es la **lectura por cámara** y sigue existiendo. No confundir con la antigua utilidad de *generación* de barcode (`util/barcode`), que fue eliminada en la modernización.

## Cuándo usar
- Escanear códigos de barras (productos, entradas) o códigos QR.

## Cuándo NO usar
- Tomar foto/grabar vídeo → usa [`PhotoPicker`](/docs/ui/widgets/photopicker).

## Apariencia
Vista previa de la cámara a pantalla completa (fondo negro), título arriba, un **crosshair rojo** central de mira y una "X" blanca de cerrar en la esquina inferior derecha.

## Ejemplo completo (copiar, pegar y ejecutar)
`BarcodePicker` es **abstracto**: instáncialo con un cuerpo que implemente `onBarcode`. Como es un overlay de cámara, dispáralo desde un `Button`. El ejemplo reutiliza el uso básico (formatos 1D, cierra en la 1ª lectura) y escribe el payload en un `Label`. `BarcodeFormat` proviene de `com.google.zxing`.

```java
package com.exemplo;

import com.google.zxing.BarcodeFormat;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.picker.*;

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Label saida = new Label("Nenhum código lido");
      add(LAY_AUTO, saida);

      Button escanear = new Button("Escanear código")
         .onAction(c ->
         {
            BarcodePicker scanner = new BarcodePicker("Aponte para o código", BarcodePicker.formats1D)
            {
               protected void onBarcode(BarcodeFormat format, String code)
               {
                  onUI(() -> saida.setText(format + ": " + code)); // vuelve al paint thread
                  hide(); // cierra tras la 1ª lectura (no se cierra solo)
               }
            };
            scanner.show(answer -> {});
         });
      add(LAY_AUTO, escanear);
   }
}
```

> Los fragmentos siguientes muestran **solo el núcleo** (lo que va dentro de `onShow()`). Para ejecutarlos, colócalos en el esqueleto `MainWindow` de arriba.

## Uso básico
```java
BarcodePicker scanner = new BarcodePicker("Aponte para o código", BarcodePicker.formats1D)
{
   protected void onBarcode(BarcodeFormat format, String code)
   {
      Debug.log(format + ": " + code);
      hide(); // cierra tras la 1ª lectura
   }
};
scanner.show(answer -> campoCodigo.setText((String) answer));
```

## Constructores
| Constructor | Descripción |
|---|---|
| `BarcodePicker(String title, BarcodeFormat... formats)` | Título + formatos aceptados (varargs; vacío = todos). Abstracto — instáncialo con un cuerpo que implemente `onBarcode`. |

## Propiedades y personalización
| Miembro | Tipo | Por defecto | Qué hace |
|---|---|---|---|
| `BarcodePicker.formats1D` (estático) | `BarcodeFormat[]` | — | Conjunto listo de formatos 1D (CODE_128, EAN_13…). |
| `BarcodePicker.formats2D` (estático) | `BarcodeFormat[]` | — | Conjunto listo de formatos 2D (QR_CODE). |
| `BarcodePicker.beep` (estático) | `boolean` | `true` | Pita al decodificar. |
| `BarcodePicker.vibrate` (estático) | `boolean` | `true` | Vibra al decodificar. |

## Callbacks y eventos
| Callback | Cuándo se dispara |
|---|---|
| `onBarcode(BarcodeFormat, String)` | (abstracto) al decodificar un código del formato aceptado. |
| `show(onAnswer)` | al cerrar, entrega la `String` del último payload leído. |

`getAnswer()` devuelve el último payload decodificado, o `null` si se canceló.

## Estados
- **No hay `hide()` automático al detectar** — quien decide cerrar es tu `onBarcode` (llamando a `hide()`). Sin eso, el scanner sigue leyendo (útil para lectura continua).
- Los códigos repetidos consecutivos (mismo `lastCode`) se ignoran.
- `onAppPause`/`onAppResume` gestionan la cámara; `onRemove` detiene el hilo de decodificación.

### Aceptar solo QR y cerrar en la primera lectura
```java
BarcodePicker scanner = new BarcodePicker("Leia o QR", BarcodePicker.formats2D)
{
   protected void onBarcode(BarcodeFormat format, String code)
   {
      onUI(() -> campoCodigo.setText(code)); // actualización de UI en la paint thread
      hide();
   }
};
scanner.show(answer -> Debug.log("Lido: " + answer));
```

## Buenas prácticas
- Llama a `hide()` en `onBarcode` para lectura única; omítelo para lectura continua.
- La decodificación se ejecuta en un hilo de fondo — envuelve las actualizaciones de UI en `onUI(...)`.

## Errores comunes
- **Olvidar `hide()`** cuando querías lectura única — el scanner sigue activo.
- **Actualizar la UI directamente en `onBarcode`** — respeta la paint thread con `onUI(...)`.

## Ver también
- Guía del área: [`ui/`](/docs/ui) · Recetas: [`cookbook/`](/docs/cookbook)
- Base del patrón: [`Picker`](/docs/ui/widgets/picker)
- Componentes relacionados: [`PhotoPicker`](/docs/ui/widgets/photopicker)
