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.
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):
Después de haber puesto estas líneas seguramente se te pedirá que sincronices el 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.
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.
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.
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:
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í.
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!
Hola. Si tiene dudas puede enviarme un mensaje en https://parzibyte.me/#contacto
Cómo se supone que hago la foto, no veo el botón?
Es automático
Sí
Hola. ¿Por qué debería existir un botón para tomar una foto? se está leyendo un código de barras, no tomando una foto. Le invito a descargar la demo para entender a lo que me refiero
Saludos 🙂
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é…
Como seria para hacerlo en fragments?
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.
Es un honor ayudar. No olvides seguirme y compartir 🙂
Saludos