Mengembangkan aplikasi Android yang membutuhkan fungsionalitas kamera terkadang terasa kompleks. Berurusan dengan Camera API lama bisa jadi tantangan, mulai dari manajemen lifecycle, menangani berbagai ukuran layar, hingga rotasi perangkat. Untungnya, Android Jetpack CameraX hadir sebagai solusi modern yang menyederhanakan proses pengembangan kamera secara signifikan.
CameraX adalah library pendamping yang memudahkan pengembang untuk berinteraksi dengan kamera perangkat. Dengan dukungan untuk berbagai kasus penggunaan (seperti pratinjau, pengambilan gambar, dan analisis gambar), integrasi dengan siklus hidup Android, dan konsistensi di berbagai perangkat, CameraX menjadi pilihan ideal bagi Anda yang ingin mengimplementasikan fitur kamera secara efisien di aplikasi Kotlin Anda.
Di artikel ini, kita akan membahas secara mendalam langkah demi langkah cara mengimplementasikan pengambilan foto menggunakan CameraX di project Kotlin Anda. Dari menyiapkan dependensi, menangani izin, hingga mengambil gambar dan menyimpannya, semuanya akan kita bahas dengan panduan praktis.
Persiapan Awal: Project Android dan Dependensi CameraX
Sebelum kita mulai menulis kode, pastikan Anda memiliki project Android di Android Studio yang siap digunakan. Jika belum, Anda bisa membuat project baru dengan template “Empty Activity” dan memilih Kotlin sebagai bahasa.
Tambahkan Dependensi CameraX
Langkah pertama adalah menambahkan dependensi CameraX ke file build.gradle.kts (Module :app) Anda. Pastikan untuk menggunakan versi terbaru yang stabil. Anda bisa mencari versi terbaru di dokumentasi resmi AndroidX Camera.
Tambahkan baris berikut di dalam blok dependencies:
dependencies {
// ... dependensi lainnya
// CameraX core library
implementation("androidx.camera:camera-core:1.3.3") // Ganti dengan versi stabil terbaru
implementation("androidx.camera:camera-camera2:1.3.3") // Ganti dengan versi stabil terbaru
implementation("androidx.camera:camera-lifecycle:1.3.3") // Ganti dengan versi stabil terbaru
// CameraX Viewfinder for PreviewView
implementation("androidx.camera:camera-view:1.3.3") // Ganti dengan versi stabil terbaru
implementation("androidx.camera:camera-extensions:1.3.3") // Opsional, untuk fitur extension
}
Setelah menambahkan dependensi, jangan lupa untuk melakukan “Sync Project with Gradle Files”.
Deklarasi Izin Kamera di AndroidManifest.xml
Aplikasi Anda memerlukan izin untuk mengakses kamera perangkat dan menyimpan gambar. Tambahkan izin berikut ke file AndroidManifest.xml di dalam tag <manifest>:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourappname">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<!-- Untuk Android 10 (API 29) ke atas, WRITE_EXTERNAL_STORAGE tidak lagi diperlukan
untuk menyimpan ke direktori spesifik aplikasi atau MediaStore.
Namun, jika target SDK Anda lebih rendah atau Anda ingin menyimpan ke Shared Storage
di bawah API 29, ini masih relevan.
Untuk API 29+, sebaiknya gunakan MediaStore untuk menyimpan gambar. -->
<application
...
</application>
</manifest>
Catatan Penting: Untuk Android 10 (API level 29) ke atas, WRITE_EXTERNAL_STORAGE tidak lagi diperlukan jika Anda menyimpan gambar ke direktori spesifik aplikasi atau menggunakan MediaStore. Android memberlakukan “Scoped Storage” yang lebih ketat. Namun, untuk kompatibilitas dengan versi Android yang lebih lama, deklarasi ini tetap berguna. Kita akan menggunakan MediaStore untuk menyimpan gambar secara modern.
Desain Antarmuka Pengguna (Layout)
Kita akan membuat layout sederhana yang terdiri dari sebuah PreviewView untuk menampilkan pratinjau kamera, dan sebuah tombol untuk mengambil foto. Buka file activity_main.xml Anda dan ganti isinya menjadi seperti ini:
<?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">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/imageCaptureButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="Ambil Foto"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Implementasi Logika Kamera di MainActivity.kt
Sekarang, saatnya menulis logika inti di file MainActivity.kt. Kita akan membagi prosesnya menjadi beberapa bagian: penanganan izin, inisialisasi CameraX, pengaturan pratinjau, dan pengambilan gambar.
1. Penanganan Izin (Runtime Permissions)
Karena kita memerlukan izin kamera, kita harus memintanya saat runtime. Ini adalah praktik standar di Android untuk izin berbahaya (dangerous permissions).
Tambahkan konstanta untuk kode permintaan izin dan sebuah launcher untuk menangani hasil permintaan izin:
package com.example.yourappname // Sesuaikan dengan package Anda
import android.Manifest
import android.content.ContentValues
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.example.yourappname.databinding.ActivityMainBinding
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private lateinit var viewBinding: ActivityMainBinding
private var imageCapture: ImageCapture? = null
private lateinit var cameraExecutor: ExecutorService
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
var allGranted = true
permissions.entries.forEach {
if (!it.value) {
allGranted = false
}
}
if (allGranted) {
startCamera()
} else {
Toast.makeText(this, "Izin kamera tidak diberikan.", Toast.LENGTH_SHORT).show()
finish()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
// Meminta izin kamera saat aplikasi dimulai
if (allPermissionsGranted()) {
startCamera()
} else {
requestPermissions()
}
// Siapkan listener untuk tombol ambil foto
viewBinding.imageCaptureButton.setOnClickListener { takePhoto() }
cameraExecutor = Executors.newSingleThreadExecutor()
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
}
private fun requestPermissions() {
requestPermissionLauncher.launch(REQUIRED_PERMISSIONS)
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
companion object {
private const val TAG = "CameraXApp"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private val REQUIRED_PERMISSIONS =
mutableListOf(
Manifest.permission.CAMERA
).apply {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}.toTypedArray()
}
}
Dalam kode di atas, kita mendefinisikan REQUIRED_PERMISSIONS yang mencakup CAMERA dan WRITE_EXTERNAL_STORAGE (untuk API < Q). Kita menggunakan ActivityResultContracts.RequestMultiplePermissions untuk menangani permintaan izin dan memanggil startCamera() jika semua izin diberikan.
2. Menginisialisasi CameraX dan Mengatur Pratinjau
Fungsi startCamera() akan menjadi inti untuk menginisialisasi CameraX dan mengikat use case (pratinjau dan pengambilan gambar) ke siklus hidup aplikasi.
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
// Digunakan untuk mengikat siklus hidup kamera ke LifecycleOwner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Inisialisasi Preview Use Case
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider)
}
// Inisialisasi ImageCapture Use Case
imageCapture = ImageCapture.Builder()
.setTargetRotation(viewBinding.viewFinder.display.rotation) // Sesuaikan rotasi
.build()
// Pilih kamera belakang sebagai default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Pastikan tidak ada use case yang terikat sebelumnya
cameraProvider.unbindAll()
// Bind use case ke kamera
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture
)
} catch (exc: Exception) {
Log.e(TAG, "Gagal mengikat use case", exc)
}
}, ContextCompat.getMainExecutor(this))
}
Di sini, kita mendapatkan instance ProcessCameraProvider yang mengikat CameraX ke LifecycleOwner (dalam kasus ini, MainActivity). Ini memastikan bahwa kamera memulai dan berhenti secara otomatis sesuai dengan siklus hidup aktivitas.
Preview.Builder()digunakan untuk membuat use case preview dan mengaitkannya denganPreviewViewdi layout kita.ImageCapture.Builder()digunakan untuk membuat use case pengambilan gambar.setTargetRotation()penting untuk memastikan gambar yang diambil memiliki orientasi yang benar.CameraSelector.DEFAULT_BACK_CAMERAmemilih kamera belakang. Anda bisa mengubahnya menjadiDEFAULT_FRONT_CAMERAjika ingin menggunakan kamera depan.cameraProvider.bindToLifecycle()mengikat semua use case yang telah kita buat ke kamera dan siklus hidup aktivitas.
3. Mengambil Foto
Sekarang kita akan mengimplementasikan fungsi takePhoto() yang akan dipanggil ketika tombol “Ambil Foto” ditekan.
private fun takePhoto() {
// Ambil referensi ImageCapture use case yang bisa null
val imageCapture = imageCapture ?: return
// Buat nama file dengan timestamp untuk menghindari duplikasi
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US)
.format(System.currentTimeMillis())
// Buat objek ContentValues untuk detail gambar
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Images")
}
}
// Buat objek OutputFileOptions
val outputOptions = ImageCapture.OutputFileOptions.Builder(
contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
).build()
// Siapkan ImageCapture listener, yang dipicu setelah foto diambil
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Gagal mengambil foto: ${exc.message}", exc)
Toast.makeText(this@MainActivity, "Gagal mengambil foto.", Toast.LENGTH_SHORT).show()
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val msg = "Foto berhasil disimpan: ${output.savedUri}"
Toast.makeText(this@MainActivity, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
}
)
}
Fungsi takePhoto() melakukan beberapa hal penting:
- Membentuk nama file berdasarkan timestamp agar unik.
- Membuat
ContentValuesyang berisi metadata untuk gambar, termasuk nama tampilan dan tipe MIME. Untuk Android 10 (API 29) ke atas, kita juga menentukanRELATIVE_PATHuntuk menyimpan gambar di folderPictures/CameraX-Imagesdi penyimpanan bersama. - Membuat
OutputFileOptionsmenggunakancontentResolverdanMediaStore.Images.Media.EXTERNAL_CONTENT_URI. Ini adalah cara modern dan aman untuk menyimpan gambar di perangkat Android. - Memanggil
imageCapture.takePicture()denganOutputFileOptionsdan sebuahOnImageSavedCallback. Callback ini akan memberi tahu kita apakah foto berhasil diambil atau terjadi kesalahan.
Eksekutor Kamera
cameraExecutor adalah ExecutorService yang digunakan CameraX untuk menjalankan tugas-tugas di background. Penting untuk mematikannya (shutdown()) di onDestroy() untuk mencegah kebocoran memori.
Masalah yang Sering Terjadi
Dalam pengembangan aplikasi kamera, beberapa masalah umum sering muncul. Berikut adalah beberapa di antaranya beserta solusinya:
1. Aplikasi Crash karena Izin Kamera Tidak Diberikan
- Gejala: Aplikasi menutup sendiri atau menunjukkan pesan error terkait izin saat mencoba mengakses kamera.
- Penyebab: Pengguna menolak izin kamera saat diminta, atau Anda lupa mendeklarasikan izin di
AndroidManifest.xml. - Solusi:
- Pastikan Anda mendeklarasikan
<uses-permission android:name="android.permission.CAMERA" />diAndroidManifest.xml. - Implementasikan penanganan izin runtime yang robust, seperti yang telah kita lakukan di artikel ini. Pastikan Anda memeriksa apakah semua izin diberikan sebelum memanggil
startCamera(). - Jika izin ditolak permanen, pandu pengguna untuk mengaktifkan izin secara manual dari pengaturan aplikasi.
- Pastikan Anda mendeklarasikan
2. Pratinjau Kamera Kosong atau Hitam
- Gejala:
PreviewViewtetap hitam atau kosong setelah aplikasi diluncurkan, tetapi tidak ada error yang terlihat. - Penyebab:
- Kamera tidak berhasil diikat ke lifecycle.
setSurfaceProvider()tidak dipanggil atau dipanggil dengan objek yang salah.- Ada konflik dengan aplikasi kamera lain yang sedang berjalan.
- Kesalahan dalam pemilihan kamera (misalnya, memilih kamera depan padahal perangkat tidak memilikinya).
- Solusi:
- Periksa logcat untuk error saat
bindToLifecycle()dipanggil. - Pastikan
preview.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider)dipanggil dengan benar. - Coba restart perangkat atau paksa tutup aplikasi kamera lain.
- Pastikan
cameraSelectormemilih kamera yang tersedia (DEFAULT_BACK_CAMERAatauDEFAULT_FRONT_CAMERA).
- Periksa logcat untuk error saat
3. Gambar yang Diambil Terbalik atau Rotasinya Salah
- Gejala: Foto yang disimpan memiliki orientasi yang salah (misalnya, potret menjadi lanskap).
- Penyebab:
setTargetRotation()padaImageCapture.Buildertidak disetel dengan benar atau tidak disetel sama sekali. - Solusi:
- Pastikan Anda memanggil
imageCapture.setTargetRotation(viewBinding.viewFinder.display.rotation).viewFinder.display.rotationakan memberikan rotasi layar saat ini, yang CameraX gunakan untuk mengoreksi orientasi gambar. - Pastikan ini diperbarui jika orientasi perangkat berubah (misalnya, di
onConfigurationChangedatau dengan mendengarkan perubahan orientasi).
- Pastikan Anda memanggil
4. Gagal Menyimpan Gambar di Android 10 (API 29) ke Atas
- Gejala: Error
IOExceptionatauSecurityExceptionsaat mencoba menyimpan gambar, terutama di perangkat yang menjalankan Android 10 atau yang lebih baru. - Penyebab: Menggunakan metode penyimpanan lama (misalnya, langsung ke
Filedi penyimpanan eksternal tanpa MediaStore) yang tidak kompatibel dengan Scoped Storage. - Solusi:
- Gunakan
MediaStoreAPI seperti yang ditunjukkan di contohtakePhoto(). Ini adalah cara yang direkomendasikan untuk menyimpan media di Android 10+ karena mematuhi Scoped Storage. - Pastikan
ContentValuesAnda disiapkan dengan benar, terutamaRELATIVE_PATH. - Pastikan Anda memiliki izin
WRITE_EXTERNAL_STORAGEjika Anda menargetkan API < 29, dan jika Anda memang membutuhkannya untuk menyimpan ke direktori bersama di perangkat lama.
- Gunakan
5. Tidak Dapat Mengakses Kamera Depan
- Gejala: Kamera gagal memulai saat mencoba menggunakan
CameraSelector.DEFAULT_FRONT_CAMERA. - Penyebab: Perangkat yang digunakan tidak memiliki kamera depan atau kamera depan sedang digunakan oleh aplikasi lain.
- Solusi:
- Periksa ketersediaan kamera sebelum mencoba mengikatnya. Anda bisa menggunakan
cameraProvider.hasCamera(cameraSelector). - Sediakan opsi untuk beralih antara kamera depan dan belakang jika keduanya tersedia.
- Tangani kasus di mana kamera yang diminta tidak ditemukan dengan pesan yang informatif kepada pengguna.
- Periksa ketersediaan kamera sebelum mencoba mengikatnya. Anda bisa menggunakan
Pengalaman dan Pertimbangan Praktis
Mengintegrasikan fitur kamera ke aplikasi bukan hanya soal menulis kode, tapi juga tentang memberikan pengalaman pengguna yang baik dan mengelola sumber daya secara efisien. Berikut beberapa pertimbangan praktis berdasarkan pengalaman pengembangan nyata:
Fleksibilitas Pilihan Kamera
Dalam praktiknya, pengguna sering ingin beralih antara kamera depan dan belakang. Anda bisa menambahkan tombol untuk mengganti CameraSelector dari DEFAULT_BACK_CAMERA ke DEFAULT_FRONT_CAMERA (dan sebaliknya) lalu memanggil ulang bindToLifecycle.
Penanganan Orientasi Perangkat
Rotasi perangkat adalah hal yang sering dilupakan. setTargetRotation() pada ImageCapture.Builder sangat penting untuk memastikan gambar yang diambil memiliki orientasi yang benar. Namun, Anda mungkin juga perlu memikirkan bagaimana UI (tombol, overlay) Anda beradaptasi dengan rotasi perangkat.
Kualitas Gambar dan Ukuran File
CameraX memungkinkan Anda mengatur resolusi target untuk ImageCapture menggunakan setTargetResolution() atau setTargetAspectRatio(). Ini penting untuk menyeimbangkan antara kualitas gambar dan ukuran file. Untuk aplikasi yang mengunggah gambar ke server, resolusi terlalu tinggi bisa boros data dan waktu.
Fitur Tambahan (Flash, Zoom, Fokus)
CameraX memiliki API untuk mengontrol fitur seperti flash mode (imageCapture.setFlashMode()), zoom, dan tap-to-focus. Mengimplementasikan fitur ini dapat sangat meningkatkan pengalaman pengguna. Misalnya, Anda bisa menambahkan tombol untuk mengaktifkan/menonaktifkan flash atau gesture detector untuk tap-to-focus.
Optimasi Performa dan Memori
Kamera adalah salah satu komponen yang haus sumber daya. Pastikan Anda mematikan cameraExecutor di onDestroy(). Hindari melakukan operasi pemrosesan gambar yang berat di main thread. Untuk gambar yang sangat besar, pertimbangkan untuk menurunkannya skalanya sebelum menampilkannya atau memprosesnya.
Error Handling yang Robust
Jangan berasumsi bahwa semua akan berjalan lancar. Selalu sertakan try-catch block dan log pesan error yang jelas. Informasikan kepada pengguna jika terjadi masalah, misalnya “Gagal memulai kamera” atau “Gagal menyimpan foto”. Ini sangat membantu dalam debugging dan meningkatkan kepuasan pengguna.
Testing di Berbagai Perangkat
Perilaku kamera bisa sedikit bervariasi antar produsen perangkat. Selalu uji aplikasi Anda di berbagai perangkat fisik (bukan hanya emulator) untuk memastikan kompatibilitas dan kinerja yang optimal.
Dengan mempertimbangkan poin-poin ini, Anda tidak hanya akan membangun aplikasi kamera yang fungsional tetapi juga memberikan pengalaman yang mulus dan menyenangkan bagi pengguna Anda.
FAQ
Apa itu CameraX?
CameraX adalah library pendamping Android Jetpack yang menyederhanakan pengembangan aplikasi kamera dengan menyediakan antarmuka API yang konsisten dan mudah digunakan, terintegrasi dengan siklus hidup Android.
Mengapa harus menggunakan CameraX daripada Camera2 API?
CameraX dibangun di atas Camera2 API yang lebih kompleks. CameraX menawarkan abstraksi yang lebih tinggi, manajemen lifecycle otomatis, dan konsistensi di berbagai perangkat, sehingga lebih cepat dan mudah untuk dikembangkan, terutama untuk kasus penggunaan umum seperti mengambil foto atau video.
Apakah CameraX mendukung kamera depan dan belakang?
Ya, CameraX mendukung pemilihan antara kamera depan dan belakang menggunakan CameraSelector.DEFAULT_FRONT_CAMERA dan CameraSelector.DEFAULT_BACK_CAMERA.
Bagaimana cara mengubah resolusi gambar yang diambil?
Anda bisa mengatur resolusi target saat membangun ImageCapture use case menggunakan setTargetResolution() atau setTargetAspectRatio(). Ini membantu menyeimbangkan kualitas dan ukuran file sesuai kebutuhan aplikasi Anda.
Bisakah saya menambahkan filter atau efek ke gambar dengan CameraX?
CameraX secara langsung tidak menyediakan fitur filter. Namun, setelah gambar diambil, Anda bisa menggunakan library pemrosesan gambar (seperti OpenCV atau kustom OpenGL ES) untuk menerapkan filter atau efek pada Bitmap atau ByteBuffer gambar yang dihasilkan.
Apakah CameraX memerlukan izin khusus untuk menyimpan gambar di Android 10 ke atas?
Untuk Android 10 (API 29) ke atas, jika Anda menyimpan gambar ke folder media umum (misalnya, Pictures), Anda harus menggunakan MediaStore API. Anda tidak lagi memerlukan izin WRITE_EXTERNAL_STORAGE jika menggunakan MediaStore dengan benar.
Kesimpulan
Membangun fitur pengambilan foto di aplikasi Android menggunakan CameraX dengan Kotlin bukan lagi tugas yang menakutkan. Dengan arsitektur yang modern, API yang intuitif, dan manajemen lifecycle yang cerdas, CameraX memungkinkan developer untuk fokus pada fitur inti aplikasi tanpa harus terjebak dalam kompleksitas API kamera tingkat rendah.
Dari penanganan izin yang mulus, tampilan pratinjau yang responsif, hingga proses pengambilan dan penyimpanan gambar yang efisien, setiap langkah yang kita bahas di sini dirancang untuk memberikan fondasi yang kuat bagi aplikasi kamera Anda. Jangan ragu untuk bereksperimen dengan fitur-fitur CameraX lainnya seperti perekaman video, analisis gambar, atau kontrol kamera yang lebih granular seperti flash dan zoom. Potensi CameraX sangat besar, dan dengan panduan ini, Anda sudah berada di jalur yang benar untuk menciptakan pengalaman kamera yang luar biasa di aplikasi Android Anda.
TAGS: CameraX, Kotlin, Android Development, Ambil Foto, Tutorial Kamera, Android Developer, Jetpack CameraX, Image Capture, Android Studio, Coding


