Skip to content
Related Articles

Related Articles

Improve Article
How to Create Custom Camera using CameraX in Android?
  • Last Updated : 19 Feb, 2021

CameraX is used to create a custom camera in the app. CameraX is a Jetpack support library, built to help you make camera app development easier. A sample video is given below to get an idea about what we are going to do in this article. Note that we are going to implement this project using the Kotlin language.

Step by Step Implementation

Step 1: Create a new project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Kotlin as the programming language.

Step 2: Add dependency to the build.gradle file and click “sync now”

def camerax_version = “1.0.0-beta07”



// 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-alpha14”

If kotlin extension is missing, add kotlin-android-extensions as shown below and click on “Sync now”.

plugins {



   id ‘com.android.application’

   id ‘kotlin-android’

   id ‘kotlin-android-extensions’

}

Step 3: Add Camera permission

Go to AndroidManifest.xml and add the camera permission.

<uses-feature android:name=”android.hardware.camera.any” />

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

Step 4: Working with the activity_main.xml file

Go to the activity_main.xml file and refer to the following code. Below is the code for the activity_main.xml file. Notice that there is a view called PreviewView with id viewFinder which we will use as Viewfinder for the camera.



XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    tools:context=".MainActivity">
  
    <Button
        android:id="@+id/camera_capture_button"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_marginBottom="50dp"
        android:background="@drawable/capture_button"
        android:elevation="2dp"
        android:scaleType="fitCenter"
        android:text="Capture Photo"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
  
    <ImageView
        android:id="@+id/iv_capture"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginEnd="20dp"
        android:layout_marginBottom="50dp"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
  
    <androidx.camera.view.PreviewView
        android:id="@+id/viewFinder"
        android:layout_width="match_parent"
        android:layout_height="450dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="20dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

Step 5: Create capture button drawable

Go to res > drawable and create a new file capture_button.xml. Below is the code of it.

XML




<?xml version="1.0" encoding="utf-8"?>
    android:shape="oval">
    <solid android:color="#0F9D58" />
    <size
        android:width="118dp"
        android:height="118dp" />
</shape>

Step 6: Working with the MainActivity.kt file

Go to the MainActivity.kt file and refer to the following code. Below is the code for the MainActivity.kt file. Comments are added inside the code to understand the code in more detail.

Kotlin




import android.Manifest
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.Toast
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.app.ActivityCompat
import androidx.core.content.ContextCompat
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
  
class MainActivity : AppCompatActivity() {
  
    private var imageCapture: ImageCapture? = null
    private lateinit var outputDirectory: File
    private lateinit var cameraExecutor: ExecutorService
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        // hide the action bar
        supportActionBar?.hide()
  
        // Check camera permissions if all permission granted
        // start camera else ask for the permission
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
        }
  
        // set on click listener for the button of capture photo
        // it calls a method which is implemented below
        findViewById<Button>(R.id.camera_capture_button).setOnClickListener {
            takePhoto()
        }
        outputDirectory = getOutputDirectory()
        cameraExecutor = Executors.newSingleThreadExecutor()
    }
  
    private fun takePhoto() {
        // Get a stable reference of the
        // modifiable image capture use case
        val imageCapture = imageCapture ?: return
  
        // Create time-stamped output file to hold the image
        val photoFile = File(
            outputDirectory,
            SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis()) + ".jpg"
        )
  
        // Create output options object which contains file + metadata
        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
  
        // Set up image capture listener,
        // which is triggered after photo has
        // been taken
        imageCapture.takePicture(
            outputOptions,
            ContextCompat.getMainExecutor(this),
            object : ImageCapture.OnImageSavedCallback {
                override fun onError(exc: ImageCaptureException) {
                    Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
                }
  
                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                    val savedUri = Uri.fromFile(photoFile)
  
                    // set the saved uri to the image view
                    findViewById<ImageView>(R.id.iv_capture).visibility = View.VISIBLE
                    findViewById<ImageView>(R.id.iv_capture).setImageURI(savedUri)
  
                    val msg = "Photo capture succeeded: $savedUri"
                    Toast.makeText(baseContext, msg, Toast.LENGTH_LONG).show()
                    Log.d(TAG, msg)
                }
            })
    }
  
    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
  
        cameraProviderFuture.addListener(Runnable {
              
            // Used to bind the lifecycle of cameras to the lifecycle owner
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
  
            // Preview
            val preview = Preview.Builder()
                .build()
                .also {
                    it.setSurfaceProvider(viewFinder.createSurfaceProvider())
                }
  
            imageCapture = ImageCapture.Builder().build()
  
            // Select back camera as a default
            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
  
            try {
                // Unbind use cases before rebinding
                cameraProvider.unbindAll()
  
                // Bind use cases to camera
                cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageCapture
                )
  
            } catch (exc: Exception) {
                Log.e(TAG, "Use case binding failed", exc)
            }
  
        }, ContextCompat.getMainExecutor(this))
    }
  
    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
    }
  
    // creates a folder inside internal storage
    private fun getOutputDirectory(): File {
        val mediaDir = externalMediaDirs.firstOrNull()?.let {
            File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
        }
        return if (mediaDir != null && mediaDir.exists())
            mediaDir else filesDir
    }
  
    // checks the camera permission
    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults:
        IntArray
    ) {
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            // If all permissions granted , then start Camera
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                // If permissions are not granted,
                // present a toast to notify the user that
                // the permissions were not granted.
                Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show()
                finish()
            }
        }
    }
  
    companion object {
        private const val TAG = "CameraXGFG"
        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
        private const val REQUEST_CODE_PERMISSIONS = 20
        private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    }
  
    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }
}

Output:

Want a more fast-paced & competitive environment to learn the fundamentals of Android?
Click here to head to a guide uniquely curated by our experts with the aim to make you industry ready in no time!
My Personal Notes arrow_drop_up
Recommended Articles
Page :