zxing
예전에도 살펴본 적이 있기는 하지만,
https://github.com/zxing/zxing/wiki/Getting-Started-Developing
바코드 스캐너 기능을 구현하는 라이브러리는 감사할 따름이다. 이런 걸 모르면 어떻게 일일이 코딩을 다 할 수 도 없고, 아무튼지, 좋은 일이다. 나도 언제쯤에 이런 라이브러리 하나 만들어서 공유을 해 볼 수 있을런지,
오늘은 이 걸 이용해서 간단한 앱을 하나 만들어 보겠다. 한가지는 샘플 소스에서 custom 하게 구성해 보는 것을 따라해 볼 것이다.
먼저 gradle 파일에 추가를 해 보자.
dependencies {
.....
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
implementation 'com.google.zxing:core:3.4.1'
.....
}
다음은 layout 을 구성해볼 차례인데,
내가 만드는 layout 은 다음 처럼 구성했다.
전에 다른 걸 할 때는 화면 전체를 바코드 읽기 위한 화면으로 활용을 했다면, custom 하게 수정을 하면 이렇게 화면의 일부만 바코드 스캔을 하는 화면으로 구성해 볼 수 있다는 점이 달라진다.
이제 layout 을 보면 다음과 같이 구현 된다.
<?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=".ScanBarcodeActivity">
<com.journeyapps.barcodescanner.DecoratedBarcodeView
android:id="@+id/zxing_barcode_scanner"
android:layout_width="371dp"
android:layout_height="362dp"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnFlash"
app:zxing_scanner_layout="@layout/zxing_barcode_scanner">
</com.journeyapps.barcodescanner.DecoratedBarcodeView>
<ImageButton
android:id="@+id/btnFlash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="336dp"
android:layout_marginTop="80dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_flash_on_24" />
<ImageButton
android:id="@+id/btnHome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="276dp"
android:layout_marginTop="52dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/zxing_barcode_scanner"
app:srcCompat="@drawable/ic_baseline_home_24" />
<ImageButton
android:id="@+id/btnLogout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="52dp"
app:layout_constraintStart_toEndOf="@+id/btnHome"
app:layout_constraintTop_toBottomOf="@+id/zxing_barcode_scanner"
app:srcCompat="@drawable/ic_baseline_logout_24" />
</androidx.constraintlayout.widget.ConstraintLayout>
특별하게 봐야할 것은 zxing_scanner_layout 을 따로 지정하고 그것을 만들어 주어야 하는 부분이다. 아래와 같이 만들어 주면 되고, zxing_framing_rect_width, zxing_framing_rect_height 을 이용하여 화면 가운데 나오는 스캔 view 의 크기를 조정해 주면 사용자가 조금은 쉽게 바코드 인식을 할 수 있게 만들 수 있다.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.journeyapps.barcodescanner.BarcodeView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_barcode_surface"
app:zxing_framing_rect_width="250dp"
app:zxing_framing_rect_height="250dp"/>
<com.journeyapps.barcodescanner.ViewfinderView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/zxing_viewfinder_view"
app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
app:zxing_result_view="@color/zxing_custom_result_view"
app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
app:zxing_viewfinder_laser_visibility="true"
app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>
<TextView
android:id="@+id/zxing_status_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:background="@color/zxing_transparent"
android:text="@string/zxing_msg_default_status"
android:textColor="@color/zxing_status_text"/>
</merge>
이제 실행을 위해서 activity을 구현해 보면
Mainactivity 는 이렇게 만들면 된다.
import androidx.activity.result.ActivityResultLauncher;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.billcoreatech.databinding.ActivityMainBinding;
import com.google.zxing.client.android.Intents;
import com.journeyapps.barcodescanner.ScanContract;
import com.journeyapps.barcodescanner.ScanOptions;
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ScanOptions options = new ScanOptions().setOrientationLocked(false).setCaptureActivity(ScanBarcodeActivity.class);
options.setBarcodeImageEnabled(true);
options.setBeepEnabled(true);
options.setPrompt(getString(R.string.msgScanTitle));
barcodeLauncher.launch(options);
}
});
}
private final ActivityResultLauncher<ScanOptions> barcodeLauncher = registerForActivityResult(new ScanContract(),
result -> {
if(result.getContents() == null) {
Intent originalIntent = result.getOriginalIntent();
if (originalIntent == null) {
Log.d("MainActivity", "Cancelled scan");
Toast.makeText(MainActivity.this, "Cancelled", Toast.LENGTH_LONG).show();
} else if(originalIntent.hasExtra(Intents.Scan.MISSING_CAMERA_PERMISSION)) {
Log.d("MainActivity", "Cancelled scan due to missing camera permission");
Toast.makeText(MainActivity.this, "Cancelled due to missing camera permission", Toast.LENGTH_LONG).show();
}
} else {
Log.d("MainActivity", "Scanned");
Toast.makeText(MainActivity.this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
}
});
}
ActivityResultLauncher 는 샘플 소스에서 그대로 복사해온 것이고, 이것을 부분을 이용하면 barcode 스캔 결과를 이용해서 다른 작업을 할 수 있다.
barcodeLauncher 에서 호출하는 ScanBarcodeActivity 소스는 다음과 같이 하면 되겠다.
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.billcoreatech.databinding.ActivityScanBarcodeBinding;
import com.journeyapps.barcodescanner.CaptureManager;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
import com.journeyapps.barcodescanner.ViewfinderView;
import java.util.Random;
public class ScanBarcodeActivity extends AppCompatActivity implements DecoratedBarcodeView.TorchListener{
ActivityScanBarcodeBinding binding ;
private CaptureManager capture;
SharedPreferences sp ;
SharedPreferences.Editor flashStatus ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityScanBarcodeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
sp = getSharedPreferences(getPackageName(), MODE_PRIVATE);
binding.zxingBarcodeScanner.setTorchListener(this);
if (!hasFlash()) {
binding.btnFlash.setVisibility(View.GONE);
}
capture = new CaptureManager(this, binding.zxingBarcodeScanner);
capture.initializeFromIntent(getIntent(), savedInstanceState);
capture.setShowMissingCameraPermissionDialog(true);
capture.decode();
changeMaskColor(null);
changeLaserVisibility(true);
binding.btnHome.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
binding.btnLogout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
binding.btnFlash.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
switchFlashlight(view);
}
});
}
@Override
protected void onResume() {
super.onResume();
capture.onResume();
}
@Override
protected void onPause() {
super.onPause();
capture.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
capture.onDestroy();
}
@Override
public void onBackPressed() {
// super.onBackPressed();
Toast.makeText(getApplicationContext(), getString(R.string.msgLogoutPress), Toast.LENGTH_SHORT).show();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
capture.onSaveInstanceState(outState);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return binding.zxingBarcodeScanner.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
}
/**
* Check if the device's camera has a Flashlight.
* @return true if there is Flashlight, otherwise false.
*/
private boolean hasFlash() {
return getApplicationContext().getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
}
public void switchFlashlight(View view) {
if (sp.getBoolean("flashOn", true)) {
binding.zxingBarcodeScanner.setTorchOn();
} else {
binding.zxingBarcodeScanner.setTorchOff();
}
}
public void changeMaskColor(View view) {
Random rnd = new Random();
int color = Color.argb(100, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
binding.zxingBarcodeScanner.getViewFinder().setMaskColor(color);
}
public void changeLaserVisibility(boolean visible) {
binding.zxingBarcodeScanner.getViewFinder().setLaserVisibility(visible);
}
@Override
public void onTorchOn() {
flashStatus = sp.edit();
flashStatus.putBoolean("flashOn", false);
flashStatus.commit();
binding.btnFlash.setBackground(getDrawable(R.drawable.ic_baseline_flash_off_24));
}
@Override
public void onTorchOff() {
flashStatus = sp.edit();
flashStatus.putBoolean("flashOn", true);
flashStatus.commit();
binding.btnFlash.setBackground(getDrawable(R.drawable.ic_baseline_flash_on_24));
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
capture.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
화면 예시에서 보았던 flash 버튼의 역활도 구현을 통해서 어두운 곳에서는 flash 을 켜거나, 끄거나 하는 기능도 기능해 볼 수 있다. zxingBarcodeScanner.setTorchOn / setTorchOff 을 통해서 구현이 가능 하다.
'모바일 앱(안드로이드)' 카테고리의 다른 글
안드로이드 앱 만들기 Kotlin 이 뭐야 ? (2) | 2022.01.18 |
---|---|
안드로이드 앱 만들기 : 구글맵 최종 위치 표시 하기. (2) | 2022.01.11 |
안드로이드 앱 만들기 : 리사이클 뷰의 활용성에 대해서. (3) | 2022.01.09 |
안드로이드 앱 만들기. 범블비가 업데이트 되었습니다. (0) | 2022.01.06 |
안드로이드 앱 만들기 : 음력을 양력으로 변환 하기 (3) | 2021.12.27 |