Androidアプリで1次元バーコードを作成する方法2
前回は「ZXing」ライブラリを使用して、Androidアプリで1次元バーコードを作成・表示する方法を紹介した。
今回はライブラリを使用せず、全て自前で1次元バーコードを作成・表示する方法を紹介する。
今回も1次元バーコード規格は「CODABAR(NW-7)」に限定するものとする。
<前置き>
「ZXing」は優れたバーコード作成・読取り用のライブラリである。
Androidアプリで1次元バーコードを作成・表示する必要があり、「ZXing」が使用できるのであれば、迷わず「ZXing」を使用する事をお勧めする。
今回紹介するライブラリを使用せず、1次元バーコードを表示する方法は、何らかの事情で「ZXing」が使用できない場合の方法と考えて頂きたい。
参考までに筆者が「ZXing」を使用できなかった事情を簡単に紹介する。
・アプリのapkファイルサイズを1KBでも削減する必要があった
・バーコードに一部独自の仕様を実装する必要があった
<ソースコード(ライブラリの代替側)>
「CODABAR(NW-7)」規格の1次元バーコードを作成するために、「ZXing」ライブラリの代替として、「MyCodaBarWriter」と「MyBitMatrix」の2クラスを作成した。
MyCodaBarWriter.java
package biz.accele.samplebarcode; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyCodaBarWriter { //スタートキャラクタ、ストップキャラクタ(A,B,C,Dの中から選択する) private final String START_CHAR = "A"; private final String END_CHAAR = "A"; //バーコードの両端に設けるマージンサイズ(クワイエットゾーン)を棒幅の何個分かで指定する private final int SIDE_MARGIN_NUM = 10; //エレメントの値 private final int WHITE = 0; //白エレメント private final int BLACK = 1; //黒エレメント //変換用情報(CODABARの仕様より作成) private final int[] convert0 = {BLACK, WHITE, BLACK, WHITE, BLACK, WHITE, WHITE, BLACK, BLACK}; private final int[] convert1 = {BLACK, WHITE, BLACK, WHITE, BLACK, BLACK, WHITE, WHITE, BLACK}; private final int[] convert2 = {BLACK, WHITE, BLACK, WHITE, WHITE, BLACK, WHITE, BLACK, BLACK}; private final int[] convert3 = {BLACK, BLACK, WHITE, WHITE, BLACK, WHITE, BLACK, WHITE, BLACK}; private final int[] convert4 = {BLACK, WHITE, BLACK, BLACK, WHITE, BLACK, WHITE, WHITE, BLACK}; private final int[] convert5 = {BLACK, BLACK, WHITE, BLACK, WHITE, BLACK, WHITE, WHITE, BLACK}; private final int[] convert6 = {BLACK, WHITE, WHITE, BLACK, WHITE, BLACK, WHITE, BLACK, BLACK}; private final int[] convert7 = {BLACK, WHITE, WHITE, BLACK, WHITE, BLACK, BLACK, WHITE, BLACK}; private final int[] convert8 = {BLACK, WHITE, WHITE, BLACK, BLACK, WHITE, BLACK, WHITE, BLACK}; private final int[] convert9 = {BLACK, BLACK, WHITE, BLACK, WHITE, WHITE, BLACK, WHITE, BLACK}; private final int[] convertA = {BLACK, WHITE, BLACK, BLACK, WHITE, WHITE, BLACK, WHITE, WHITE, BLACK}; private final int[] convertB = {BLACK, WHITE, WHITE, BLACK, WHITE, WHITE, BLACK, WHITE, BLACK, BLACK}; private final int[] convertC = {BLACK, WHITE, BLACK, WHITE, WHITE, BLACK, WHITE, WHITE, BLACK, BLACK}; private final int[] convertD = {BLACK, WHITE, BLACK, WHITE, WHITE, BLACK, BLACK, WHITE, WHITE, BLACK}; //変換用情報をMap型に格納する private final Map<String, Object> mapConvert = new HashMap<String, Object>(){ { put("0", convert0); put("1", convert1); put("2", convert2); put("3", convert3); put("4", convert4); put("5", convert5); put("6", convert6); put("7", convert7); put("8", convert8); put("9", convert9); put("A", convertA); put("B", convertB); put("C", convertC); put("D", convertD); } }; public MyBitMatrix encode(String targetData, int width, int height){ //スタートキャラクタ、ストップキャラクタの付与 String exTargetData = START_CHAR + targetData + END_CHAAR; //データの変換 List<Integer> codeArray = convertData(exTargetData); //バーコードデータの生成 MyBitMatrix bitMatrix = createBarcode(codeArray, width, height); return bitMatrix; } // データを変換する private List<Integer> convertData(String targetData){ List<Integer> resultArray = new ArrayList<>(); //サイドマージン(左) for(int i=0; i<SIDE_MARGIN_NUM; i++){ resultArray.add(WHITE); } //データ変換 for(int i=0; i<targetData.length(); i++){ // 先頭から1文字づつ取り出す String targetChar = targetData.substring(i, i+1); // 取り出した文字を変換する joinArray(resultArray, targetChar); // キャラクターギャップ resultArray.add(WHITE); } // 最後のキャラクターギャップを削除する resultArray.remove(resultArray.size() - 1); //サイドマージン(右) for(int i=0; i<SIDE_MARGIN_NUM; i++){ resultArray.add(WHITE); } return resultArray; } //Listの末尾に文字を変換した情報を付け加える private void joinArray(List<Integer> resultArray, String targetChar){ int[] targetArray = (int[])mapConvert.get(targetChar); for(int i=0; i<targetArray.length; i++){ resultArray.add(targetArray[i]); } } //バーコード情報を生成する private MyBitMatrix createBarcode(List<Integer> targetData, int width, int height) { // バー幅の算出 int multiple = getBarWidth(targetData.size(), width); // バーコード描画サイズの算出 int drawPx = targetData.size() * multiple; // 要求幅に合わせるために必要な調整幅を算出する int leftSideAdjust = (width - drawPx) / 2; // 結果を格納するためのBitMatrixをインスタンス化する MyBitMatrix output = new MyBitMatrix(width, height); // 制御変数の初期化 int outputX = leftSideAdjust; for (int inputX = 0; inputX < targetData.size(); inputX++) { if (targetData.get(inputX).intValue() == BLACK) { output.setRegion(outputX, 0, multiple, height); outputX += multiple; }else{ outputX += multiple; } } return output; } // 指定されたピクセル数を超えず、かつ最大限大きく描画するためにバー幅の最適値を算出する private int getBarWidth(int barCount, int viewWidth){ int resultMultiple = 1; int multiple = 1; while(true){ int px = barCount * multiple; if(px > viewWidth){ break; }else{ resultMultiple = multiple; } multiple += 1; } return resultMultiple; } }
MyBitMatrix.java
package biz.accele.samplebarcode; public final class MyBitMatrix { private final int rowSize; private final int[] bits; public MyBitMatrix(int width, int height) { this.rowSize = (width + 31) / 32; bits = new int[rowSize * height]; } public boolean get(int x, int y) { int offset = y * rowSize + (x / 32); return ((bits[offset] >>> (x & 0x1f)) & 1) != 0; } public void setRegion(int left, int top, int width, int height) { int right = left + width; int bottom = top + height; for (int y = top; y < bottom; y++) { int offset = y * rowSize; for (int x = left; x < right; x++) { bits[offset + (x / 32)] |= 1 << (x & 0x1f); } } } }
<ソースコード(呼び出し側)>
前回の「ZXing」ライブラリを使用する場合と比較して、変更点は多くない。
(activity_main.xmlに関しては完全に同一である)
MainActivity.java
package biz.accele.samplebarcode; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Matrix; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ImageView; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ImageView imageView = (ImageView) findViewById(R.id.result_view); // バーコードの各種設定 String targetData = "123456789012345"; //バーコードに変換する対象データ int width = 1000; //作成するバーコードの幅 int height = 200; //作成するバーコードの高さ // データ変換用クラスをインスタンス化する // 変更点1 // ・CodaBarWriter -> MyCodaBarWriter MyCodaBarWriter writer = new MyCodaBarWriter(); try { // 対象データを変換する // 変更点2 // ・BitMatrix -> MyBitMatrix // ・引数の数が少なくなった MyBitMatrix bitMatrix = writer.encode(targetData, width, height); // BitMatrixのデータが「true」の時は「黒」を設定し、「false」の時は「白」を設定する int[] pixels = new int[width * height]; for (int y = 0; y < height; y++) { int offset = y * width; for (int x = 0; x < width; x++) { pixels[offset + x] = bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE; } } // ビットマップ形式に変換する Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); //ビットマップの回転(縦に表示するため) Matrix mat = new Matrix(); mat.postRotate(90); Bitmap bmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, mat, true); // イメージビューに表示する imageView.setImageBitmap(bmp); } catch (Exception e) {} } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <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" android:background="#F0E68C" tools:context="biz.accele.samplebarcode.MainActivity"> <!-- バーコードを表示するためのImageView --> <ImageView android:id="@+id/result_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> </RelativeLayout>
<実行結果>