Android

Leer código de barras con Android y ZXing (programación de app)

En este post voy a enseñarte cómo leer o escanear códigos de barras con Android usando la librería de Zebra Crossing (ZXing) con Java.

Además de que se permitirán leer códigos de barras también se podrán escanear códigos QR.

Lector de código de barras con Android

La app que vamos a programar será compatible con versiones de Android 4.4 y superiores; también vamos a manejar los permisos de acceso a la cámara en tiempo de ejecución y en el manifiesto.

Al final tendremos una app que lanza el lector de códigos de barras y devuelve el texto leído en forma de cadena.

Artículos recomendados

Anteriormente hicimos un lector de códigos QR con Android, míralo aquí. En esta app vamos a manejar los permisos en tiempo de ejecución para la cámara, así que mira el post en donde explicamos a fondo cómo solicitar permisos en tiempo de ejecución.

Código en GitHub y APK

A través del post explicaré los puntos más importantes, pero si quieres ver el código completo sobre cómo queda cada archivo mira el proyecto en mi GitHub.

También puedes probar la app descargándola dese el apartado releases. No te preocupes, no dañará tu dispositivo.

La librería

Como lo dije anteriormente, la librería será ZXing cuyo repositorio está aquí. Nosotros vamos a usar un “port” del usuario dm77 ya que facilita la integración de la librería agregando unos repositorios.

Para integrarla debemos modificar el archivo build.gradle que dice Module:app

Si no tiene el apartado repositories agrégalo y dentro de él coloca jcenter()

En el apartado dependencies agrega una línea que diga:

implementation 'me.dm7.barcodescanner:zxing:1.9.13'

Al final debe quedar como en la imagen (puede variar de acuerdo a tus dependencias):

Archivo gradle implementando barcodescanner zxing

Después de haber puesto  estas líneas seguramente se te pedirá que sincronices el proyecto:

Sincronizar proyecto

Si se sincroniza bien, podemos continuar. Si no, verifica que hayas colocado todo de manera correcta.

Permisos

Se necesitan los permisos para acceder a la cámara. En el manifiesto agrega la siguiente línea:

<uses-permission android:name="android.permission.CAMERA" />

Dentro de la actividad principal también vamos a solicitar permisos en tiempo de ejecución, pues son necesarios a partir de Android 6.

Solicitar permisos de acceso a la cámara para leer código de barras con Android

Actividad principal

En la actividad principal pondremos un botón que lanzará una actividad que leerá el código de barras y regresará lo que haya leído.

Actividad principal

Podemos integrar el lector de distintas maneras pero yo lo haré de forma que ocupe toda una actividad para separar conceptos y lógica.

Layout

El diseño queda así:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btnEscanear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:text="Escanear"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tvCodigoLeido"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btnEscanear"
        tools:text="Código leído" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:text="parzibyte.me"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvCodigoLeido" />
</android.support.constraint.ConstraintLayout>

Tiene un botón (Button) que lanza la actividad y dos TextView; en uno se muestra la dirección de mi sitio web y en el otro el código que se haya leído.

Código Java de actividad principal

En el código de Java simplemente agregamos listeners al botón, además de solicitar los permisos y sobrescribir a onAcitivityResult.

Este último método será llamado cuando se escanee el código y se finalice la otra actividad, pero trayendo resultados.

package me.parzibyte.leercodigosdebarras;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private static final int CODIGO_PERMISOS_CAMARA = 1, CODIGO_INTENT = 2;
    private boolean permisoCamaraConcedido = false, permisoSolicitadoDesdeBoton = false;
    private TextView tvCodigoLeido;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        verificarYPedirPermisosDeCamara();

        Button btnEscanear = findViewById(R.id.btnEscanear);
        tvCodigoLeido = findViewById(R.id.tvCodigoLeido);
        btnEscanear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!permisoCamaraConcedido) {
                    Toast.makeText(MainActivity.this, "Por favor permite que la app acceda a la cámara", Toast.LENGTH_SHORT).show();
                    permisoSolicitadoDesdeBoton = true;
                    verificarYPedirPermisosDeCamara();
                    return;
                }
                escanear();
            }
        });
    }

    private void escanear() {
        Intent i = new Intent(MainActivity.this, ActivityEscanear.class);
        startActivityForResult(i, CODIGO_INTENT);
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (requestCode == CODIGO_INTENT) {
            if (resultCode == Activity.RESULT_OK) {
                if (data != null) {
                    String codigo = data.getStringExtra("codigo");
                    tvCodigoLeido.setText(codigo);
                }
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case CODIGO_PERMISOS_CAMARA:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // Escanear directamten solo si fue pedido desde el botón
                    if (permisoSolicitadoDesdeBoton) {
                        escanear();
                    }
                    permisoCamaraConcedido = true;
                } else {
                    permisoDeCamaraDenegado();
                }
                break;
        }
    }

    private void verificarYPedirPermisosDeCamara() {
        int estadoDePermiso = ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA);
        if (estadoDePermiso == PackageManager.PERMISSION_GRANTED) {
            // En caso de que haya dado permisos ponemos la bandera en true
            // y llamar al método
            permisoCamaraConcedido = true;
        } else {
            // Si no, pedimos permisos. Ahora mira onRequestPermissionsResult
            ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.CAMERA},
                    CODIGO_PERMISOS_CAMARA);
        }
    }


    private void permisoDeCamaraDenegado() {
        // Esto se llama cuando el usuario hace click en "Denegar" o
        // cuando lo denegó anteriormente
        Toast.makeText(MainActivity.this, "No puedes escanear si no das permiso", Toast.LENGTH_SHORT).show();
    }
}

Gran parte de la lógica se centra en los permisos; si no los entiendes mira el post en donde se explican a fondo.

Cuando se hace click en el botón, si se han concedido los permisos, se lanza una nueva actividad con startActivityForResult.

Actividad para leer el código de barras

En esta actividad vamos a comenzar a escanear el código con la cámara del dispositivo. Cuando se logre escanear un código vamos a cerrar la actividad con el método finish pasándole a la actividad el texto que leímos.

Lector de código de barras con Android

Lo he hecho de esta manera para separar conceptos, pero podrían implementarse otras formas; todo es cuestión de tus necesidades.

Layout

El layout tiene únicamente un ConstraintLayout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ActivityEscanear">

</android.support.constraint.ConstraintLayout>

Código Java que lee el código de barras

Aquí es en donde la magia sucede. Manejamos los ciclos de vida de la actividad para informar al escaner que se detenga o resuma.

Estamos implementando la interfaz ZXingScannerView.ResultHandler así que debemos sobrescribir el método handleResult.

En palabras simples: cuando el código sea escaneado se pasará el resultado llamando a handleResult, y podremos obtener información a través del Result, por ejemplo, llamando a getText.

package me.parzibyte.leercodigosdebarras;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.google.zxing.Result;

import me.dm7.barcodescanner.zxing.ZXingScannerView;

public class ActivityEscanear extends AppCompatActivity implements ZXingScannerView.ResultHandler {
    private ZXingScannerView escanerZXing;

    @Override
    public void onCreate(Bundle state) {
        super.onCreate(state);
        escanerZXing = new ZXingScannerView(this);
        // Hacer que el contenido de la actividad sea el escaner
        setContentView(escanerZXing);
    }

    @Override
    public void onResume() {
        super.onResume();
        // El "manejador" del resultado es esta misma clase, por eso implementamos ZXingScannerView.ResultHandler
        escanerZXing.setResultHandler(this);
        escanerZXing.startCamera(); // Comenzar la cámara en onResume
    }

    @Override
    public void onPause() {
        super.onPause();
        escanerZXing.stopCamera(); // Pausar en onPause
    }

    // Estamos sobrescribiendo un método de la interfaz ZXingScannerView.ResultHandler
    @Override
    public void handleResult(Result resultado) {

        // Si quieres que se siga escaneando después de haber leído el código, descomenta lo siguiente:
        // Si la descomentas no recomiendo que llames a finish
//        escanerZXing.resumeCameraPreview(this);
        // Obener código/texto leído
        String codigo = resultado.getText();
        // Preparar un Intent para regresar datos a la actividad que nos llamó
        Intent intentRegreso = new Intent();
        intentRegreso.putExtra("codigo", codigo);
        setResult(Activity.RESULT_OK, intentRegreso);
        // Cerrar la actividad. Ahora mira onActivityResult de MainActivity
        finish();
    }
}

He dejado los comentarios necesarios. Cuando el lector escanea un código podemos seguir escaneando si lo requerimos, aunque yo lo detengo porque cierro la actividad.

Regresar a actividad principal

Después de escanear regresamos a la actividad principal y si todo va bien ponemos el código leído en un TextView:

Código de barras leído con Android y ZXing

Podríamos manejar el código de distintas maneras, pero lo he hecho simple. Una vez que lo tenemos como un String sus aplicaciones son infinitas.

Conclusión

Así es como podemos leer un código de barras con Android y la librería ZXing de una forma sencilla. La librería proporciona más métodos que puedes leer en la documentación oficial.

Intenté hacer este post lo más sencillo completo, si tienes dudas puedes dejarlas en los comentarios.

Te invito a leer más sobre Android aquí.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

  • Hola, tengo una webapp que hace esto mismo.
    El problema es que al generar un apk y luego abrirlo desde el celular, al momento de abrir la camara para el escaneo del código en lugar de aparecer el video me aparece una imagen como de reproductor (cuando no se encuentra el video).
    Estimo que la web que la web que utilizo para generar el apk no habilita los permisos de la cam. Alguna idea para solucionar esto?
    Conocés alguna web que genere un apk y que de la opción de habilitar permisos?
    Gracias de antemano!

  • Muchas gracias por el aporte. No he logrado aún hacer funcionar la app... tengo algún error de casteo o con la librería... no lo sé...

  • Gracias por este magnifico ejemplo.

    Yo había implementado un escaner de códigos de barra usando la misma librería, pero si quería cancelar el escaneo no podía, incluso al usar el botón back la app se cerraba en lugar de regresar a la activity que quería.

    Con tu ejemplo obtuve ideas para solucionar mi problema, muchas gracias.

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

1 semana hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.