Android

QR코드 스캔 커스텀 ( + 플래시 )

그란. 2023. 1. 4. 19:00

 

QR 코드 스캔시 아래 화면과 같이 커스텀 

 

 

 

- 기본 UI에서 세로로 지정하기 

 

세로로 지정하기에 대한 구글링 해보면 아래와 같이 나온다.

( 하지만 이렇게 하는건 비효율적이라고 생각함. 왜 굳이 쓸모없이 액티비티를 만드는건데? )

 

1. 새로운 액티비티를 만들고 

class CustomCaptureActivity : CaptureActivity()

2. 해당 액티비티로 captureActivity를 지정하고   

captureActivity = CustomCaptureActivity::class.java

3. 해당 액티비티의 screenOrientation 속성을 portrait 나 fullSensor로 명시


Q. 일단 왜 가로로 나오는가? 

- 우리가 사용해야할 CaptureActivity가 

<activity
    android:name="com.journeyapps.barcodescanner.CaptureActivity"
    android:clearTaskOnLaunch="true"
    android:screenOrientation="sensorLandscape"
    android:stateNotNeeded="true"
    android:theme="@style/zxing_CaptureTheme"
    android:windowSoftInputMode="stateAlwaysHidden" />

이렇게 되어있기 때문이다. (LandScape) 

 

Q. 그러면 어떻게 해야해? 

- 저 라이브러리의 Activity 속성을 변경한다. 

<activity
    android:name="com.journeyapps.barcodescanner.CaptureActivity"
    android:exported="false"
    android:screenOrientation="fullSensor"
    tools:replace="android:screenOrientation" />

( 원래 기존 정의되어있는 액티비티의 속성을 추가할때는 tools:node="merge"를 사용하면 된다. )

하지만 그렇게 하면 에러가 발생함. 특정 속성이 이미 명시 되어있고 변경할때는 tools:replace를 이용해야 한다.

 

IntentIntegrator(requireActivity()).apply {
    setDesiredBarcodeFormats(IntentIntegrator.QR_CODE)
    setBeepEnabled(false)
    captureActivity = CaptureActivity::class.java
    setBarcodeImageEnabled(true)
    initiateScan()
}

 

-> 그래서 빈 액티비티를 만들 필요가 없다

 


 

이제 UI 커스텀을 해보자. 

커스텀 하기 위해 CaptureActivity를 상속하지 않고 CaptureActivity의 역할을 하는 Activity 를 생성한다.

( 해당 내용은 CaptureActivity 의 코드와 같다 ) 

class CustomScanActivity : AppCompatActivity(){

    private val barcodeScannerView: DecoratedBarcodeView by lazy { findViewById(R.id.decoratedBarcodeView) }
    private val capture: CaptureManager by lazy { CaptureManager(this, barcodeScannerView) }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_custom_scanner)
        capture.initializeFromIntent(intent, savedInstanceState)
        capture.decode()

    }


    override fun onResume() {
        super.onResume()
        capture.onResume()
    }

    override fun onPause() {
        super.onPause()
        capture.onPause()
    }

    override fun onDestroy() {
        super.onDestroy()
        capture.onDestroy()
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        capture.onSaveInstanceState(outState)
    }
	
}

 

activity_custom_scanner.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/blue_grey">

    <com.journeyapps.barcodescanner.DecoratedBarcodeView
        android:id="@+id/decoratedBarcodeView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:zxing_scanner_layout="@layout/merge_custom_barcode" />

	// 사각형 점선 역할
    <View
        android:layout_width="263dp"
        android:layout_height="263dp"
        android:background="@drawable/bg_stroke_dash"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_qr_code"
        style="@style/NotoBold"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="QR - CODE"
        android:textColor="@color/white"
        android:textSize="30sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/cb_flash" />

    <TextView
        android:id="@+id/tv_guide_scan"
        style="@style/NotoBold"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:background="@color/zxing_transparent"
        android:gravity="center_horizontal"
        android:text="화면에 보이는 코드를 스캔해주세요."
        android:textColor="@color/zxing_status_text"
        android:textSize="15sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_qr_code" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

merge_custom_barcode.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

	// 렌즈 역할
    <com.journeyapps.barcodescanner.BarcodeView
        android:id="@+id/zxing_barcode_surface"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:zxing_framing_rect_height="260dp"
        app:zxing_framing_rect_width="260dp" />

    <com.journeyapps.barcodescanner.ViewfinderView
        android:id="@+id/zxing_viewfinder_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        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/white"
        app:zxing_viewfinder_mask="@color/blue_grey" />

</merge>

 

IntentIntegrator(requireActivity()).apply {
                setDesiredBarcodeFormats(IntentIntegrator.QR_CODE)
                setBeepEnabled(false)
                captureActivity = CustomScanActivity::class.java //CaptureActivity가 아닌 Custom
                setBarcodeImageEnabled(true)
                initiateScan()
            }

디테일 추가 : 플래시 기능 넣기 

 

위의 activity_custom_scanner.xml 에 플래시 아이콘을 추가한다. 

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/blue_grey">
    
    ...

    <CheckBox
        android:id="@+id/cb_flash"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:button="@null"
        android:drawableStart="@drawable/ic_flash_selector"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

	...
</androidx.constraintlayout.widget.ConstraintLayout>

 

 

TorchListener 추가 

class CustomScanActivity : AppCompatActivity(), DecoratedBarcodeView.TorchListener {

	private val flash: CheckBox by lazy { findViewById(R.id.cb_flash) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_custom_scanner)
        barcodeScannerView.setTorchListener(this)
        capture.initializeFromIntent(intent, savedInstanceState)
        capture.decode()

        flash.setOnCheckedChangeListener { _, isChecked ->
            if (isChecked) barcodeScannerView.setTorchOn()
            else barcodeScannerView.setTorchOff()
        }

    }
    
    override fun onTorchOn() {
        flash.isChecked = true
    }

    override fun onTorchOff() {
        flash.isChecked = false
    }

}