본문 바로가기
안드로이드/코틀린

안드로이드 CameraX, 카메라 프리뷰 예제 - 사진 촬영 및 저장 구현하기

by 시작이반의반 2024. 12. 5.

안드로이드 카메라 프리뷰, CameraX 예제
안드로이드 카메라 프리뷰, CameraX 예제

안드로이드 앱에서 카메라 기능은 자주 사용되는 중요한 요소 중 하나입니다.

이번 글에서는 CameraX 라이브러리에 대해서 간략하게 살펴보고,

CameraX를 활용하여 사진 촬영, 이미지 저장, 이미지 결과까지 확인하는 카메라 프리뷰 예제를 살펴보려고 합니다.

해당 예제를 통해서 Android Jetpack의 CameraX 라이브러리를 효과적으로 사용하는 방법을 확인할 수 있습니다.

혹시 카메라 프리뷰 예제를 실행하고 싶으시다면 아래 링크를 통해 다운로드가 가능합니다.

 

카메라 프리뷰 예제 실행화면
카메라 프리뷰 예제 실행화면

 

 

안드로이드 CameraX 라이브러리

 

CameraX는 Android Jetpack의 일부로 안드로이드 앱에서 카메라 기능을 간단하고 효율적으로 구현할 수 있도록 설계된 라이브러리입니다. 기존의 Camera API는 다소 복잡한 설정과 디바이스 호환성 문제를 가지고 있습니다. CameraX는 이러한 문제를 해결하며 안드로이드 카메라 개발을 쉽게 만드는 강력한 도구입니다. 카메라 기능을 구현해야 된다면 CameraX 라이브러리를 사용하는 걸 추천드립니다. 

 

항목 Camera API CameraX
출시 시기 2008년 2019년
API 복잡성 다소 복잡하고 낮은 수준의 API 제공 사용자 친화적인 고수준 API 제공
미래 지향성 유지보수 종료, 업데이트 없음 Jetpack으로 지속적인 업데이트 제공
호환성 디바이스마다 동작이 달라지기도 함 CameraX 내부적으로 호환성 처리
생명주기 직접 생명주기 관래해야함 Activity 또는 Fragment와 자동으로 연동

 

 

 

안드로이드 CameraX 라이브러리 추가

 

gradle에 아래와 같이 cameraX 라이브러리를 추가합니다.

dependencies {
    ....

    // CameraX
    implementation("androidx.camera:camera-core:1.4.0")
    implementation("androidx.camera:camera-camera2:1.4.0")
    implementation("androidx.camera:camera-lifecycle:1.4.0")
    implementation("androidx.camera:camera-view:1.4.0")
    implementation("androidx.camera:camera-extensions:1.4.0")
}

 

 

안드로이드 CameraX 권한 승인

 

AndroidManifest.xml에 CAMERA 권한을 추가하고,

<uses-permission android:name="android.permission.CAMERA" />

 

러닝타임에 CAMERA 권한 승인 요청하는 동작을 작성합니다.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        requestCameraPermission()
    }

    private fun requestCameraPermission() {
        val permissions = arrayOf(Manifest.permission.CAMERA)
        ActivityCompat.requestPermissions(this, permissions, 10)
    }
}

 

 

 

안드로이드 카메라 프리뷰 예제: MainActivity

 

카메라 프리뷰 MainActivity 실행화면 및 소스코드는 아래와 같습니다.

CAMERA PREVIEW 버튼을 통해서 카메라 프리뷰를 진입하고, 이미지 결과를 보여주는 화면으로 구성되어 있습니다.

카메라 프리뷰 예제: MainActivity카메라 프리뷰 예제: MainActivity
카메라 프리뷰 예제: MainActivity

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    private val cameraPreviewLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            if (result.resultCode == RESULT_OK) {
                val imageUri = result.data?.getStringExtra("imageUri")

                imageUri?.let {
                    binding.ivResult.setImageURI(null)
                    binding.ivResult.setImageURI(Uri.parse(it))
                }
            }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.btnCameraPreview.setOnClickListener {
            val intent = Intent(this, CameraActivity::class.java)
            cameraPreviewLauncher.launch(intent)
        }

        requestCameraPermission()
    }

    private fun requestCameraPermission() {
        val permissions = arrayOf(Manifest.permission.CAMERA)
        ActivityCompat.requestPermissions(this, permissions, 10)
    }
}

 

 

 

안드로이드 카메라 프리뷰 예제: CameraActivity

 

카메라 프리뷰 예제에서 CameraActivity 실행화면 및 소스코도는 아래와 같습니다.

카메라 프리뷰 영역과 사진촬영, 플래시 On/Off, 화면전환 Front/Back 버튼으로 기본적인 기능을 포함하고 있습니다.

카메라 프리뷰 예제: CameraActivity
카메라 프리뷰 예제: CameraActivity

class CameraActivity : AppCompatActivity() {
    private lateinit var binding: ActivityCameraPreviewBinding
    private lateinit var cameraProvider: ProcessCameraProvider
    private lateinit var cameraControl: CameraControl
    private lateinit var cameraInfo: CameraInfo

    // UseCase
    private var preview: Preview? = null
    private var imageCapture: ImageCapture? = null

    // Camera State
    private var isFlashOn = false
    private var isFrontCamera = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_camera_preview)
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

        startCamera()
        setListeners()
    }

    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener({
            cameraProvider = cameraProviderFuture.get()

            try {
                bindCameraUseCases()
            } catch (e: Exception) {
                Log.e("CameraX", "카메라 시작 실패", e)
            }
        }, ContextCompat.getMainExecutor(this))
    }

    private fun bindCameraUseCases() {
        preview = Preview.Builder().build().apply { 
            surfaceProvider = binding.cameraPreview.surfaceProvider 
        }
        imageCapture = ImageCapture.Builder()
            .setTargetRotation(binding.cameraPreview.display.rotation)
            .build()

        val cameraSelector =
            if (isFrontCamera) CameraSelector.DEFAULT_FRONT_CAMERA
            else CameraSelector.DEFAULT_BACK_CAMERA

        cameraProvider.unbindAll()

        val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
        cameraControl = camera.cameraControl
        cameraInfo = camera.cameraInfo
    }

    private fun setListeners() {
        binding.btnCapture.setOnClickListener { takeCapture() }
        binding.btnFlash.setOnClickListener { toggleFlashlight() }
        binding.btnTransition.setOnClickListener { toggleCamera() }
    }

    private fun takeCapture() {
        imageCapture?.let { capture ->
            val imageFile = File(getExternalFilesDir(null), "captured_image.jpg")
            val outputOptions = ImageCapture.OutputFileOptions.Builder(imageFile).build()

            capture.takePicture(outputOptions, ContextCompat.getMainExecutor(this),
                object : ImageCapture.OnImageSavedCallback {
                    override fun onError(exc: ImageCaptureException) {
                        Log.e("CameraX", "사진 촬영 실패: ${exc.message}", exc)
                    }

                    override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                        val savedUri = Uri.fromFile(imageFile)
                        val resultIntent = Intent().apply {
                            putExtra("imageUri", savedUri.toString())
                        }
                        setResult(RESULT_OK, resultIntent)
                        finish()
                    }
                }
            )
        }
    }

    private fun toggleFlashlight() {
        cameraControl.enableTorch(!isFlashOn)
        isFlashOn = !isFlashOn
    }

    private fun toggleCamera() {
        isFrontCamera = !isFrontCamera
        bindCameraUseCases()
    }
}

 

 

안드로이드 카메라 프리뷰 예제 다운로드

 

해당 예제는 cameraPreview 모듈을 생성하여 구현한 카메라 프리뷰 예제입니다.

아래 링크를 통해서 다운로드가 가능하고 직접 실행하여 확인이 가능합니다.

 

 

 

 

관련 글

안드로이드 이미지 확대 축소: PhotoView 예제

 

안드로이드 이미지 확대 축소 : PhotoView 예제

일반적으로 사용되는 ImageView는 단순히 이미지를 표시해 주는 View입니다.PhotoView는 ImageView를 확장해서 작성된 라이브러리로 쉽게 이미지 확대 및 축소 사용이 가능합니다.이번 글에서는 PhotoView

joo-selfdev.tistory.com

안드로이드 사진 촬영 효과 예제

 

안드로이드 캡처 효과 예제 : CaptureEffectView

최근 안드로이드 캡처 동작 관련해서 공부하다가 캡처는 잘되는데... 뭔가 심심한 느낌이 있습니다.안드로이드 기기에서 지원하는 전원+볼륨 다운 버튼으로 screenshot을 했을 때 나오는 효과처럼

joo-selfdev.tistory.com

댓글