今天來給各位一個我覺得很實用的東西,用過安卓手機的人不知道有沒有遇到需要掃描條碼卻沒有條碼掃描器的困境呢,這時通常會去Google play 商店下載一些看起來很精美,卻充斥一堆廣告的app,但看完今天的文章,你/妳也可以製作出一個屬於自己的QRCode掃描和產生器,重點是沒有廣告喔~~! 聽起來是不是很吸引人阿,那我們開始吧!
<?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=".MainActivity">
<TextView
android:id="@+id/result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="結果:"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/guideline6"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.15328467" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.85" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.7" />
<SurfaceView
android:id="@+id/qrCode_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/guideline5"
app:layout_constraintEnd_toStartOf="@+id/guideline3"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline4" />
<Button
android:id="@+id/encoder_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="開始掃描"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline13"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline6" />
<TextView
android:id="@+id/result_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/guideline6"
app:layout_constraintStart_toEndOf="@+id/result" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline13"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.50121653" />
<Button
android:id="@+id/goTo_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="前往"
android:enabled="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline3"
app:layout_constraintStart_toStartOf="@+id/guideline13"
app:layout_constraintTop_toTopOf="@+id/guideline6" />
<Button
android:id="@+id/constructQrCode_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="產生QRCode"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
因為我有多做額外的功能,如果不需要的可以拉個SurfaceView就行了喔,還有SurfaceView通常會被用來做動畫和畫圖,不過今天我們是用來當我們的掃描器畫面,有興趣的可以去研究看看SurfaceView,挺有趣的,這邊就不多贅述拉。
2. 實例化及建立相機和偵測器
private CameraSource cameraSource;
private BarcodeDetector barcodeDetector;
private SurfaceView qrCode_view;
-------------------------------------------------
qrCode_view = findViewById(R.id.qrCode_view);
//條碼感應器
barcodeDetector = new BarcodeDetector.Builder(this)
//設定感測模式(QRCode)
.setBarcodeFormats(Barcode.QR_CODE).build();
//關於相機內容設定
cameraSource = new CameraSource.Builder(this,barcodeDetector)
.setAutoFocusEnabled(true).build();
//新增SurfaceHolder的CallBack
qrCode_view.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
//偵測權限
if(ActivityCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED) {
Log.e("流程", "surfaceCreated:沒權限" );
return;
}
try{
Log.e("流程", "surfaceCreated:開啟相機");
cameraSource.start(surfaceHolder);
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder
, int i, int i1, int i2) {
//相機狀態改變
Log.e("流程", "surfaceChanged:");
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
Log.e("流程", "surfaceDestroyed:");
//相機暫停
cameraSource.stop();
}
});
//偵測結果
barcodeDetector.setProcessor(new Detector.Processor<Barcode>(){
@Override
public void release(){
}
//產生結果時
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
//獲取偵測結果
final SparseArray<Barcode> result=detections.getDetectedItems();
if(result.size()!=0){
//回到UI線程,更新UI
runOnUiThread(()->{
result_txt.setText(result.valueAt(0).displayValue);
goTo_btn.setEnabled(true);
});
}
}
});
這裡建立了相機和偵測器,可以想像確實身為一格感測器這些是必要的東西,這裡的關係是相機(CameraSource)包含偵測器(BarcodeDetector),且BarcodeDetector需要設定偵測結果的回傳,而surfaceView負責管理相機的開啟狀態。
基本上這樣就可以開始掃描了,我們來看一下效果。
<?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=".BarCodeEncoderActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.15" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.85" />
<EditText
android:id="@+id/content_edt"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@null"
android:ems="10"
android:hint="請輸入內容"
android:inputType="textPersonName"
android:padding="5dp"
app:layout_constraintBottom_toTopOf="@+id/guideline10"
app:layout_constraintEnd_toStartOf="@+id/guideline8"
app:layout_constraintStart_toStartOf="@+id/guideline7"
app:layout_constraintTop_toTopOf="@+id/guideline9" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.10" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.15" />
<ImageView
android:id="@+id/result_img"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/guideline12"
app:layout_constraintEnd_toStartOf="@+id/guideline8"
app:layout_constraintHorizontal_bias="0.493"
app:layout_constraintStart_toStartOf="@+id/guideline7"
app:layout_constraintTop_toTopOf="@+id/guideline11" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.25" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.65" />
<Button
android:id="@+id/transform_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="轉換"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline8"
app:layout_constraintStart_toStartOf="@+id/guideline7"
app:layout_constraintTop_toBottomOf="@+id/result_img" />
</androidx.constraintlayout.widget.ConstraintLayout>
簡單的拉出一個輸入框和負責轉換的按鈕。
2. 建立BarcodeEncoder並加入Button內
private Button transform_btn;
------------------------------------------------------
transform_btn = (Button)findViewById(R.id.transform_btn);
transform_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//BarcodeEncoder (將資料負責轉換成Barcode)
BarcodeEncoder encoder = new BarcodeEncoder();
try{
//將輸入的內容轉換,大小不宜太小會模糊
Bitmap bit = encoder.encodeBitmap(content_edt.getText()
.toString(), BarcodeFormat.QR_CODE,250,250);
//設定至imageView顯示
result_img.setImageBitmap(bit);
}catch (WriterException e){
e.printStackTrace();
}
}
});
轉換QRCode就相對比降簡單明瞭,唯一要注意的就是圖片不要用得太小,不然會很模糊相這樣:
所以注意一下,找一下適當的大小生成喔。