Implementierung der Barcode API – Barcode Scanner für Android
QR-Code-Scanner oder Barcode-Scanner für Android sind in vielen Apps vorhanden, um nützliche Daten zu lesen. In diesem Tutorial werden wir die Barcode API diskutieren und implementieren, die in der Google Mobile Vision API vorhanden ist.
Implementierung der Barcode API – Barcode-Scanner für Android
Mit der Einführung der Google Vision API ist die Implementierung von Barcodes in einer Anwendung für Entwickler viel einfacher geworden. Folgend sind die Hauptformate, die die Vision API unterstützt.
- 1D-Barcodes: EAN-13, EAN-8, UPC-A, UPC-E, Code-39, Code-93, Code-128, ITF, Codabar
- 2D-Barcodes: QR-Code, Data Matrix, PDF-417, AZTEC
Barcodes können Dinge wie URL, Kontaktinformationen bis hin zu Geolokation, WIFI, Führerschein-IDs scannen. QR-Code ist das populärere Format und wird häufig in vielen Anwendungen gesehen. Im Folgenden werden wir eine Anwendung entwickeln, die den QR-Code-Wert aus einem Bitmap-Bild scannt sowie QR-Codes über eine Kamera erkennt und die entsprechenden Aktionen durchführt.
Implementierung der Barcode API – Konfiguration von Android Studio für die Barcode-Bibliothek
Fügen Sie Folgendes in die Datei build.gradle ein.
implementation 'com.google.android.gms:play-services-vision:11.8.0'
Fügen Sie Folgendes in den Application-Tag der Datei AndroidManifest.xml ein, um die Barcode-Erkennung in Ihrer Anwendung zu ermöglichen.
<meta-data
android:name="com.google.android.gms.vision.DEPENDENCIES"
android:value="barcode" />
QR-Code-Scanner aus Bild
Der Code für die Layout-Datei activity_main.xml ist unten angegeben.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<Button
android:id="@+id/btnTakePicture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/take_barcode_picture" />
<Button
android:id="@+id/btnScanBarcode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btnTakePicture"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/activity_horizontal_margin"
android:text="@string/scan_barcode" />
</RelativeLayout>
Der Code für die MainActivity.java ist unten angegeben.
package com.journaldev.barcodevisionapi;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button btnTakePicture, btnScanBarcode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
private void initViews() {
btnTakePicture = findViewById(R.id.btnTakePicture);
btnScanBarcode = findViewById(R.id.btnScanBarcode);
btnTakePicture.setOnClickListener(this);
btnScanBarcode.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnTakePicture:
startActivity(new Intent(MainActivity.this, PictureBarcodeActivity.class));
break;
case R.id.btnScanBarcode:
startActivity(new Intent(MainActivity.this, ScannedBarcodeActivity.class));
break;
}
}
}
In der MainActivity.java gibt es zwei Schaltflächen. Die erste startet eine Aktivität, die nach einem QR-Code im von der Kamera aufgenommenen Bitmap-Bild scannt und die darin enthaltenen Daten zurückgibt (falls vorhanden). Die zweite scannt nach dem QR-Code und erkennt ihn in Echtzeit. Bevor wir zur Geschäftslogik der Anwendung übergehen, müssen wir die folgenden Berechtigungen in die Datei AndroidManifest.xml einfügen.
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
Um die von anderen Anwendungen erstellten Dateien zu teilen und darauf zuzugreifen, müssen wir den folgenden Provider-Tag in unseren Application-Tag in der Datei AndroidManifest.xml einfügen.
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
Dies ist erforderlich, um das von der Kamera aufgenommene Bild in unserer QR-Code-Scanner-Anwendung für Android abzurufen. Beginnen wir mit der ersten, nämlich der PictureBarcodeActivity.java. Der Code für das XML-Layout activity_barcode_picture.xml ist unten angegeben.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<ImageView
android:id="@+id/imageView"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerHorizontal="true"
android:src="@mipmap/journaldev_logo" />
<TextView
android:id="@+id/txtResultsHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/imageView"
android:layout_centerHorizontal="true"
android:text="Results"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txtResultsBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/txtResultsHeader"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/activity_horizontal_margin"
android:gravity="center" />
<Button
android:id="@+id/btnOpenCamera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/activity_horizontal_margin"
android:layout_marginTop="@dimen/activity_horizontal_margin"
android:text="@string/open_camera" />
</RelativeLayout>
Der Code für die Klasse PictureCodeActivity.java ist unten angegeben.
package com.journaldev.barcodevisionapi;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.vision.Frame;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
import java.io.File;
import java.io.FileNotFoundException;
public class PictureBarcodeActivity extends AppCompatActivity implements View.OnClickListener {
Button btnOpenCamera;
TextView txtResultBody;
private BarcodeDetector detector;
private Uri imageUri;
private static final int REQUEST_CAMERA_PERMISSION = 200;
private static final int CAMERA_REQUEST = 101;
private static final String TAG = "API123";
private static final String SAVED_INSTANCE_URI = "uri";
private static final String SAVED_INSTANCE_RESULT = "result";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barcode_picture);
initViews();
if (savedInstanceState != null) {
if (imageUri != null) {
imageUri = Uri.parse(savedInstanceState.getString(SAVED_INSTANCE_URI));
txtResultBody.setText(savedInstanceState.getString(SAVED_INSTANCE_RESULT));
}
}
detector = new BarcodeDetector.Builder(getApplicationContext())
.setBarcodeFormats(Barcode.DATA_MATRIX | Barcode.QR_CODE)
.build();
if (!detector.isOperational()) {
txtResultBody.setText("Detector initialisation failed");
return;
}
}
private void initViews() {
txtResultBody = findViewById(R.id.txtResultsBody);
btnOpenCamera = findViewById(R.id.btnTakePicture);
txtResultBody = findViewById(R.id.txtResultsBody);
btnOpenCamera.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnTakePicture:
ActivityCompat.requestPermissions(PictureBarcodeActivity.this, new
String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
break;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_CAMERA_PERMISSION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
takeBarcodePicture();
} else {
Toast.makeText(getApplicationContext(), "Permission Denied!", Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_REQUEST && resultCode == RESULT_OK) {
launchMediaScanIntent();
try {
Bitmap bitmap = decodeBitmapUri(this, imageUri);
if (detector.isOperational() && bitmap != null) {
Frame frame = new Frame.Builder().setBitmap(bitmap).build();
SparseArray barcodes = detector.detect(frame);
for (int index = 0; index < barcodes.size(); index++) {
Barcode code = barcodes.valueAt(index);
txtResultBody.setText(txtResultBody.getText() + "\n" + code.displayValue + "\n");
int type = barcodes.valueAt(index).valueFormat;
switch (type) {
case Barcode.CONTACT_INFO:
Log.i(TAG, code.contactInfo.title);
break;
case Barcode.EMAIL:
Log.i(TAG, code.displayValue);
break;
case Barcode.ISBN:
Log.i(TAG, code.rawValue);
break;
case Barcode.PHONE:
Log.i(TAG, code.phone.number);
break;
case Barcode.PRODUCT:
Log.i(TAG, code.rawValue);
break;
case Barcode.SMS:
Log.i(TAG, code.sms.message);
break;
case Barcode.TEXT:
Log.i(TAG, code.displayValue);
break;
case Barcode.URL:
Log.i(TAG, "url: " + code.displayValue);
break;
case Barcode.WIFI:
Log.i(TAG, code.wifi.ssid);
break;
case Barcode.GEO:
Log.i(TAG, code.geoPoint.lat + ":" + code.geoPoint.lng);
break;
case Barcode.CALENDAR_EVENT:
Log.i(TAG, code.calendarEvent.description);
break;
case Barcode.DRIVER_LICENSE:
Log.i(TAG, code.driverLicense.licenseNumber);
break;
default:
Log.i(TAG, code.rawValue);
break;
}
}
if (barcodes.size() == 0) {
txtResultBody.setText("No barcode could be detected. Please try again.");
}
} else {
txtResultBody.setText("Detector initialisation failed");
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Failed to load Image", Toast.LENGTH_SHORT)
.show();
Log.e(TAG, e.toString());
}
}
}
private void takeBarcodePicture() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File photo = new File(Environment.getExternalStorageDirectory(), "pic.jpg");
imageUri = FileProvider.getUriForFile(PictureBarcodeActivity.this,
BuildConfig.APPLICATION_ID + ".provider", photo);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, CAMERA_REQUEST);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
if (imageUri != null) {
outState.putString(SAVED_INSTANCE_URI, imageUri.toString());
outState.putString(SAVED_INSTANCE_RESULT, txtResultBody.getText().toString());
}
super.onSaveInstanceState(outState);
}
private void launchMediaScanIntent() {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(imageUri);
this.sendBroadcast(mediaScanIntent);
}
private Bitmap decodeBitmapUri(Context ctx, Uri uri) throws FileNotFoundException {
int targetW = 600;
int targetH = 600;
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(ctx.getContentResolver().openInputStream(uri), null, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
return BitmapFactory.decodeStream(ctx.getContentResolver()
.openInputStream(uri), null, bmOptions);
}
}
Einige Schlussfolgerungen aus dem oben genannten Code sind unten aufgeführt.
SurfaceView eignet sich gut zur Anzeige von Kameravorschaubildern, da es die GUI schnell rendert. Die Schnittstelle SurfaceHolder.Callback wird verwendet, um Informationen über Änderungen zu erhalten, die auf der Oberfläche auftreten (in diesem Fall die Kameravorschau). SurfaceHolder.Callback implementiert drei Methoden:
- SurfaceChanged: Diese Methode wird aufgerufen, wenn sich die Größe oder das Format der Oberfläche ändert.
- SurfaceCreated: Wenn die Oberfläche zum ersten Mal erstellt wird, wird diese Methode aufgerufen.
- SurfaceDestroyed: Diese wird aufgerufen, wenn die Oberfläche zerstört wird.
CameraSource verwaltet die Kamera in Verbindung mit einem zugrunde liegenden Detektor. Hier ist SurfaceView der zugrunde liegende Detektor. CameraSource.start() öffnet die Kamera und beginnt, Vorschaubilder an das SurfaceView zu senden. CameraSource wird folgendermaßen erstellt:
cameraSource = new CameraSource.Builder(this, barcodeDetector)
.setRequestedPreviewSize(1920, 1080)
.setAutoFocusEnabled(true) //you should add this feature
.build();
Wir haben einen Prozessor auf dem Barcode-Detektor mit setProcessor() zugewiesen. Die Schnittstelle enthält den Rückruf an die Methode receiveDetections(), die den QR-Code aus der Kameravorschau empfängt und sie in das SparseArray hinzufügt.
Der Wert des QR-Codes wird in der TextView mit einem Runnable angezeigt, da die Barcodes in einem Hintergrundthread erkannt werden.
In diesem Beispiel haben wir zwei Barcodes mit dem Generator erstellt. Einer enthält die URL. Der zweite enthält eine E-Mail-Adresse. Wenn Sie auf die Schaltfläche klicken, starten wir basierend auf dem erkannten QR-Code-Wert entweder die URL oder senden eine E-Mail an die erkannte relevante E-Mail-Adresse aus dem QR-Code.
Das ist alles für das QR-Code-Scanner-Projekt für Android mit der Mobile Vision API.