четверг, 21 июня 2012 г.

Генерация QR-кода в Android-приложении

Недавно познакомился поближе с замечательной технологией: QR-кодами. Простая вроде бы вещь, а какой простор для фантазии открывается! Тут тебе и обмен ссылками между Android-устройствами, передача контактов, своей "визитки", авторизация... Впрочем, оставляю вам придумать свою идею самостоятельно, а пока вы думаете, покажу как работать с QR-кодами программно. Напишем свой простенький генератор QR-кодов. Сделать это совсем несложно: приложение, скриншот которого вы видите справа, я сделал примерно за 10 минут.
Работу этого приложения вы, кстати, можете проверить тут же: просто наведите на скриншот любой сканер QR-кодов и сравните полученную сторку с тем, что введено в EditText вверху.
Итак, приступим.

Всё что нужно, уже написано

Для работы с QR-кодами, штрихкодами, DataMatrix и прочими визуальными кодами есть много библиотек. Лучшая (и самая монстроидальная) из них - ZXing. У неё есть реализации для десятка языков, свой клиент под Android и ещё много чего. Нам из всего этого богатства понадобится только core.jar весом 400 kb. В архиве версии 2.0 он лежит в zxing-2.0/core.

Используем это

Преобразование строки в картинку в QR-кодом мы сделаем в отдельном потоке (всё-таки это не мгновенная операция). Класс для кодирования выглядит так:

package net.multipi.qrg;
 
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
 
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.DisplayMetrics;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import android.widget.ImageView;
 
public class Coder extends AsyncTask<String, Integer, ImageView> {
 
 private Activity act;
 private ProgressDialog dialog;
 private static final int BLACK = 0xFF000000;
 private static final int WHITE = 0xFFFFFFFF;
 private static final int ID = 34646456;
 
 public Coder(Activity act) {
  this.act = act;
 }
 
 @Override
 protected void onPreExecute() {
  dialog = new ProgressDialog(act);
  dialog.setTitle("Encoding");
  dialog.setMessage("Please, wait...");
  dialog.setCancelable(false);
  dialog.show();
  super.onPreExecute();
 }
 
 @Override
 protected ImageView doInBackground(String... params) {
  DisplayMetrics dm = new DisplayMetrics();
  act.getWindowManager().getDefaultDisplay().getMetrics(dm);
  int width = dm.widthPixels;
  ImageView image = new ImageView(act);
  try {
   BitMatrix matrix = new QRCodeWriter().encode(params[0],
     com.google.zxing.BarcodeFormat.QR_CODE, width, width);
   image.setImageBitmap(matrixToBitmap(matrix));
  } catch (WriterException e) {
   e.printStackTrace();
  }
  image.setId(ID);
  return image;
 }
 
 @Override
 protected void onPostExecute(ImageView image) {
  try {
   dialog.dismiss();
  } catch (Exception e) {
   e.printStackTrace();
  }
  ImageView old = (ImageView) act.findViewById(ID);
  if (old != null) {
   ((FrameLayout) old.getParent()).removeViewInLayout(old);
  }
  act.addContentView(image, new LayoutParams(LayoutParams.FILL_PARENT,
    LayoutParams.FILL_PARENT));
  super.onPostExecute(image);
 }
 
 private Bitmap matrixToBitmap(BitMatrix matrix) {
  int width = matrix.getWidth();
  int height = matrix.getHeight();
  Bitmap image = Bitmap.createBitmap(width, height,
    Bitmap.Config.ARGB_8888);
  for (int x = 0; x < width; x++) {
   for (int y = 0; y < height; y++) {
    image.setPixel(x, y, matrix.get(x, y) ? BLACK : WHITE);
   }
  }
  return image;
 }
}
 
В onPreExecute мы просто включаем прелоадер. В doInBackground происходит самое интересное: с помощью QRcodeWriter-а мы превращаем строку в BitMatrix, потом в Bitmap, из которого в конце концов получаем ImageView, который и возвращается нам в основной поток в методе onPostExecute. Добавляем его на нашу Activity, при этом если предидущая картинка уже есть, убираем её.

Показываем результат

Теперь код главного Activity. Ничего особенного в нём нет, просто для полноты картины:

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
 
public class QRGeneratorActivity extends Activity {
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
 
  LinearLayout ll = new LinearLayout(this);
  ll.setOrientation(LinearLayout.VERTICAL);
 
  LinearLayout top = new LinearLayout(this);
 
  final EditText in = new EditText(this);
  in.setHint("You text");
  in.setLayoutParams(new LinearLayout.LayoutParams(
    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1f));
  top.addView(in);
 
  Button gen = new Button(this);
  gen.setText("Generate");
  gen.setLayoutParams(new LinearLayout.LayoutParams(
    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 2.5f));
  gen.setOnClickListener(new View.OnClickListener() {
 
   @Override
   public void onClick(View v) {
    new Coder(QRGeneratorActivity.this).execute(in.getText()
      .toString());
 
   }
  });
  top.addView(gen);
 
  ll.addView(top, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
 
  setContentView(ll);
 }
}

Как видите, свой QR-code генератор можно сделать очень просто. С распознаванием QR-кодов немного сложнее, но это мы разберём уже в следующих статьях.

6 комментариев:

  1. Спасибо. Нельзя ли выложить исходники на getHub?

    ОтветитьУдалить
  2. Этот комментарий был удален автором.

    ОтветитьУдалить
  3. Очень нужен весь исходник! Если можно сбросьте на vadim-liski@mail.ru

    ОтветитьУдалить
  4. Спасибо огромное, а то я голову сломал как использовать zxing по простому. ListView только у меня закрывал кнопку и поле ввода, пришлось сдвинуть вниз принудительно.

    ОтветитьУдалить
  5. Не ListView, а ImageView, попутал

    ОтветитьУдалить
  6. Этот комментарий был удален автором.

    ОтветитьУдалить