안녕하세요.
안드로이드 스튜디오 java언어로 QR코드 스캐너를 만들 일이 있었는데 제대로 정리를 하기 위해 글을 써봅니다.
1. 준비
저는 ml kit의 바코드 스캐닝을 받아와서 써야하기 때문에 앱 모듈의 그레이들에 필요한 요소들을 추가해줍니다.
디버그용과 릴리즈용, 출시된 앱에 따라 추가해야하는 코드가 다르니 꼭 확인 후 추가해주세요.
https://developers.google.com/ml-kit/vision/barcode-scanning/android
Scan Barcodes with ML Kit on Android | Google Developers
Scan Barcodes with ML Kit on Android You can use ML Kit to recognize and decode barcodes. There are two ways to integrate barcode scanning: by bundling the model as part of your app, or by using an unbundled model that depends on Google Play Services. If y
developers.google.com
그리고 jetpack의 카메라X를 쓸 예정이라 그것도 들고와서 추가해줍시다.
https://developer.android.com/jetpack/androidx/releases/camera
CameraX | Android 개발자 | Android Developers
CameraX CameraX가 Jetpack에 추가되어 앱에 카메라 기능을 더 쉽게 추가할 수 있습니다. 라이브러리는 다양한 호환성 수정사항과 해결 방법을 제공하여 많은 기기에서 개발자 환경을 일관되게 유지하
developer.android.com
하지만 !!!!!!
21.12.04 날짜를 기준으로 공식 문서에 있는 최신 카메라X를 적용시 sdk 31버전과 충돌이 있는 것 같아요.
그래서 다음과 같은 에러가 발생합니다.
java.lang.NoSuchMethodError: No static method getOrCreateInstance
이럴 경우 카메라X의 이전 버전을 들고와서 적용해줍니다.
def camerax_version = "1.0.1"
// CameraX core library using camera2 implementation
implementation "androidx.camera:camera-camera2:$camerax_version"
// CameraX Lifecycle Library
implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX View class
implementation "androidx.camera:camera-view:1.0.0-alpha27"
추가가 끝났다면 카메라 사용을 위한 퍼미션 체크를 해주세요.
카메라 퍼미션은 다루지 않겠습니다.
2. xml
카메라를 보여줄 화면을 만들어줍니다.
카메라X의 프리뷰를 xml에 예쁘게 넣어봅시다.
<androidx.camera.view.PreviewView
android:id="@+id/camerax_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
/>
3. 카메라 뷰 바인딩
카메라X 세팅을 해주고 이미지를 분석할 수 있는 준비를 해줍시다.
이미지를 분석하기 위해서는 executor와 analyzer가 필요합니다.
imageAnalysis.setAnalyzer(cameraExecutor, myImageAnalyzer);
(분석시키기 위해 cameraExecutor와 analyzer를 넣어줘야하는 모습)
executor는 ExecutorService 외에도 터프하게
Executor executor = Executors.newSingleThreadExecutor();
초기화 해서 사용 가능합니다. 저는 ExecutorService를 사용했습니다.
public class MainActivity extends AppCompatActivity {
private PreviewView mPreviewView;
private ListenableFuture cameraProviderFuture;
private ExecutorService cameraExecutor;
private MyImageAnalyzer myImageAnalyzer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init(){
mPreviewView = findViewById(R.id.camerax_preview);
cameraExecutor = Executors.newSingleThreadExecutor();
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
myImageAnalyzer = new MyImageAnalyzer(this.getSupportFragmentManager());
cameraProviderFuture.addListener(new Runnable() {
@Override
public void run() {
try {
ProcessCameraProvider processCameraProvider = (ProcessCameraProvider) cameraProviderFuture.get();
bindPreview(processCameraProvider);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, ContextCompat.getMainExecutor(this));
}
private void bindPreview(ProcessCameraProvider processCameraProvider) {
Preview preview = new Preview.Builder().build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
ImageCapture imageCapture =new ImageCapture.Builder().build();
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setTargetRotation(Surface.ROTATION_270)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
preview.setSurfaceProvider(mPreviewView.getSurfaceProvider());
imageAnalysis.setAnalyzer(cameraExecutor, myImageAnalyzer);
processCameraProvider.unbindAll();
processCameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, imageAnalysis);
}
이제 카메라 준비가 끝났습니다.
카메라로 가져온 이미지에서 정보를 가져오는 스캐닝 작업을 해주기 전에!!
내부 클래스로 analyzer를 만들어줍니다.
공식에서 적어준걸 가져옵시다.
https://developers.google.com/ml-kit/vision/barcode-scanning/android#java
Scan Barcodes with ML Kit on Android | Google Developers
Scan Barcodes with ML Kit on Android You can use ML Kit to recognize and decode barcodes. There are two ways to integrate barcode scanning: by bundling the model as part of your app, or by using an unbundled model that depends on Google Play Services. If y
developers.google.com
public class MyImageAnalyzer implements ImageAnalysis.Analyzer{
private FragmentManager fragmentManager;
public MyImageAnalyzer(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
}
@Override
public void analyze(@NonNull ImageProxy image) {
scanBarcode(image);
}
}
4. 바코드 스캐닝
analyzer 에서 던져주는 ImageProxy를 분석하는 코드입니다.
private void scanBarcode(ImageProxy image) {
//input image
@SuppressLint("UnsafeOptInUsageError") Image image1 = image.getImage();
InputImage inputImage = InputImage.fromMediaImage(image1, image.getImageInfo().getRotationDegrees());
BarcodeScannerOptions options =
new BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE,
Barcode.FORMAT_AZTEC)
.build();
//Get an instance of BarcodeScanner
BarcodeScanner scanner = BarcodeScanning.getClient(options);
//process the image
Task<List<Barcode>> result = scanner.process(inputImage)
.addOnSuccessListener(new OnSuccessListener<List<Barcode>>() {
@Override
public void onSuccess(List<Barcode> barcodes) {
readerBarcodeData(barcodes);
Log.d("cameraSuccess::", barcodes.toString());
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// Task failed with an exception
// ...
Log.d("cameraFail::", e.toString());
}
})
.addOnCompleteListener(new OnCompleteListener<List<Barcode>>() {
@Override
public void onComplete(@NonNull Task<List<Barcode>> task) {
image.close();
}
});
}
BarcodeScannerOptions 에서 옵션을 다르게 선택하면 QR코드가 아니라 바코드를 읽어낼 수 있습니다.
지금은 QR코드만 스캔할 수 있도록 옵션을 설정해두었습니다.
이렇게 성공적으로 바코드 스캔까지 마쳤으면 바코드가 담고있는 정보를 가져와 정제를 해줍니다.
//get information barcode
private void readerBarcodeData(List<Barcode> barcodes) {
for (Barcode barcode: barcodes) {
Rect bounds = barcode.getBoundingBox();
Point[] corners = barcode.getCornerPoints();
String rawValue = barcode.getRawValue();
int valueType = barcode.getValueType();
// See API reference for complete list of supported types
switch (valueType) {
case Barcode.TYPE_WIFI:
Toast.makeText(this, "wifi", Toast.LENGTH_SHORT).show();
String ssid = barcode.getWifi().getSsid();
String password = barcode.getWifi().getPassword();
int type = barcode.getWifi().getEncryptionType();
cameraExecutor.shutdownNow();
break;
case Barcode.TYPE_URL:
Toast.makeText(this, "url", Toast.LENGTH_SHORT).show();
String title = barcode.getUrl().getTitle();
String url = barcode.getUrl().getUrl();
cameraExecutor.shutdownNow();
break;
}
}
}
바코드가 담고있는 정보의 타입에 따라 어떻게 다룰것인지 나눠줍니다.
url을 받아왔으면 intent로 넘겨줄 수 있겠네요!
이렇게 끝!
일 줄 알았겠지만 현재 사용중인 액티비티가 꺼질때 백그라운드에서 카메라가 돌지 않도록 꺼줍시다.
꺼주지 않으면... 혼나요.
private void closeCamera(){
if (cameraProviderFuture != null && cameraExecutor != null){
cameraProviderFuture.cancel(true);
cameraProviderFuture = null;
cameraExecutor.shutdown();
cameraExecutor = null;
}
}
저는 그냥 null을 박아버렸습니다.
사실 더 좋은 방법이 있지 않을까 싶은데 없애주는 게 마음이 편해요.
카메라 꺼주는 메소드를 만들어서
@Override
protected void onPause() {
super.onPause();
closeCamera();
}
@Override
protected void onDestroy() {
super.onDestroy();
closeCamera();
}
이렇게 붙여줍니다.
onPause에서 카메라를 없앴으니 onResume에서는 다시 켜줘야겠죠!!
코드 전문은 깃헙을 참조해주세요.
https://github.com/jmnl225/QRcodeScanner
GitHub - jmnl225/QRcodeScanner
Contribute to jmnl225/QRcodeScanner development by creating an account on GitHub.
github.com
'안드로이드' 카테고리의 다른 글
[안드로이드] SharedPreferences를 이용한 자동로그인 (0) | 2022.01.10 |
---|