必要な条件
このチュートリアルでは, 128 codeの生成や読み取ることができるAndroid アプリを作っていきたいと思います。 チュートリアルに沿っていくには、次の条件が必要になります:
テスト用のAndroidスマホ
Visual Studio Code(他のIDEでも良い)
Node (npm)
Reactの基礎知識
準備
Expo CLI
パソコンのコマンドラインでExpo CLIをインストールしましょう:npm install -g expo-cli
。アカウントを持っていればexpo login
実行します。アカウントを持っていなければ、expo register
を実行します。
Expoアプリ
AndroidスマホでExpoアプリをダウンロードします。ダウンロードが終わってから、アプリを開きます。「Profile」タブでログインが出来ます。
アプリ作成
好きなIDEのターミナルで次のコードを実行します:
expo init barcode-scanner
。「Choose a template」」が表示される時に「blank」を選びます。
cd barcode-scanner
を実行して、必要なパッケージをインストールします:npm i expo-barcode-scanner jsbarcode react-native-svg
npm start
を実行します。Expoアプで、「Scan QR Code」をクリックして、http://localhost:19002/ で表示されているQRコードをスキャンします。ダウンロードが終わってから、スマホでアプリが開きます。
開発中、コードを変更する時にスマホで表示されるまでに時間が掛かる可能性があります。その場合には、http://localhost:19002/ で「Run in web browser」をクリックするとパソコンのブラウザーで、アプリの様子が確認できます。
App.jsx
App.jsxのコードをすべてを削除し、以下のコードに置き換えます。
import React, { useState } from "react";
const App = () => {
const [mode, setMode] = useState<ModeType>("welcome");
return (
<>
</>
);
};
export default App;
ホーム画面
プロジェクトのルートで、componentsフォルダを作りましょう。そのフォルダで、Welcome.jsxファイル作成します。
import { StyleSheet, Text, View, Pressable } from "react-native";
import React from "react";
export default function Welcome({ setMode }) {
return (
<View style={styles.container}>
<Pressable onPress={() => setMode("scan")} style={styles.button}>
<Text style={styles.text}>バーコードを読み取る</Text>
</Pressable>
<Pressable onPress={() => setMode("generate")} style={styles.button}>
<Text style={styles.text}>バーコードを生成する</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
button: {
paddingVertical: 12,
paddingHorizontal: 32,
marginVertical: 20,
borderRadius: 4,
elevation: 3,
backgroundColor: "blue",
},
text: {
fontSize: 16,
lineHeight: 21,
fontWeight: "bold",
letterSpacing: 0.25,
color: "white",
},
});
WelcomeコンポーネントをApp.jsxに追加します。
import React, { useState } from "react";
import Welcome from "./components/Welcome";
...
return (
<>
{mode === "welcome" && <Welcome setMode={setMode} />}
</>
);
};
...
バーコードの読取
バーコードスキャナーを作成していきます。componentsフォルダで、Scanner.jsxを作ります。
import { StyleSheet, Text, View, Button, Pressable } from "react-native";
import React, { useState, useEffect } from "react";
import { BarCodeScanner } from "expo-barcode-scanner";
export default function Scanner({ setMode }) {
const [hasPermission, setHasPermission] = useState(null || Boolean);
const [scanned, setScanned] = useState(false);
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === "granted");
})();
}, []);
//バーコードがスキャンされた時に
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
alert(`バーコードタイプ: ${type}。 バーコードデータ: ${data}`);
};
return (
<View style={styles.container}>
{/* カメラアにクセスすることがまだ許可も拒否もされていない場合 */}
{hasPermission === null && <Text>カメラの許可を要求する</Text>}
{/* カメラアにクセスすることが拒否されている合 */}
{hasPermission === false && <Text>カメラにアクセスできません</Text>}
<Pressable style={styles.button} onPress={() => setMode("welcome")}>
<Text style={styles.buttonText}>戻る</Text>
</Pressable>
{/* カメラアにクセスすることが許可されている場合 */}
{hasPermission && (
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
)}
{/* スキャンが終わってから表示する */}
{scanned && (
<Button
title={"別のバーコードを読み取る"}
onPress={() => setScanned(false)}
/>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
button: {
zIndex: 100,
position: "absolute",
bottom: 40,
paddingVertical: 12,
paddingHorizontal: 32,
borderRadius: 4,
elevation: 3,
backgroundColor: "blue",
},
buttonText: {
color: "white",
},
});
ScannerコンポーネントをApp.jsxに追加します。
...
import Welcome from "./components/Welcome";
import Scanner from "./components/Scanner";
...
return (
<>
{mode === "welcome" && <Welcome setMode={setMode} />}
{mode === "scan" && <Scanner setMode={setMode} />}
</>
);
};
...
バーコードの生成
componentsフォルダで、Generate.jsxを作ります。
import React, { useState } from "react";
import { Pressable, StyleSheet, Text, TextInput, View } from "react-native";
import Barcode from "./Barcode";
export default function Generate({ setMode }) {
const [inputText, onChangeInputText] = useState("");
const [barcodeValue, setBarcodeValue] = useState("");
//ローマ字や数字以外が入ってないことを確認する
function onlyLettersAndNumbers(str) {
return /^[A-Za-z0-9]*$/.test(str);
}
const generateBarcode = () => {
if (onlyLettersAndNumbers(inputText)) {
setBarcodeValue(inputText);
} else {
alert("ローマ字や数字を入力してください");
}
};
return (
<View style={styles.container}>
<Text style={styles.text}>10文字まで入力してください</Text>
<TextInput
style={styles.input}
maxLength={10}
value={inputText}
onChangeText={onChangeInputText}
/>
<Pressable style={styles.button} onPress={generateBarcode}>
<Text style={styles.buttonText}>バーコードを生成する</Text>
</Pressable>
{barcodeValue ? <Barcode value={barcodeValue} /> : <Text></Text>}
<Pressable style={styles.backButton} onPress={() => setMode("welcome")}>
<Text style={styles.buttonText}>戻る</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
alignItems: "center",
justifyContent: "center",
},
input: {
height: 40,
marginHorizontal: 12,
marginVertical: 20,
borderWidth: 1,
padding: 10,
width: 200,
backgroundColor: "white",
},
button: {
paddingVertical: 12,
paddingHorizontal: 32,
borderRadius: 4,
elevation: 3,
backgroundColor: "blue",
marginBottom: 20,
},
buttonText: {
color: "white",
},
text: {
paddingHorizontal: 20,
},
backButton: {
position: "absolute",
bottom: 40,
paddingVertical: 12,
paddingHorizontal: 32,
borderRadius: 4,
elevation: 3,
backgroundColor: "blue",
},
});
GenerateコンポーネントをApp.jsxに追加します。
...
import Generate from "./components/Generate";
...
return (
<>
{mode === "welcome" && <Welcome setMode={setMode} />}
{mode === "scan" && <Scanner setMode={setMode} />}
{mode === "generate" && <Generate setMode={setMode} />}
</>
);
};
...
componentsフォルダで、Barcode.jsxを作ります。
import React, { useEffect, useState } from "react";
import Svg, { G } from "react-native-svg";
import JSBarcode from "jsbarcode";
import BarcodeText from "./barcode/BarcodeText";
import BarcodeChunk from "./barcode/BarcodeChunk";
const Barcode = ({ value }) => {
const [encoding, setEncoding] = useState();
const [maxHeight, setMaxHeight] = useState(0);
const [width, setWidth] = useState(0);
useEffect(() => {
//すべてのオプションをマージする
const merge = (old, replaceObj) => {
return { ...old, ...replaceObj };
};
//エンコーディングの高さを計算する
function getEncodingHeight(options) {
return (
options.height +
options.fontSize +
options.textMargin +
options.marginTop +
options.marginBottom
);
}
//エンコーディング属性を計算する
function calculateEncodingAttributes(encoding, barcodeOptions) {
const options = merge(barcodeOptions, encoding.options);
encoding.width = encoding.data.length * options.width;
encoding.height = getEncodingHeight(options);
encoding.barcodePadding = 0;
}
const createBarcode = () => {
let barcode = {};
//全てのオプションとデフォルトオプションがhttps://github.com/lindell/JsBarcode/wiki/Optionsで見える
let options = { format: "CODE128", marginLeft: 10, marginRight: 10 };
JSBarcode(barcode, value, options);
let encoding = barcode.encodings[0];
calculateEncodingAttributes(encoding, options);
setEncoding(encoding);
const totalWidth = encoding.width;
setMaxHeight(encoding.height);
setWidth(totalWidth + options.marginLeft + options.marginRight);
};
createBarcode();
}, [value]);
return (
<Svg
x={0}
y={0}
width={width}
height={maxHeight}
viewBox={`0 0 ${width} ${maxHeight}`}
originX={0}
originY={0}
>
{/* encodingが未定義ではない場合にのみ表示する */}
{encoding && (
<G
x={encoding.options.marginLeft}
y={encoding.options.marginTop}
fill={encoding.options.lineColor}
>
{/* バーコード */}
<BarcodeChunk
binary={encoding.data}
padding={0}
options={encoding.options}
/>
{/* バーコードの下に表示されるテキスト */}
<BarcodeText
text={encoding.text}
width={encoding.width}
padding={0}
options={encoding.options}
/>
</G>
)}
</Svg>
);
};
export default Barcode;
最後にBarcodeTextとBarcodeChunkコンポーネントを作ります。
import { Text } from "react-native-svg";
export default function BarcodeText(props) {
const { text, width, options } = props;
if (options.displayValue) {
const y = options.height + options.textMargin + options.fontSize;
const x = width / 2;
return (
<Text
x={x}
y={y}
fontFamily={options.font}
fontSize={options.fontSize}
fontWeight={"bold"}
textAnchor={"middle"}
>
{text}
</Text>
);
} else {
return null;
}
}
import { Rect } from "react-native-svg";
const BarcodeChunk = ({ binary, options }) => {
let barWidth = 0;
let x = 0;
let bars = [];
for (var b = 0; b < binary.length; b++) {
x = b * options.width;
if (binary[b] === "1") {
barWidth++;
} else if (barWidth > 0) {
bars.push({
x: x - options.width * barWidth,
y: 0,
width: options.width * barWidth,
height: options.height,
});
barWidth = 0;
}
}
if (barWidth > 0) {
bars.push({
x: x - options.width * (barWidth - 1),
y: 0,
width: options.width * barWidth,
height: options.height,
});
}
return (
<>
{bars.map((bar, i) => (
<Rect
key={i}
x={bar.x}
y={bar.y}
width={bar.width}
height={bar.height}
/>
))}
</>
);
};
export default BarcodeChunk;
これで、128 codeの生成や読み取ることができるAndroid アプリが完了です。Androidスマホで、バーコードを読み取るや生成してみてください!