Как написать программу для сканера штрих кода

Ни для кого не секрет, что промышленное ПО для терминалов сбора данных (ТСД) пишется для автоматизации бизнес процессов, в особенности — складских. Большая часть задач, которые необходимо решить с помощью ТСД, связана со штрихкодированием, благо сканер штрихкода в девайс встроен.
В этом статье пойдет речь о том, как начать писать программы для терминалов сбора данных, и как подключить и использовать сканер штрихкода.

Мой подопытный образец Motorola MC3190 обладает лазерным сканером штрихкода. (В ближайшее время планирую плотно заняться Datalogic c имэйджером).

Кому интересно, можете ознакомиться со спецификацией.

На девайсе установлена Windows CE, соответственно и писать будет необходимо под нее. И если Вы никогда не пробовали писать под ТСД, конечно же, начать стоит с Hello World. Чтобы запустить приложение, написанное на C# на сем устройстве, необходимо установить на него .NET 3.5 Compact. А так же иметь под рукой Visual Studio 2008, так как во всех последующих релизах вариант проекта Smart Device отсутствует (мелкомягкие любят навязывать свои новые технологии, которые, к сожалению, не всегда совместимы с ранними версиями их же операционной системы).

Итак, создав простенькое приложение, можно попробовать его сразу же запустить. Если устройство подключено к компьютеру, то студия сразу же предложит выполнить запуск непосредственно на нем, с возможностью отладки, разумеется. Подождав несколько минут можно увидеть запуск формочки на устройстве.


Вот теперь можно приступать к самому интересному.

Для того чтобы использовать объектную модель, уже реализованную в C#, Motorola подготовила целый ряд библиотек. Фактически с помощью них можно творить все, что нам вздумается. Называется это чудо Symbol. В нашем случае будет использована Symbol.Barcode. К сожалению, в данный момент официальный сайт библиотеки symbol.com перенаправляет нас на motorolasolutions.com, сообщая о том, что технология устарела. Но я в свое время сделал копии всех Reference с примерами:

cloud.mail.ru/public/6716403f96ac%2FSymbolReference.7z
cloud.mail.ru/public/c48120476007%2FSymbolExample.7z

В примерах, те, кому интересно, конечно могут покопаться, но я могу сразу предупредить, что есть статьи, в которых описано все гораздо проще и удобнее, на мой взгляд, лучшая среди них: kbss.ru/blog/dotnetcf/178.html. Но, тем не менее, я считаю, что каждый раз подключать так сканер штрихкода не очень удобно, особенно если приложение планируется многооконным. К тому же в некоторых случаях могут возникнуть проблемы со считыванием, при возвращении фокуса в родительское окно от дочернего.

Из всего, что можно найти в интернете, главное осознать то, что самым удобным способом обработки штрихкодов является модель, построенная на событиях:
1. Сработал сканер штрихкода.
2. Распознал штрихкод корректно.
3. Мы вызываем событие обработки этого штрихкода у себя в программе, уже разобранного и удобного для восприятия.

Компонент формы — паттерн, позволяющий добавлять свой контрол в конструктор форм студии. Этим и воспользуемся. И не только потому, что это упростит разработку форм, а еще и потому, что в дальнейшем компоненту можно будет доработать так, чтобы она поддерживала сканеры не только от Motorol`ы.

Затем надо описать класс компоненты.

public partial class BarcodeReader : Component
    {
        private Symbol.Barcode.BarcodeReader barcodeReader = null;

        public delegate void OnReadBarcodeReaderEventHandler(string Text);
        public event OnReadBarcodeReaderEventHandler OnRead;

        public BarcodeReader()
        {
            InitializeComponent();
            InitializeBarcodeReader();
        }

        public BarcodeReader(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
            InitializeBarcodeReader();
        }

        private void InitializeBarcodeReader()
        {
            this.barcodeReader = new Symbol.Barcode.BarcodeReader();
            this.barcodeReader.ListChanged += new System.ComponentModel.ListChangedEventHandler(BarcodeReader_ListChanged);
            this.barcodeReader.Start();

            this.components.Add(this.barcodeReader);
        }

        void BarcodeReader_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
        {
            if (this.barcodeReader.ReaderData.Result == Symbol.Results.SUCCESS)
            {
                OnRead(this.barcodeReader.ReaderData.Text);
            }
        }
    }

На что здесь надо обратить внимание:
1. Любое событие, которые добавляется в компоненте, становится доступно в конструкторе формы.
2. На основании IContainer container в конструкторе форм реализован механизм высвобождения памяти, в том числе и unmanage объектов. Соответственно грех этим не воспользоваться. Добавляем в контейнер объект считывателя штрихкода, и память высвободится рекурсивно, при закрытии формы, в которой содержится описываемая здесь компонента.
//
О том, как это происходит можно понять открыв designer любой из форм, созданной в конструкторе, и посмотрев на перегрузку:

        private System.ComponentModel.IContainer components = null;
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

//
3. Чуть-чуть переопределяем событие считывания, избавив себя от обработки считанных с ошибками данных.

Дальше дело за малым: перетащить компоненту на форму:

И добавить событие обработки штрихкода:


Убедиться в работе более чем достаточно следующего кода:

  private void barcodeReader1_OnRead(string Text)
        {
            MessageBox.Show(Text);
        }

Получилось несколько сумбурно, и очень много скриншотов. Повторив вышеперечисленные шаги вы освоите сразу три вещи:

1. Создавать и запускать в режиме отладки программы под терминалы сбора данных.
2. Разберетесь как работать со сканером штрихкода.
3. Научитесь создавать компоненты форм.

Я надеюсь эта статья поможет тем, кто только начал разбираться с программированием под ТСД, и несколько упростит этим людям жизнь.

github example

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

А вы пишите под ТСД?


13.72%
Я вообще не программист
45

Проголосовали 328 пользователей.

Воздержались 60 пользователей.

[wpanchor id=”1″]В этом уроке научимся создавать приложение для чтения штрих-кодов и QR-кодов с использованием стандартной библиотеки Mobile Vision API.

Еще с выпуском Google Play services версии 7.8 разработчики добавили интерфейсы Mobile Vision, которые обеспечивают API для обнаружения штрих-кода. Они считывают и декодируют множество различных типов штрих-кодов, быстро, легко и локально.

Классы для обнаружения и анализа штрих-кодов доступны в пространстве имен com.google.android.gms.vision.barcode. Основной рабочей лошадкой является класс BarcodeDetector. Он выполняет обработку объектов Frame и возвращает массив штрих-кодов SparseArray <Barcode>.

Тип Barcode представляет собой единый общепризнанный штрих-код и его значение. В случае 1D штрих-кодов, таких как коды UPC, это будет просто номер, который закодирован в штрих-коде. Его значение доступно в поле rawValue, в то время как тип штрих-кода (то есть его кодировку) можно найти в поле format.

Для 2D штрих-кодов, которые содержат структурированные данные, такие как QR-коды — в поле valueFormat устанавливается определенный тип значения, соответствующего полю данных. Так, например, если обнаружен тип URL , то поле valueFormat вернет константу URL, а объект Barcode.UrlBookmark будет содержать значение URL-адреса. Помимо URL-адресов, существует множество различных типов данных, которые QR-код может хранить. Например, почтовый адрес, дату и время события календаря, мероприятие в календаре, информацию контакта, номер телефона, местоположение на карте и другие данные, полный список которых приводится в документации. Ссылки на документацию здесь.

Использование в приложении Mobile Vision API позволяет считывать штрих-коды в любом положении.

Важно отметить, что синтаксический разбор всех штрих-кодов выполняется локально, поэтому вам не нужно использовать соединение с сервером для чтения данных из кода. Например, при считывании линейного штрих-кода PDF-417, который может вместить до 1 КБ текста, можно сразу же получить всю закодированную в нем информацию.

Итак, для разработки приложения нам понадобится:

  • Среда разработки Android Studio
  • Смартфон на Android 4.2.2 или более поздней версии
  • Последняя версия Android SDK, включая компонент SDK tools. Вы можете получить его с помощью Android SDK Manager в Android Studio.
  • Google Play Services SDK. Вы можете получить его также в Android SDK Manager в Android Studio.

Создаем новый проект в Android Studio. При создании выбираем шаблон Empty Activity.

2016-11-09_21-56-54

На следующем шаге нужно убедиться, что ваше приложение может использовать службы Google Play, в состав которых входит Mobile Vision API. Для этого нужно обновить файл build.gradle вашего проекта.

В секции зависимостей должны быть такие строки. Обновите Gradle при необходимости.

compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.google.android.gms:play-services:9.8.0'

Службы Google Play часто обновляются, и чтобы получить последнюю версию, в Android Studio выберите инструменты > Android > SDK Manager.

Затем найдите строчку для сервисов Google Play и убедитесь, что у вас установлена версия 26 и выше. Если нет – установите компонент.

2016-11-09_21-58-42

Теперь создадим пользовательский интерфейс.

В Android Studio выберите папку «res» и откройте ее вложенную папку «layout». Здесь вы увидите «activity_main.xml». Откройте его в редакторе макетов.

Вы можете видеть, что ваш макет содержит текстовое поле <TextView>. Нужно изменить макет, как показано ниже. Теперь здесь будет кроме текстового поля также кнопка и изображение. Для всех экранных компонентов прописываем идентификаторы, чтобы потом обращаться к ним в коде.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:orientation="vertical"
    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"
    tools:context="info.fandroid.barcodedetection.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:id="@+id/txtContent"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:text="Process"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"/>
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imgview"/>
</LinearLayout>

По нажатию на кнопку будет происходить загрузка и обработка изображения штрих-кода, которое будет отображаться в ImageView. После завершения обработки штрих-кода информация, считанная из него, будет отображаться в TextView.

Обычно приложения для считывания штрих-кодов получают изображение с камеры устройства, или обрабатывают превью камеры. Для реализации этого потребуется достаточно много кода, и в конце урока я покажу пример реализации такого приложения. Чтобы упростить этот пример, мы обработаем готовое изображение qr-кода, которое уже присутствует в вашем приложении.

Вот пример изображения qr-кода, которое вы можете скачать отсюда.

qr

Назовите его qr.png и добавьте в папку проекта res/drawable.Android Studio обеспечит доступ к файлу в качестве ресурса с идентификатором: R.drawable.qr

Теперь перейдем к написанию кода приложения.

В файле MainActivity.java в методе onCreate добавьте следующий код.

Button button = (Button) findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        }
});

Это настраивает обработчик событий (onClick), срабатывающий когда пользователь нажимает кнопку. Остальной код напишем в методе onClick.

Начнем с загрузки изображения штрих-кода. Сначала находим ImageView по идентификатору. Затем используется BitMapFactory для декодирования ресурса R.drawable.qr в растровое изображение. Полученное растровое изображение передаем ImageView.

ImageView myimageView = (ImageView)findViewById(R.id.imgview);
                Bitmap myBitmap = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.qr);
                myimageView.setImageBitmap(myBitmap);

Далее создаем экземпляр класса BarcodeDetector, используя Builder и настраиваем его на поиск QR-кодов и матрицы данных (есть много других типов штрих-кодов, которые он мог бы найти).

BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(getApplicationContext())
                        .setBarcodeFormats(Barcode.DATA_MATRIX | Barcode.QR_CODE)
                        .build();

Вполне возможно, что первый раз наш детектор штрих-код сработает, когда служба Google Play еще не будет готова для обработки штрих-кодов. Поэтому мы должны проверить, что наш детектор работает, прежде чем использовать его. Если нет, нам придется ждать окончания загрузки или сообщить пользователям, что нужно найти подключение к Интернету или освободить место на устройстве. Прежде чем мы обратимся к текстовому полю, нужно объявить и найти его выше в методе onCreate.

TextView txtView = (TextView) findViewById(R.id.txtContent);

Теперь допишем метод вывода сообщения.

if (!barcodeDetector.isOperational()) {
                    txtView.setText("Could not set up the detector!");
                }

Теперь, когда наш детектор создан и мы знаем, что он работает, создаем кадр из растрового изображения и передаем его детектору. Тот возвращает нам массив штрих-кодов SparseArray.

Frame frame = new Frame.Builder().setBitmap(myBitmap).build();
                SparseArray<Barcode> barcodes = barcodeDetector.detect(frame);

Обратите внимание, что Mobile Vision API способен обнаруживать несколько штрих-кодов в одном кадре. В этом случае массив SparseArray <Barcode> будет заполнен несколькими записями.

Обычно на этом этапе нужно пробежать по массиву SparseArray и обработать каждый штрих-код отдельно. Нужно предусмотреть возможность, что штрих-кодов может быть несколько, или ни одного. В нашем случае мы знаем, что у нас есть только 1 штрих-код, и можем прописать жесткий код для него. Для этого мы берем штрих-код, называемый «thisCode», который будет первым элементом в массиве. Затем присваиваем значение его поля rawValue текстовому полю textView – и все.

Barcode thisCode = barcodes.valueAt(0);
                txtView.setText(thisCode.rawValue);

Теперь все, что вам нужно сделать, это запустить приложение.

Запускать лучше на реальном устройстве. На эмуляторе работать не будет, потому что на эмуляторах по умолчанию отсутствует сервис Google Play.

Вот приложение запустилось на устройстве, жмем кнопку. Если вы используете изображение штрих-кода qr.png, вы увидите в текстовом поле данные, закодированные в QR-код – это адрес нашего сайта fandroid.info.

device-2016-11-08-200410

Это был простой пример приложения, демонстрирующий работу Mobile Vision API. Но полноценный сканер штрих-кодов должен получать изображение с камеры устройства, или обрабатывать превью камеры.

Разработчики подготовили пример такого приложения на Github, вы можете скачать его код по ссылке.

Клонируйте или скачайте проект, и откройте в Android Studio модуль barcode-reader.

При запуске приложения из этого проекта мы можем увидеть, как оно работает. Первоначально открывается стартовый экран, на котором можно включить автофокус или вспышку. Кнопка считывания штрих-кода запускает камеру. Приложение отслеживает появление штрих-кодов на превью и рисует рамки на обнаруженных штрих-кодах. При нажатии на область обнаруженного штрих-кода превью закрывается, передавая при этом данные обнаруженного штрих-кода в текстовое поле на стартовом экране. Подробнее смотрите в видео.

Это приложение также использует Mobile Vision API. Но при рассмотрении проекта в Android Studio можно увидеть, что кода здесь побольше, чем в нашем простом примере.

2016-11-09_22-13-29

Приложение состоит из восьми классов. Три из них отвечают за интерфейс и работу с камерой.

  • Класс CameraSource предоставляет управление камерой для получения предварительного просмотра.
  • Класс CameraSourcePreview отвечает за отображение превью на экране.
  • Класс GraphicOverlay отображает графические объекты поверх связанного предварительного просмотра камеры.
  • Класс MainActivity отображает стартовое окно с настройками и кнопкой запуска сканирования, и получает данные штрих-кода для размещения в TextView.
  • Класс BarcodeTrackerFactory реализует паттерн “Фабрика” и используется для создания трекеров штрих-кода – по одному для каждого штрих-кода.
  • Класс BarcodeGraphicTracker это трекер, который используется для обнаружения штрих-кодов на экране, и их отслеживания для наложения графики, а также удаления графики, когда штрих-код покидает зону видимости.
  • Класс BarcodeGraphic используется для отрисовки экземпляра накладываемого на штрих-код изображения с учетом его положения, размера и идентификатора.
  • Класс BarcodeCaptureActivity – это активити, которое запускается при нажатии кнопки считывания штрих-кода в стартовом окне приложения. Это активити отображает превью камеры и определяет штрих-коды на нем, выполнzет их считывание и наложение графических рамок на каждый штрих-код с помощью вышеперечисленных классов.

Я не буду в рамках этого урока углубляться в более подробное объяснение кода от разработчиков, а оставлю это вам в качестве домашнего задания. Тем более что код прекрасно документирован, каждый класс и метод содержит комментарии на английском языке. Читать и разбираться в таком коде очень полезно, особенно начинающему разработчику.

А если комментариев в коде вам недостаточно, пользуйтесь возможностью поиска в Google по выделенному имени класса или метода в контекстном меню редактора в Android Studio. Поиск наверняка приведет вас на страницу официальной документации, где вы можете получить исчерпывающую информацию. также в результатах поиска будет очень полезно изучить примеры с сайта stackoverflow.com, с комментариями опытных разработчиков.

В этом руководстве мы будем использовать библиотеку ZXing (Zebra Crossing) для сканирования штрих-кодов в приложении Android. Мы будем обращаться к ресурсам этой библиотеки с открытым исходным кодом в нашем приложении, получая и обрабатывая возвращенные результаты.

Поскольку мы используем библиотеку ZXing , нам не нужно беспокоиться о пользователях без установленного сканера штрих-кода, потому что предоставленные классы интеграции позаботятся об этом за нас. Импортируя классы интеграции ZXing в наше приложение, мы можем упростить сканирование пользователей и сосредоточить наши усилия на разработке на обработке результатов сканирования. В следующем выпуске серии мы разработаем приложение для сканирования книг, в котором мы будем использовать приложение, созданное в этом руководстве. Мы также добавим поддержку API Google Книг, чтобы мы могли отображать информацию о отсканированных книгах.

Премиум-вариант: QR-код и считыватель штрих-кодов

Если вы ищете ярлык, вы можете найти несколько готовых QR-кодов и считывателей штрих-кодов для приложений Android на Envato Market.

Например, QR-Code & Barcode Reader использует камеру мобильного устройства для считывания штрих-кодов и QR-кодов.

Программа автоматически распознает тип закодированных данных, обеспечивая приятный предварительный просмотр и различные параметры обмена. Он добавит новый контакт, если QR-код является визитной карточкой, или отправит SMS или позвонит кому-либо, в зависимости от закодированных данных. Выбор поисковой системы идеально подходит для поиска товаров в Интернете, сравнения цен и обзоров.

Функции автоматизации, такие как режим массового сканирования или автоматическое действие, открывают широкие возможности для интеграции предприятия. Для компиляции проекта требуется библиотека сканирования штрих-кодов с открытым исходным кодом zBar, которая, как утверждается, является самой быстрой среди других доступных на рынке и включена здесь.

Считыватель штрих-кодов QR-кода на рынке Envato

QR-код и считыватель штрих-кодов на Envato Market

Вы можете найти в Envato Market другие варианты, чтобы помочь вам, или получить приложение для Android, разработанное в Envato Studio. В противном случае, читайте подробные пошаговые инструкции по созданию считывателя штрих-кода самостоятельно.


1. Создайте новый проект Android

Шаг 1

В Eclipse создайте новый проект Android. Введите выбранное вами приложение, проект и имена пакетов. Позвольте Eclipse создать для вас пустое мероприятие с именем по вашему выбору как для занятия, так и для его макета.

Новый проект

Шаг 2

Откройте ваш основной файл макета. С настройками по умолчанию Eclipse начинает макет с объекта Relative Layout , который вы можете оставить как есть. Внутри него замените существующий контент (обычно текстовое представление ) кнопкой.

01

02

03

04

05

06

07

08

09

10

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”

    xmlns:tools=”http://schemas.android.com/tools”

    android:layout_width=”match_parent”

    android:layout_height=”match_parent” >

    <Button android:id=”@+id/scan_button”

        android:layout_width=”wrap_content”

        android:layout_height=”wrap_content”

        android:layout_centerHorizontal=”true”

        android:text=”@string/scan” />

</RelativeLayout>

После кнопки добавьте два текстовых представления, в которые мы выведем информацию о сканировании.

01

02

03

04

05

06

07

08

09

10

11

12

13

14

<TextView

    android:id=”@+id/scan_format”

    android:layout_width=”wrap_content”

    android:layout_height=”wrap_content”

    android:textIsSelectable=”true”

    android:layout_centerHorizontal=”true”

    android:layout_below=”@id/scan_button” />

<TextView

    android:id=”@+id/scan_content”

    android:layout_width=”wrap_content”

    android:layout_height=”wrap_content”

    android:textIsSelectable=”true”

    android:layout_centerHorizontal=”true”

    android:layout_below=”@id/scan_format” />

Добавьте текстовую строку кнопки в ваш XML-файл ” res / values ​​/ strings “.

1

<string name=”scan”>Scan</string>

Пользователь нажимает кнопку для сканирования. Когда приложение получает результат от операции сканирования штрих-кода, оно отображает данные содержимого сканирования и имя формата в двух текстовых представлениях .


2. Добавьте ZXing к вашему проекту

Шаг 1

ZXing – это библиотека с открытым исходным кодом, которая предоставляет доступ к проверенному и функциональному сканированию штрих-кода на Android. Многие пользователи уже установили приложение на своих устройствах, поэтому вы можете просто запустить сканирование Intents и получить результаты. В этом уроке мы будем использовать метод « Сканирование через намерение», чтобы упростить сканирование. Этот метод включает импорт нескольких классов в ваше приложение и позволяет ZXing позаботиться о случаях, когда у пользователя не установлен сканер. Если у пользователя не установлен сканер штрих-кода, ему будет предложено загрузить его.

Совет: Поскольку ZXing является открытым исходным кодом, вы можете полностью импортировать исходный код в свои проекты. Тем не менее, это действительно целесообразно, только если вам нужно внести изменения в его функциональность. Вы также можете скомпилировать проект и включить его JAR-файл в свои собственные приложения, если хотите. В большинстве случаев использование сканирования через намерение – это надежный и простой в реализации вариант, плюс ваши пользователи будут иметь доступ к самой последней версии приложения ZXing.

В Eclipse добавьте новый пакет в свой проект, щелкнув правой кнопкой мыши папку « src », выбрав « Новый », затем « Пакет » и введя « com.google.zxing.integration.android » в качестве имени пакета.

Новый пакет

Шаг 2

Eclipse предлагает несколько способов импортировать существующий код в ваши проекты. Для целей данного руководства вам, вероятно, будет проще всего создать два обязательных класса и скопировать код из ZXing. Щелкните правой кнопкой мыши новый пакет, выберите « Новый », затем « Класс » и введите « IntentIntegrator » в качестве имени класса. Вы можете оставить другие настройки по умолчанию такими, какие они есть. Создав этот класс, сделайте то же самое для другого класса, который мы будем импортировать, указав ему « IntentResult » в качестве имени класса.

Новый класс

Скопируйте код из обоих классов в библиотеке ZXing и вставьте его в созданные вами файлы классов. Это IntentIntegrator и IntentResult . Обратитесь к загрузке исходного кода, если у вас есть какие-либо сомнения относительно того, где должны быть различные файлы и папки или что в них должно быть.

Пакет проектов

Теперь вы можете импортировать классы ZXing в ваш основной класс Activity .

1

2

import com.google.zxing.integration.android.IntentIntegrator;

import com.google.zxing.integration.android.IntentResult;

Продолжайте и добавьте другие операторы импорта, которые мы будем использовать для этого урока. Имейте в виду, что Eclipse, возможно, уже добавил некоторые для вас.

1

2

3

4

5

6

7

8

import android.os.Bundle;

import android.app.Activity;

import android.content.Intent;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;

Не стесняйтесь взглянуть на содержание двух классов ZXing. Это довольно просто, но детали обработки сканирования штрих-кода выполняются в другом месте библиотеки. Эти два класса действительно действуют как интерфейс к функциям сканирования.


3. Сканирование

Шаг 1

Давайте реализуем сканирование, когда пользователь нажимает кнопку, которую мы добавили. В основном классе активности вашего приложения метод onCreate по умолчанию, введенный Eclipse, должен выглядеть примерно так.

1

2

3

4

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

}

Над этим методом добавьте следующие переменные экземпляра для представления кнопки и двух текстовых представлений, которые мы создали в файле макета.

1

2

private Button scanBtn;

private TextView formatTxt, contentTxt;

В onCreate , после существующего кода, создайте экземпляры этих переменных, используя значения идентификаторов, которые мы указали в XML.

1

2

3

scanBtn = (Button)findViewById(R.id.scan_button);

formatTxt = (TextView)findViewById(R.id.scan_format);

contentTxt = (TextView)findViewById(R.id.scan_content);

Затем добавьте слушателя к кнопке, чтобы мы могли обрабатывать нажатия.

1

scanBtn.setOnClickListener(this);

Расширьте начальную строку объявления класса, чтобы реализовать интерфейс OnClickListener .

1

public class MainActivity extends Activity implements OnClickListener

Шаг 2

Теперь мы можем реагировать на нажатия кнопок, начиная процесс сканирования. Добавьте метод onClick в свой класс деятельности .

1

2

3

public void onClick(View v){

//respond to clicks

}

Проверьте, была ли нажата кнопка сканирования внутри этого метода.

1

2

3

if(v.getId()==R.id.scan_button){

//scan

}

Внутри этого условного блока создайте экземпляр импортированного нами класса Intent Integrator .

1

IntentIntegrator scanIntegrator = new IntentIntegrator(this);

Теперь мы можем вызвать метод Intent Integrator, чтобы начать сканирование.

1

scanIntegrator.initiateScan();

На этом этапе сканер запустится, если он установлен на устройстве пользователя. Если нет, им будет предложено загрузить его. Результаты сканирования будут возвращены в основное действие, с которого было начато сканирование, поэтому мы сможем получить его в методе onActivityResult .

Совет: Когда вы вызываете метод initiateScan , вы можете передать коллекцию типов штрих-кодов, которые хотите сканировать. По умолчанию метод сканирует все поддерживаемые типы. К ним относятся UPC-A, UPC-E, EAN-8, EAN-13, QR-код, RSS-14, RSS Expanded, Data Matrix, Aztec, PDF 417, Codabar, ITF, коды 39, 93 и 128. ZXing Библиотека также включает в себя параметры сканирования штрих-кода, которые мы не собираемся освещать в этом руководстве. Вы можете проверить проект в Google Code для получения дополнительной информации.


4. Получить результаты сканирования

Шаг 1

Когда пользователь нажимает кнопку сканирования, запускается сканер штрих-кода. Когда они сканируют штрих-код, он возвращает отсканированные данные в метод onActivityResult вызывающего действия . Добавьте метод в ваш основной класс деятельности .

1

2

3

public void onActivityResult(int requestCode, int resultCode, Intent intent) {

//retrieve scan result

}

Внутри метода попробуйте проанализировать результат в экземпляре класса ZXing Intent Result, который мы импортировали.

1

IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);

Шаг 2

Как и для любых данных, извлекаемых из другого приложения, очень важно проверить наличие нулевых значений. Продолжайте, только если у нас есть действительный результат.

1

2

3

if (scanningResult != null) {

//we have a result

}

Если данные сканирования не получены (например, если пользователь отменяет сканирование, нажав кнопку назад), мы можем просто вывести сообщение.

1

2

3

4

5

else{

    Toast toast = Toast.makeText(getApplicationContext(),

        “No scan data received!”, Toast.LENGTH_SHORT);

    toast.show();

}

Вернувшись в блок if , давайте выясним, какие данные вернул скан. Объект Intent Result предоставляет методы для извлечения содержимого сканирования и формата данных, полученных из него. Получить содержимое как строковое значение.

1

String scanContent = scanningResult.getContents();

Получить имя формата, также в виде строки.

1

String scanFormat = scanningResult.getFormatName();

Шаг 3

Теперь ваша программа имеет формат и содержание отсканированных данных, поэтому вы можете делать с ней все, что захотите. Для целей этого урока мы просто запишем значения в текстовые представления в нашем макете.

1

2

formatTxt.setText(“FORMAT: ” + scanFormat);

contentTxt.setText(“CONTENT: ” + scanContent);

Запустите ваше приложение на устройстве, а не на эмуляторе, чтобы увидеть, как работает сканирование. Попробуйте отсканировать книгу или любой другой штрих-код, который у вас может быть.

Приложение во время сканирования

Когда сканирование начинается, пользователь попадает в приложение ZXing для сканирования штрих-кода.
Результат сканирования
Результаты сканирования возвращаются в приложение.

Вывод

В этом руководстве мы рассмотрели процесс сканирования штрих-кодов в приложениях Android с помощью библиотеки ZXing. В ваших собственных приложениях может потребоваться дополнительная обработка полученных результатов сканирования, например загрузка URL-адресов или поиск данных в стороннем источнике данных. В продолжение этого урока мы будем использовать функцию сканирования штрих-кодов, чтобы создать приложение для сканирования книг, которое позволит нам получать данные о отсканированных книгах из API Google Книг.

Не забывайте, что если у вас возникли проблемы с созданием считывателя штрих-кода, вы можете найти несколько готовых вариантов на Envato Market, чтобы помочь вам.

Пишем сканера QR-кодов под Android
Пишем сканера QR-кодов под Android

Узнайте о самом быстром способе создания сканера QR-кодов для Android. Пошаговый предварительный просмотр камеры реализации, а также как интегрировать SDK для сканирования QR-кодов.

Приложение для сканирования QR-кодов в основном состоит из двух частей: предварительного просмотра с помощью камеры и сканирования QR-кода. Существует множество приложений для сканирования QR-кодов для Android, которые можно скачать в Google Play Store. Однако интереснее создать сканер QR-кода самостоятельно, чем использовать уже существующий. Цель этой статьи — рассказать о самом быстром способе создания сканера QR-кодов для Android. Вы увидите, как шаг за шагом реализовать предварительный просмотр камеры, а также как интегрировать SDK для сканирования QR-кодов.

Предварительные условия
Для реализации сканера QR-кодов необходимы следующие библиотеки Android. Вы можете свободно заменить их своими собственными библиотеками.

  • Camera Preview SDK
  • CameraX

Поскольку Android Camera2 API  чрезвычайно сложен для новичков, Google выпустил CameraX, чтобы упростить разработку приложений для камер. Учебник codelab является хорошей отправной точкой для изучения CameraX.

Установка

В AndroidManifest.xml, добавить разрешение на камеру:

<uses-feature android:name="android.hardware.camera.any" />

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

В app/build.gradle, добавьте зависимость:

dependencies {

            ...

            def camerax_version = "1.0.1"

            implementation "androidx.camera:camera-camera2:$camerax_version"

            implementation "androidx.camera:camera-lifecycle:$camerax_version"

            implementation "androidx.camera:camera-view:1.0.0-alpha27"

        }

Dynamsoft Camera Enhancer

Similar to CameraX, [Dynamsoft Camera Enhancer](https://www.dynamsoft.com/camera-enhancer/docs/introduction/) is also a wrapper for Android Camera2 API. In addition to basic camera capabilities, it features frame filtering for better image quality. We use Dynamsoft Camera Enhancer to make a comparison with CameraX.

Dynamsoft Camera Enhancer

Подобно CameraX, [Dynamsoft Camera Enhancer] также является оберткой для Android Camera2 API. В дополнение к базовым возможностям камеры, в ней предусмотрена фильтрация кадров для улучшения качества изображения. Мы используем Dynamsoft Camera Enhancer для сравнения с CameraX.

Установка

В «settings.gradle» добавьте пользовательский репозиторий Maven:

dependencyResolutionManagement {

            repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)

            repositories {

                ...

                maven{url "https://download.dynamsoft.com/maven/dce/aar"}

            }

        }

В app/build.gradle, добавьте зависимость:

dependencies {

            ...

            implementation 'com.dynamsoft:dynamsoftcameraenhancer:[email protected]'

        }

SDK для сканирования QR-кодов

Dynamsoft Barcode Reader:

SDK для считывания штрихкодов, поддерживающий все основные форматы линейных и двумерных штрихкодов.

Установка

В settings.gradle, добавить пользовательский репозиторий Maven:

dependencyResolutionManagement {

            repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)

            repositories {

                ...

                maven{url "https://download.dynamsoft.com/maven/dbr/aar"}

            }

        }

В app/build.gradle, добавьте зависимость:

dependencies {

            ...

            implementation 'com.dynamsoft:dynamsoftbarcodereader:[email protected]'

        }

Вам также необходим лицензионный ключ для активации SDK для штрихкодов.

Создание предварительного просмотра камеры Android за 5 минут

Три шага для реализации предварительного просмотра камеры с помощью CameraX

Официальное руководство по CameraX написано на языке Kotlin. Здесь мы используем Java.

  1. Создайте макет пользовательского интерфейса, который содержит представление предварительного просмотра CameraX:
<?xml version="1.0" encoding="utf-8"?>

    <androidx.constraintlayout.widget.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=".CameraXActivity">

    

        <androidx.camera.view.PreviewView

            android:id="@+id/camerax_viewFinder"

            android:layout_width="match_parent"

            android:layout_height="match_parent" />

    

    </androidx.constraintlayout.widget.ConstraintLayout>

2. Проверьте и запросите разрешения на съемку:

@Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.camerax_main);

        previewView = findViewById(R.id.camerax_viewFinder);



        if (!CameraUtils.allPermissionsGranted(this)) {

            CameraUtils.getRuntimePermissions(this);

        } else {

            startCamera();

        }

    }



    private static String[] getRequiredPermissions(Context context) {

        try {

            PackageInfo info =

                    context.getPackageManager()

                            .getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);

            String[] ps = info.requestedPermissions;

            if (ps != null && ps.length > 0) {

                return ps;

            } else {

                return new String[0];

            }

        } catch (Exception e) {

            return new String[0];

        }

    }



    public static boolean allPermissionsGranted(Context context) {

        for (String permission : getRequiredPermissions(context)) {

            if (!isPermissionGranted(context, permission)) {

                return false;

            }

        }

        return true;

    }



    public static void getRuntimePermissions(Activity activity) {

        List<String> allNeededPermissions = new ArrayList<>();

        for (String permission : getRequiredPermissions(activity)) {

            if (!isPermissionGranted(activity, permission)) {

                allNeededPermissions.add(permission);

            }

        }



        if (!allNeededPermissions.isEmpty()) {

            ActivityCompat.requestPermissions(

                    activity, allNeededPermissions.toArray(new String[0]), PERMISSION_REQUESTS);

        }

    }



    private static boolean isPermissionGranted(Context context, String permission) {

        if (ContextCompat.checkSelfPermission(context, permission)

                == PackageManager.PERMISSION_GRANTED) {

            Log.i(TAG, "Permission granted: " + permission);

            return true;

        }

        Log.i(TAG, "Permission NOT granted: " + permission);

        return false;

    }

3. Запустите предварительный просмотр камеры:

private void startCamera() {

        ListenableFuture<ProcessCameraProvider> cameraProviderFuture =

                ProcessCameraProvider.getInstance(getApplication());

        cameraProviderFuture.addListener(

                () -> {

                    try {

                        ProcessCameraProvider cameraProvider = cameraProviderFuture.get();



                        Preview.Builder builder = new Preview.Builder();

                        Preview previewUseCase = builder.build();

                        previewUseCase.setSurfaceProvider(previewView.getSurfaceProvider());

                        CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;

                        cameraProvider.unbindAll();

                        cameraProvider.bindToLifecycle(this, cameraSelector, previewUseCase);

                    } catch (ExecutionException | InterruptedException e) {

                        Log.e(TAG, "Unhandled exception", e);

                    }

                },

                ContextCompat.getMainExecutor(getApplication()));

    }

Второй шаг для реализации предварительного просмотра камеры с помощью Dynamsoft Camera Enhancer

Используя Dynamsoft Camera Enhancer, вы напишите меньше кода, чем при использовании CameraX для реализации той же функциональности.

  1. Создайте макет пользовательского интерфейса, который содержит представление предварительного просмотра DCE:
<?xml version="1.0" encoding="utf-8"?>

    <androidx.constraintlayout.widget.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=".CameraXActivity">

    

        <com.dynamsoft.dce.DCECameraView

            android:id="@+id/dce_viewFinder"

            android:layout_width="match_parent"

            android:layout_height="match_parent" />

    

    </androidx.constraintlayout.widget.ConstraintLayout>

2. Запустите предварительный просмотр камеры:

@Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.dce_main);

        previewView = findViewById(R.id.dce_viewFinder);



        cameraEnhancer = new CameraEnhancer(this);

        cameraEnhancer.setCameraView(previewView);

        cameraEnhancer.addListener(this);

    }



    @Override

    protected void onResume() {

        super.onResume();

        try {

            cameraEnhancer.open();

        } catch (CameraEnhancerException e) {

            e.printStackTrace();

        }

    }



    @Override

    protected void onPause() {

        super.onPause();

        try {

            cameraEnhancer.close();

        } catch (CameraEnhancerException e) {

            e.printStackTrace();

        }

    }

Все в одном

Мы создаем входную активность для запуска CameraX и Dynamsoft Camera Enhancer соответственно:

package com.example.qrcodescanner;



import android.content.Intent;

import android.os.Bundle;

import android.view.View;



import androidx.appcompat.app.AppCompatActivity;



public class EntryChoiceActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.entry_choice);



        findViewById(R.id.camerax_entry_point).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                Intent intent = new Intent(EntryChoiceActivity.this, CameraXActivity.class);

                startActivity(intent);

            }

        });



        findViewById(R.id.dce_entry_point).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                Intent intent = new Intent(EntryChoiceActivity.this, DceActivity.class);

                startActivity(intent);

            }

        });

    }

}

Превращение камеры Android в сканер QR-кодов

Для сканирования QR-кодов нам необходимо постоянно получать кадры предварительного просмотра камеры и передавать их в детектор QR-кодов.

Как установить обратный вызов кадра камеры

При использовании CameraX мы можем использовать класс «ImageAnalysis» для получения кадров камеры:

ImageAnalysis analysisUseCase = new ImageAnalysis.Builder().build();

analysisUseCase.setAnalyzer(cameraExecutor,

        imageProxy -> {

            // image processing

            // Must call close to keep receiving frames.

            imageProxy.close();

        });

cameraProvider.bindToLifecycle(this, cameraSelector, previewUseCase, analysisUseCase);

В отличие от него, Dynamsoft Camera Enhancer намного проще. Функция обратного вызова похожа на ту, что используется в Android Camera1:

public class DceActivity extends AppCompatActivity implements DCEFrameListener {

    @Override

    public void frameOutputCallback(DCEFrame dceFrame, long l) {

        // image processing

    }

}

Типы данных, возвращаемые их функциями обратного вызова, различны. Для дальнейшего использования требуется преобразование типов данных.

Декодирование QR-кода

В CameraX мы сначала преобразуем «ByteBuffer» в «byte[]», а затем вызываем метод «decodeBuffer()»:

analysisUseCase.setAnalyzer(cameraExecutor,

imageProxy -> {

    TextResult[] results = null;

    ByteBuffer buffer = imageProxy.getPlanes()[0].getBuffer();

    int nRowStride = imageProxy.getPlanes()[0].getRowStride();

    int nPixelStride = imageProxy.getPlanes()[0].getPixelStride();

    int length = buffer.remaining();

    byte[] bytes = new byte[length];

    buffer.get(bytes);

    try {

        results = reader.decodeBuffer(bytes, imageProxy.getWidth(), imageProxy.getHeight(), nRowStride * nPixelStride, EnumImagePixelFormat.IPF_NV21, "");

    } catch (BarcodeReaderException e) {

        e.printStackTrace();

    }



    // Must call close to keep receiving frames.

    imageProxy.close();

});

В то время как в Dynamsoft Camera Enhancer мы получаем  Bitmap изDCEFrame  и затем вызываем метод decodeBufferedImage()

public void frameOutputCallback(DCEFrame dceFrame, long l) {

    TextResult[] results = null;

    try {

        results = reader.decodeBufferedImage(dceFrame.toBitmap(), "");

    } catch (BarcodeReaderException e) {

        e.printStackTrace();

    }

}

Использование зума и фонарика для повышения качества кадра

На точность распознавания всегда влияет качество входного изображения. Если QR-код слишком мал, мы можем увеличить масштаб камеры, чтобы увеличить изображение. Если входное изображение слишком темное, мы можем включить фонарик, чтобы осветлить изображение. И CameraX, и Dynamsoft Camera Enhancer полностью поддерживают управление камерой.

Зум камеры Android

Для запуска зума мы используем жест «щипок пальцем». Таким образом, первым шагом будет создание детектора жестов и передача ему метода «onTouchEvent()»:

public class ZoomController {

    public final static String TAG = "ZoomController";

    private float currentFactor = 1.0f;

    private float minZoomRatio = 1.0f, maxZoomRatio = 1.0f;

    private ZoomStatus zoomStatus;

    private ScaleGestureDetector scaleGestureDetector;

    private ScaleGestureDetector.OnScaleGestureListener scaleGestureListener = new ScaleGestureDetector.OnScaleGestureListener() {

        @Override

        public boolean onScale(ScaleGestureDetector detector) {

            Log.i(TAG, "onScale: " + detector.getScaleFactor());

            currentFactor = detector.getScaleFactor() * currentFactor;

            if (currentFactor < minZoomRatio) currentFactor = minZoomRatio;

            if (currentFactor > maxZoomRatio) currentFactor = maxZoomRatio;



            if (zoomStatus != null) {

                zoomStatus.onZoomChange(currentFactor);

            }



            return true;

        }



        @Override

        public boolean onScaleBegin(ScaleGestureDetector detector) {

            return true;

        }



        @Override

        public void onScaleEnd(ScaleGestureDetector detector) {

        }

    };



    public ZoomController(Activity activity) {

        scaleGestureDetector = new ScaleGestureDetector(activity, scaleGestureListener);

    }



    public interface ZoomStatus {

        void onZoomChange(float ratio);

    }



    public void addListener(ZoomStatus zoomStatus) {

        this.zoomStatus = zoomStatus;

    }



    public void initZoomRatio(float minZoomRatio, float maxZoomRatio) {

        this.minZoomRatio = minZoomRatio;

        this.maxZoomRatio = maxZoomRatio;

    }



    public boolean onTouchEvent(MotionEvent event) {

        return scaleGestureDetector.onTouchEvent(event);

    }

}



@Override

public boolean onTouchEvent(MotionEvent event) {

    zoomController.onTouchEvent(event);

    return super.onTouchEvent(event);

}

При обнаружении жеста мы получаем масштабный коэффициент и используем его в качестве коэффициента масштабирования.

Установка коэффициента масштабирования камеры с помощью CameraX

if (camera != null) {

    camera.getCameraControl().setZoomRatio(ratio);

}

Настройка коэффициента масштабирования камеры с помощью Dynamsoft Camera Enhancer

try {

    cameraEnhancer.setZoom(ratio);

} catch (CameraEnhancerException e) {

    e.printStackTrace();

}

Фонарик для камеры Android

Для автоматического включения фонарика мы отслеживаем значение освещенности, возвращаемое датчиком освещенности.

public class AutoTorchController implements SensorEventListener {

    public final static String TAG = "AutoTorchController";

    private SensorManager sensorManager;

    private TorchStatus torchStatus;



    public interface TorchStatus {

        void onTorchChange(boolean status);

    }



    public AutoTorchController(Activity activity) {

        sensorManager = (SensorManager)activity.getSystemService(SENSOR_SERVICE);

    }



    public void onStart() {

        Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);

        if(lightSensor != null){

            sensorManager.registerListener(

                    this,

                    lightSensor,

                    SensorManager.SENSOR_DELAY_NORMAL);



        }

    }



    public void onStop() {

        sensorManager.unregisterListener(this);

    }



    @Override

    public void onSensorChanged(SensorEvent event) {

        if(event.sensor.getType() == Sensor.TYPE_LIGHT){

            if (event.values[0] < 20) {

                if (torchStatus != null) torchStatus.onTorchChange(true);

            }

            else {

                if (torchStatus != null) torchStatus.onTorchChange(false);

            }

        }

    }



    @Override

    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }



    public void addListener(TorchStatus torchStatus) {

        this.torchStatus = torchStatus;

    }

}

Переключить фонарь камеры с помощью CameraX

if (camera != null) camera.getCameraControl().enableTorch(status);

Переключение вспышки камеры с помощью Dynamsoft Camera Enhancer

if (status) {

    try {

        cameraEnhancer.turnOnTorch();

    } catch (CameraEnhancerException e) {

        e.printStackTrace();

    }

}

else {

    try {

        cameraEnhancer.turnOffTorch();

    } catch (CameraEnhancerException e) {

        e.printStackTrace();

    }

}

Исходный код

В народе довольно популярны приложения, умеющие сканировать штрих коды и QR-коды на различных вещах и продуктах и выдающие о них необходимую полезную информацию. В сегодняшнем уроке мы сделаем свое собственное приложение, способное работать с этими замысловатыми кодами. Но наше приложение не будет полностью работающим сканером, написанным лично нами, мы сделаем всего лишь приложение, которое будет использовать для анализа кодов известное на Play Market приложение под названием Barcode Scanner (бесплатное). Наше приложение будет с помощью намерения Intent запускать это упомянутое выше приложение, там будет происходить анализ необходимого кода, а результат анализа будет передаваться назад нашему приложению с помощью метода OnActivityResult, а затем высвечиваться уже в нашем приложении с помощью всплывающего Toast сообщения. 

Для начала создаем новый проект, выбираем Blank Activity.

Первым делом создадим пользовательский интерфейс приложения. Он будет состоять из двух кнопок для запуска сканеров штрих кода и QR-кода. При нажатии на кнопки пользователю будет предложено загрузить приложение Barcode Scanner с маркета. Открываем файл activity_main.xml и добавляем туда следующие элементы:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/container"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="#ffffff"
 android:orientation="vertical">

 <TextView
 android:id="@+id/textView1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center|top"
 android:layout_margin="20dp"
 android:text="Сканер"
 android:textColor="#000000"
 android:textSize="30dp" />

 <Button
 android:id="@+id/scanner"
 android:layout_width="250dp"
 android:layout_height="80dp"
 android:layout_gravity="center"
 android:layout_margin="10dp"
 android:gravity="center"
 android:onClick="scanQR"
 android:text="QR-код"
 android:textSize="18dp" >
 </Button>

 <Button
 android:id="@+id/scanner2"
 android:layout_width="250dp"
 android:layout_height="80dp"
 android:layout_gravity="center"
 android:layout_margin="10dp"
 android:gravity="center"
 android:onClick="scanBar"
 android:text="Штрих код"
 android:textSize="18dp" >
 </Button>
</LinearLayout>

Теперь переходим к работе с кодом. В принципе, ничего нового мы здесь не увидим, все довольно знакомое. Переход на другое приложение с помощью Intent, выполнение alert dialog для предложения пользователю перейти на Play Market для загрузки сканера, метод для обработки результатов программы сканера и получения данных с него OnActivityResult и их вывод в Toast сообщении. 

Открываем файл MainActivity.java и добавляем в него следующий, довольно понятный код:

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {

 static final String ACTION_SCAN = "com.google.zxing.client.android.SCAN";

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

 // Запускаемм сканер штрих кода:
 public void scanBar(View v) {
 try {

 // Запускаем переход на com.google.zxing.client.android.SCAN с помощью intent:
 Intent intent = new Intent(ACTION_SCAN);
 intent.putExtra("SCAN_MODE", "PRODUCT_MODE");
 startActivityForResult(intent, 0);
 } catch (ActivityNotFoundException anfe) {

 // Предлагаем загрузить с Play Market:
 showDialog(MainActivity.this, "Сканнер не найден", "Установить сканер с Play Market?", "Да", "Нет").show();
 }
 }

 // Запуск сканера qr-кода:
 public void scanQR(View v) {
 try {
 
 // Запускаем переход на com.google.zxing.client.android.SCAN с помощью intent:
 Intent intent = new Intent(ACTION_SCAN);
 intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
 startActivityForResult(intent, 0);
 } catch (ActivityNotFoundException anfe) {

 // Предлагаем загрузить с Play Market:
 showDialog(MainActivity.this, "Сканнер не найден", "Установить сканер с Play Market?", "Да", "Нет").show();
 }
 }

 // alert dialog для перехода к загрузке приложения сканера:
 private static AlertDialog showDialog(final Activity act, CharSequence title,
 CharSequence message,CharSequence buttonYes, CharSequence buttonNo) {
 AlertDialog.Builder downloadDialog = new AlertDialog.Builder(act);
 downloadDialog.setTitle(title);
 downloadDialog.setMessage(message);
 downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() {
 public void onClick(DialogInterface dialogInterface, int i) {

 // Ссылка поискового запроса для загрузки приложения:
 Uri uri = Uri.parse("market://search?q=pname:" + "com.google.zxing.client.android");
 Intent intent = new Intent(Intent.ACTION_VIEW, uri);
 try {
 act.startActivity(intent);
 } catch (ActivityNotFoundException anfe) {

 }
 }
 });
 downloadDialog.setNegativeButton(buttonNo, new DialogInterface.OnClickListener() {
 public void onClick(DialogInterface dialogInterface, int i) {
 }
 });
 return downloadDialog.show();
 }

 // Обрабатываем результат, полученный от приложения сканера:
 public void onActivityResult(int requestCode, int resultCode, Intent intent) {
 if (requestCode == 0) {
 if (resultCode == RESULT_OK) {

 // Получаем данные после работы сканера и выводим их в Toast сообщении:
 String contents = intent.getStringExtra("SCAN_RESULT");
 String format = intent.getStringExtra("SCAN_RESULT_FORMAT");
 Toast toast = Toast.makeText(this, "Содержание: " + contents + " Формат: " + format, Toast.LENGTH_LONG);
 toast.show();
 }
 }
 }
}

Вот и все, на этом наш сканер готов. Запускаем и проверяем:

Запуск перехода

Переход выполнен, загружаем ПО:

Загрузка программы

  • Download demo — 8.92 KB

Introduction

Most bar code scanners emulate keyboards. This allows them to be easily used in any application. The user simply needs to put the focus on any text input control and then scan a bar code.

This same behaviour also allows the user to scan bar codes into fields not intended for scanning and can cause problem. This method does not allow the application to take instant action when a scan is received because the application has no way of distinguishing the bar code scanner input from the keyboard.

Many vendors have their own APIs which can be used to distinguish the bar code scanner from the keyboard. In the real world, there are thousands of bar code scanners and relying on vendor specific APIs is not practical. Many of the generic or clone scanners which are now a common commodity have no API and rely solely on keyboard emulation which further complicates the issue.

Fortunately, there is an easy yet not well known method for detecting distinct input from the bar code scanner. This allows the user to scan bar codes without needing to put the focus on a special field, or activate buttons or menu items before each scan.

Background

One of my projects required integration with POS peripherals for sales, receiving, inventory, and other standard retail functions. Recently we needed to integrate bar code readers. Previously we had used bar code scanners for lookups, serial number entry, etc. But users were required to tell the software first by clicking a button, or selecting a text box first and then scanning the code. The software did not know the difference between the scanner and the keyboard. The application just took the text sent to it and was oblivious as to wheter it came from the keyboard or a bar code scanner.

This approach permits a lot of user mistakes. It is also inefficient. There are ways to directly integrate with bar code scanners using third party libraries, SDKs, or OPOS. But this seemed like a lot of fuss just to be able to get data from the bar code scanner and know it came from the bar code scanner.

Many suggested to use OPOS. In the past we had used OPOS in the past for interacting with high speed thermal printers. OPOS however relies on individual setup on each machine, and a separate OPOS driver for each piece of hardware. Vendor support for OPOS is varied, and very often the vendor drivers are shoddy and not well supported. Bar code scanners however are lower cost items and in a given site ther eare often many different brands of scanners. In the end we later swtiched to raw support for the thermal printers as well and completely eliminated OPOS and all its hassles with it.

After more research I learned that many users resorted to the WinAPI and keyboard hooks. These solutions required a lot of external calls and many relied on Windows messages which did not work as well with WPF. This method also required the developer to uniquely identify they device specifically on each machine.

First Attempt

Bar code scanners can be programmed to send a prefix and suffix for each scan. Many scanners allow you to program them with a verity of characters. For widest compatibilty STX (start of text, 0x02 ASCII) and ETX (end of text, 0x03 ASCII) are the best choices.

I created a KeyDown event in a WPF app and scanned a bar code. The first key I received was LeftCtrl. LeftCtrl??? Obviously WPF didn’t know how to handle low ASCII codes that keyboards were never designed to send. Part of this complication is because WPF generally deals with scan codes and not ASCII codes. Not all ASCII codes can map to scan codes.

I gave up this approach and tried many of the more complex WinAPI approaches to try to access the raw input from the bar code scanner. Most had problems with WPF and required a lot of adaptation because they were written for WinForms and relied on Windows messages.

I succeeeded wtih one test and became excited. But then I ran my first test and got a code of 162. 162 is the scan code for LeftCtrl. :(

Retry

Back to WPF and a simple PreviewKeyUp. I dug a bit deeper beyond the first character and noticed a pattern. I hoped that I could detect this pattern reliably and intercept the keystrokes. I made a program to log key ups and downs and realized quickly that STX (0x02) was being translated to:

  • Down: LeftCtrl
  • Down: B
  • Up: B
  • Up: LeftCtrl

Then it hit me. My previous assumptions were wrong. Windows was seeing STX (0x02). I previously assumed Windows was getting confused. In fact it was doing a bit of translation. In the older times, Ctrl + <A-Z letter> was mapped to 1-26 in ASCII. That is, Ctrl-A was equivalent to 0x01. On many older 8 bit computers you could press Ctrl-M and the computer would see it the same as Enter (0x0D). So Windows was seeing the STX (0x02) from the bar code scanner and translating it into a series of key code scans that represented Ctrl-B.

Now I had something I could work with.

The Code

I added two events to my main WPF window: PreviewKeyUp and PreviewKeyDown. I kept track of the Ctrl key state and then looked for a B to detect Ctrl-B. From there I collected all input until I found a Ctrl-C (ETX, 0x03) sequence. Note that most bar code readers do not send these sequences by default, but nearly all can be easily configured to do so.

From Ctrl-B to the Ctrl-C, I assumed all input was from the bar code scanner and set the event handled property so other events in my program would not see the data. At the end, I called an event and handed the data off to my program.

Demo

In this demo, the sole purpose of the edit field is to show that normal keyboard input is taken, but bar code scanner input is not. The listbox on the left shows the raw key scan sequences. Bar code results are shown in the listbox on the right.

The demo was built in C#, WPF, and Visual Studio 2010. It can be very easily used with VB.NET, WinForms, and other versions of Visual Studio.

BarCodeScannerReader/Untitled.png

Reuse

This method can be easily encapsulated into a class for easy reuse on any window. In my application, all of our WPF windows descend from a common base class, so I have put the functionality there. For each window, I simply need to add this in the constructor:

ScanReceived = Scan;
void Scan(string aData) {
  MessageBox.Show("Scan received: " + aData);
}

Result

Without the use of WinAPI, invasive code, or complex code, I found a method to reliably interpret bar code scanner input. In fact the actual code fits easily on a screen. With this method, there is no need to identify to the code which device is the bar code scanner. The code simply looks for patterns in the key presses and separates the data out.

Caveat

There is one small caveat. Ctrl-B cannot be used in your application for menus or other hot key functions. The code relies on this to detect the start of input from the bar code scanner. The code also looks for Ctrl-C, but only after a Ctrl-B. At other times, it ignores Ctrl-C, so the commonly used shortcut for copy to clipboard does not conflict with the use of this technique.

In most applications this is not an issue. However if you need Ctrl-B for something like Bold in a word processor, it could be a problem for you. To get around this the code will need to be upgraded to look for Ctrl-B and then wait for a certain amount of time and if no characters appear followed by a Ctrl-C, it could thne pass on the Ctrl-B to the application as normal.

Known Issues

There are a few issues when other keypress handlers exist on the window. I have addressed these in my production code and will update the demo as time permits.

Future Expansion

Bar code scanners can also be programmed to issue other prefixes to identify the type of code scanned, i.e., UPC vs. Code39. With the products we deal with, this is useful because if a UPC is scanned we know it identifies the product, while Code39 and other non UPC/EAN codes typically represent serial numbers of the item. This allows users to receive items very quickly without the need to identify first what they want to do to the software, and instead the software can infer the user intended actions based on the active window. I have expanded my code to recognize other prefixes and respond with other available events.

This article, along with any associated source code and files, is licensed under The BSD License

  • Download demo — 8.92 KB

Introduction

Most bar code scanners emulate keyboards. This allows them to be easily used in any application. The user simply needs to put the focus on any text input control and then scan a bar code.

This same behaviour also allows the user to scan bar codes into fields not intended for scanning and can cause problem. This method does not allow the application to take instant action when a scan is received because the application has no way of distinguishing the bar code scanner input from the keyboard.

Many vendors have their own APIs which can be used to distinguish the bar code scanner from the keyboard. In the real world, there are thousands of bar code scanners and relying on vendor specific APIs is not practical. Many of the generic or clone scanners which are now a common commodity have no API and rely solely on keyboard emulation which further complicates the issue.

Fortunately, there is an easy yet not well known method for detecting distinct input from the bar code scanner. This allows the user to scan bar codes without needing to put the focus on a special field, or activate buttons or menu items before each scan.

Background

One of my projects required integration with POS peripherals for sales, receiving, inventory, and other standard retail functions. Recently we needed to integrate bar code readers. Previously we had used bar code scanners for lookups, serial number entry, etc. But users were required to tell the software first by clicking a button, or selecting a text box first and then scanning the code. The software did not know the difference between the scanner and the keyboard. The application just took the text sent to it and was oblivious as to wheter it came from the keyboard or a bar code scanner.

This approach permits a lot of user mistakes. It is also inefficient. There are ways to directly integrate with bar code scanners using third party libraries, SDKs, or OPOS. But this seemed like a lot of fuss just to be able to get data from the bar code scanner and know it came from the bar code scanner.

Many suggested to use OPOS. In the past we had used OPOS in the past for interacting with high speed thermal printers. OPOS however relies on individual setup on each machine, and a separate OPOS driver for each piece of hardware. Vendor support for OPOS is varied, and very often the vendor drivers are shoddy and not well supported. Bar code scanners however are lower cost items and in a given site ther eare often many different brands of scanners. In the end we later swtiched to raw support for the thermal printers as well and completely eliminated OPOS and all its hassles with it.

After more research I learned that many users resorted to the WinAPI and keyboard hooks. These solutions required a lot of external calls and many relied on Windows messages which did not work as well with WPF. This method also required the developer to uniquely identify they device specifically on each machine.

First Attempt

Bar code scanners can be programmed to send a prefix and suffix for each scan. Many scanners allow you to program them with a verity of characters. For widest compatibilty STX (start of text, 0x02 ASCII) and ETX (end of text, 0x03 ASCII) are the best choices.

I created a KeyDown event in a WPF app and scanned a bar code. The first key I received was LeftCtrl. LeftCtrl??? Obviously WPF didn’t know how to handle low ASCII codes that keyboards were never designed to send. Part of this complication is because WPF generally deals with scan codes and not ASCII codes. Not all ASCII codes can map to scan codes.

I gave up this approach and tried many of the more complex WinAPI approaches to try to access the raw input from the bar code scanner. Most had problems with WPF and required a lot of adaptation because they were written for WinForms and relied on Windows messages.

I succeeeded wtih one test and became excited. But then I ran my first test and got a code of 162. 162 is the scan code for LeftCtrl. :(

Retry

Back to WPF and a simple PreviewKeyUp. I dug a bit deeper beyond the first character and noticed a pattern. I hoped that I could detect this pattern reliably and intercept the keystrokes. I made a program to log key ups and downs and realized quickly that STX (0x02) was being translated to:

  • Down: LeftCtrl
  • Down: B
  • Up: B
  • Up: LeftCtrl

Then it hit me. My previous assumptions were wrong. Windows was seeing STX (0x02). I previously assumed Windows was getting confused. In fact it was doing a bit of translation. In the older times, Ctrl + <A-Z letter> was mapped to 1-26 in ASCII. That is, Ctrl-A was equivalent to 0x01. On many older 8 bit computers you could press Ctrl-M and the computer would see it the same as Enter (0x0D). So Windows was seeing the STX (0x02) from the bar code scanner and translating it into a series of key code scans that represented Ctrl-B.

Now I had something I could work with.

The Code

I added two events to my main WPF window: PreviewKeyUp and PreviewKeyDown. I kept track of the Ctrl key state and then looked for a B to detect Ctrl-B. From there I collected all input until I found a Ctrl-C (ETX, 0x03) sequence. Note that most bar code readers do not send these sequences by default, but nearly all can be easily configured to do so.

From Ctrl-B to the Ctrl-C, I assumed all input was from the bar code scanner and set the event handled property so other events in my program would not see the data. At the end, I called an event and handed the data off to my program.

Demo

In this demo, the sole purpose of the edit field is to show that normal keyboard input is taken, but bar code scanner input is not. The listbox on the left shows the raw key scan sequences. Bar code results are shown in the listbox on the right.

The demo was built in C#, WPF, and Visual Studio 2010. It can be very easily used with VB.NET, WinForms, and other versions of Visual Studio.

BarCodeScannerReader/Untitled.png

Reuse

This method can be easily encapsulated into a class for easy reuse on any window. In my application, all of our WPF windows descend from a common base class, so I have put the functionality there. For each window, I simply need to add this in the constructor:

ScanReceived = Scan;
void Scan(string aData) {
  MessageBox.Show("Scan received: " + aData);
}

Result

Without the use of WinAPI, invasive code, or complex code, I found a method to reliably interpret bar code scanner input. In fact the actual code fits easily on a screen. With this method, there is no need to identify to the code which device is the bar code scanner. The code simply looks for patterns in the key presses and separates the data out.

Caveat

There is one small caveat. Ctrl-B cannot be used in your application for menus or other hot key functions. The code relies on this to detect the start of input from the bar code scanner. The code also looks for Ctrl-C, but only after a Ctrl-B. At other times, it ignores Ctrl-C, so the commonly used shortcut for copy to clipboard does not conflict with the use of this technique.

In most applications this is not an issue. However if you need Ctrl-B for something like Bold in a word processor, it could be a problem for you. To get around this the code will need to be upgraded to look for Ctrl-B and then wait for a certain amount of time and if no characters appear followed by a Ctrl-C, it could thne pass on the Ctrl-B to the application as normal.

Known Issues

There are a few issues when other keypress handlers exist on the window. I have addressed these in my production code and will update the demo as time permits.

Future Expansion

Bar code scanners can also be programmed to issue other prefixes to identify the type of code scanned, i.e., UPC vs. Code39. With the products we deal with, this is useful because if a UPC is scanned we know it identifies the product, while Code39 and other non UPC/EAN codes typically represent serial numbers of the item. This allows users to receive items very quickly without the need to identify first what they want to do to the software, and instead the software can infer the user intended actions based on the active window. I have expanded my code to recognize other prefixes and respond with other available events.

This article, along with any associated source code and files, is licensed under The BSD License

Понравилась статья? Поделить с друзьями:
  • Как написать программу на паскале для вычисления выражения
  • Как написать программу для рисования
  • Как написать программу на паскале ветвление
  • Как написать программу для решения уравнений на python
  • Как написать программу на паскале abc