Android SurfaceView Using Kotlin With Example

Greetings!
We have recently published 100+ articles on android tutorials with kotlin and java. If you need, you may visit Android Tutorial for beginners page. You can also check Kotlin Tutorial for beginners. Also, if you are interested in content writing, you can mail us at tutorialwing@gmail.com.
Hello Readers! In this tutorial, we will go through tutorial on android surfaceView using kotlin. We will see different attributes of android surfaceView widget to customise it.

In this example, we will show camera in surfaceView using kotlin.

Note – We have also covered this tutorial in java. So, you may visit tutorial on android surfaceView in java.

Output

Tutorialwing Android Kotiln SurfaceView Example Output of SurfaceView using kotlin with example

SurfaceView Example Output

Sourcecode(Kotlin)

You can download source code of this tutorial on android SurfaceView using kotlin by entering the details –

Getting Started

Android SurfaceView can be defined as below –

SurfaceView is subclass of View class that are provides a drawing surface embedded inside of View hierarchy.

You can also control and customise the surface as per your need.

Attributes of Android surfaceView Widget

Some of the popular attributes of android surfaceView inherited from View class are –

Sr. XML Attributes Description
1 android:background Defines background of the view.
2 android:theme Defines a theme of the view
3 android:visibility Defines visibility (VISIBLE, INVISIBLE or GONE) of the view
4 android:elevation Defines z-depth of the view
5 android:id Sets id of the view
6 android:padding Defines padding of the view



Example of Android SurfaceView using Kotlin

Now, we will create surfaceView using kotlin and show camera in it.

At first, we will create a new android application.

1. Creating New Project in Kotlin

Follow steps below to create new project. Please ignore the steps if you have already created the project.

Step Description
1. Open Android Studio.
2. Go to File => New => New Project. Write application name as SurfceView. Then, check Include Kotlin Support and click next button.
3. Select minimum SDK you need. However, we have selected 21 as minimum SDK. Then, click next button
4. Then, select Empty Activity => click next => click finish.
5. You will get a newly created project successfully if you have followed steps properly.

Since we have a project now, we will modify xml and other files to use SurfaceView using kotlin in the application.

2. Modify Values folder

Open res/values/strings.xml file and add below lines of code into it.

<resources>
	<string name="app_name">SurfaceView</string>
	<string name="take_photo">TAKE PHOTO</string>
    <string name="permission_required">Permission Required</string>
	<string name="permission_message">You must grant permission to access camera and external storage to run this application.</string>
	<string name="permission_warning">All permissions are required.</string>
</resources>

Then, open res/values/dimens.xml file. Now, add below code into it.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="show_permission_padding">20dp</dimen>
</resources>

3. Modify Layout in xml file

Open src/main/res/layout/activity_main.xml file and add below code into it.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

    <Button
        android:id="@+id/startBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal|bottom"
        android:text="@string/take_photo"
        android:visibility="gone" />

    <TextView
        android:id="@+id/showPermissionMsg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="@dimen/show_permission_padding"
        android:text="@string/permission_message"
        android:textAlignment="center"
        android:textStyle="bold|italic"
        android:visibility="gone" />

</FrameLayout>

Here, we have defined ui to show surfaceView and a button. When all the permissions are granted by user, we show camera to the user. If not, we show a message accordingly.




4. Setup For Required Permission at runtime.

Now, we will add code to take CAMERA and WRITE_EXTERNAL_STORAGE permissions from user. As we already know, App should provide runtime permissions for devices on or after android 6.0 . So, we will also add code to get permissions at runtime.

Permission prior to Android 6.0

Open main/AndroidManifest.xml file. Then, add below code into it.

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

For devices prior to android 6.0, we write all the required permissions in AndroidManifest file. So, we have written all the permissions in this file.

Permission after Android 6.0

We will write code in kotlin file to get permissions at runtime. So, open main/java/com.tutorialwing.surfaceview/MainActivity.kt file. Then, write below code into it.

package com.tutorialwing.surfaceview

import android.Manifest.permission.CAMERA
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.AlertDialog
import android.content.pm.PackageManager
import android.hardware.Camera
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.Toast
import java.util.*

class MainActivity : AppCompatActivity(), SurfaceHolder.Callback, Camera.PictureCallback {

    private val neededPermissions = arrayOf(CAMERA, WRITE_EXTERNAL_STORAGE)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Check code to get permissions if needed.
        val result = checkPermission()
        if (result) {
            // if permissions granted, update ui accordingly.
        }
    }

    private fun checkPermission(): Boolean {
        val currentAPIVersion = Build.VERSION.SDK_INT
        if (currentAPIVersion >= android.os.Build.VERSION_CODES.M) {
            val permissionsNotGranted = ArrayList<String>()
            for (permission in neededPermissions) {
                if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                    permissionsNotGranted.add(permission)
                }
            }
            if (permissionsNotGranted.size > 0) {
                var shouldShowAlert = false
                for (permission in permissionsNotGranted) {
                    shouldShowAlert = ActivityCompat.shouldShowRequestPermissionRationale(this, permission)
                }

                val arr = arrayOfNulls<String>(permissionsNotGranted.size)
                val permissions = permissionsNotGranted.toArray(arr)
                if (shouldShowAlert) {
                    showPermissionAlert(permissions)
                } else {
                    requestPermissions(permissions)
                }
                return false
            }
        }
        return true
    }

    private fun showPermissionAlert(permissions: Array<String?>) {
        val alertBuilder = AlertDialog.Builder(this)
        alertBuilder.setCancelable(true)
        alertBuilder.setTitle(R.string.permission_required)
        alertBuilder.setMessage(R.string.permission_message)
        alertBuilder.setPositiveButton(android.R.string.yes) { _, _ -> requestPermissions(permissions) }
        val alert = alertBuilder.create()
        alert.show()
    }

    private fun requestPermissions(permissions: Array<String?>) {
        ActivityCompat.requestPermissions(this@MainActivity, permissions, REQUEST_CODE)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            REQUEST_CODE -> {
                for (result in grantResults) {
                    if (result == PackageManager.PERMISSION_DENIED) {
                        // Not all permissions granted. Show some message and return.
                        return
                    }
                }

                // All permissions are granted. Do the work accordingly.
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

    companion object {
        const val REQUEST_CODE = 100
    }
}

Above code have been written to get permissions at runtime. We need CAMERA and WRITE_EXTERNAL_STORAGE permissions to run the application.

Different methods are –

(a) checkPermission() – This method checks if all the required permissions have been granted or not. If not, either showPermissionAlert() or requestPermissions() method is called.

(b) showPermissionAlert() – This method display alert dialog to the user that contains message (informing user that all the permissions must be granted to the user to run the application).

(c) requestPermissions() – This method calls ActivityCompat.requestPermissions method that display ui to grant all the permissions by user.

(d) onRequestPermissionsResult() – This method is called when action on all permissions are complete. Permissions may be granted or rejected.

5. Setup SurfaceView and Show Camera in kotlin file

Till now, we have done basic setup for surfaceView using kotlin. Now, we will show surfaceView and show camera to the user. Then, user can take appropriate action like taking photo etc.

We assume that all the permissions have been granted by user.

So, open src/main/java/com.tutorialwing.surfaceview/MainActivity.kt file. Then, add below code into it.

package com.tutorialwing.surfaceview

import android.Manifest.permission.CAMERA
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.AlertDialog
import android.content.pm.PackageManager
import android.hardware.Camera
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.View
import android.widget.Button
import android.widget.Toast
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.util.*

class MainActivity : AppCompatActivity(), SurfaceHolder.Callback, Camera.PictureCallback {

    private var surfaceHolder: SurfaceHolder? = null
    private var camera: Camera? = null

    private var surfaceView: SurfaceView? = null

    private val neededPermissions = arrayOf(CAMERA, WRITE_EXTERNAL_STORAGE)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        surfaceView = findViewById(R.id.surfaceView)
        val result = checkPermission()
        if (result) {
            // Code to show surfaceView and camera. 
        }
    }

    // Other code relate to permissions at run time.
    //...
    //...

    private fun setViewVisibility(id: Int, visibility: Int) {
        val view = findViewById<View>(id)
        view!!.visibility = visibility
    }

    private fun setupSurfaceHolder() {
        setViewVisibility(R.id.startBtn, View.VISIBLE)
        setViewVisibility(R.id.surfaceView, View.VISIBLE)

        surfaceHolder = surfaceView!!.holder
        surfaceHolder!!.addCallback(this)
        setBtnClick()
    }

    private fun setBtnClick() {
        val startBtn = findViewById<Button>(R.id.startBtn)
        startBtn?.setOnClickListener { captureImage() }
    }

    private fun captureImage() {
        if (camera != null) {
            camera!!.takePicture(null, null, this)
        }
    }

    override fun surfaceCreated(surfaceHolder: SurfaceHolder) {
        startCamera()
    }

    private fun startCamera() {
        camera = Camera.open()
        camera!!.setDisplayOrientation(90)
        try {
            camera!!.setPreviewDisplay(surfaceHolder)
            camera!!.startPreview()
        } catch (e: IOException) {
            e.printStackTrace()
        }

    }

    override fun surfaceChanged(surfaceHolder: SurfaceHolder, i: Int, i1: Int, i2: Int) {
        resetCamera()
    }

    private fun resetCamera() {
        if (surfaceHolder!!.surface == null) {
            // Return if preview surface does not exist
            return
        }

        // Stop if preview surface is already running.
        camera!!.stopPreview()
        try {
            // Set preview display
            camera!!.setPreviewDisplay(surfaceHolder)
        } catch (e: IOException) {
            e.printStackTrace()
        }

        // Start the camera preview...
        camera!!.startPreview()
    }

    override fun surfaceDestroyed(surfaceHolder: SurfaceHolder) {
        releaseCamera()
    }

    private fun releaseCamera() {
        camera!!.stopPreview()
        camera!!.release()
        camera = null
    }

    override fun onPictureTaken(bytes: ByteArray, camera: Camera) {
        saveImage(bytes)
        resetCamera()
    }

    private fun saveImage(bytes: ByteArray) {
        val outStream: FileOutputStream
        try {
            val fileName = "TUTORIALWING_" + System.currentTimeMillis() + ".jpg"
            val file = File(Environment.getExternalStorageDirectory(), fileName)
            outStream = FileOutputStream(file)
            outStream.write(bytes)
            outStream.close()
            Toast.makeText(this@MainActivity, "Picture Saved: $fileName", Toast.LENGTH_LONG).show()
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
}

Above code are responsible for setup of surfaceView using kotlin. We have shown when to show surfaceView or camera, how to take picture using camera, how to save picture taken using camera etc. Basically, we are showing how can you manage your surfaceView using Kotlin to display camera successfully.

Here, we have implemented two interface –

(a) SurfaceHolder.Callback

(b) Camera.PictureCallback

5.1 SurfaceHolder.Callback

SurfaceHolder.Callback interface is used to receive changes about the surface and setup surfaceView accordingly. Below methods are implemented using this interface –

(i) surfaceCreated() method – This method is called immediately after surface is first created. So, here we are starting the camera in this application.

(ii) surfaceChanged() method – This method is called when there is any structural changes to the surface. As you would have guessed by now, we reset the camera when-ever there is any changes to the surface. Then, start the camera again.

(iii) surfaceDestroyed() method – This method is called immediately before surface is being destroyed. Since surface is being destroyed, we release all the resources here. For example, access to camera etc.

Another Interface is –

5.2 Camera.PictureCallback

This is called when picture is taken by camera. There is one method – onPictureTaken which is called when picture is taken. So, we have written code to save taken picture in external storage. Finally, we are showing a toast message to the user about saved picture.




Final MainActivity.kt code

Final code in main/java/com.tutorialwing.surfaceview/MainActivity.kt file including permissions at runtime and surfaceView setup etc. would be like below –

package com.tutorialwing.surfaceview

import android.Manifest.permission.CAMERA
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.AlertDialog
import android.content.pm.PackageManager
import android.hardware.Camera
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.View
import android.widget.Button
import android.widget.Toast
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.util.*

class MainActivity : AppCompatActivity(), SurfaceHolder.Callback, Camera.PictureCallback {

    private var surfaceHolder: SurfaceHolder? = null
    private var camera: Camera? = null

    private var surfaceView: SurfaceView? = null

    private val neededPermissions = arrayOf(CAMERA, WRITE_EXTERNAL_STORAGE)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        surfaceView = findViewById(R.id.surfaceView)
        val result = checkPermission()
        if (result) {
            setupSurfaceHolder()
        }
    }

    private fun checkPermission(): Boolean {
        val currentAPIVersion = Build.VERSION.SDK_INT
        if (currentAPIVersion >= android.os.Build.VERSION_CODES.M) {
            val permissionsNotGranted = ArrayList<String>()
            for (permission in neededPermissions) {
                if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                    permissionsNotGranted.add(permission)
                }
            }
            if (permissionsNotGranted.size > 0) {
                var shouldShowAlert = false
                for (permission in permissionsNotGranted) {
                    shouldShowAlert = ActivityCompat.shouldShowRequestPermissionRationale(this, permission)
                }

                val arr = arrayOfNulls<String>(permissionsNotGranted.size)
                val permissions = permissionsNotGranted.toArray(arr)
                if (shouldShowAlert) {
                    showPermissionAlert(permissions)
                } else {
                    requestPermissions(permissions)
                }
                return false
            }
        }
        return true
    }

    private fun showPermissionAlert(permissions: Array<String?>) {
        val alertBuilder = AlertDialog.Builder(this)
        alertBuilder.setCancelable(true)
        alertBuilder.setTitle(R.string.permission_required)
        alertBuilder.setMessage(R.string.permission_message)
        alertBuilder.setPositiveButton(android.R.string.yes) { _, _ -> requestPermissions(permissions) }
        val alert = alertBuilder.create()
        alert.show()
    }

    private fun requestPermissions(permissions: Array<String?>) {
        ActivityCompat.requestPermissions(this@MainActivity, permissions, REQUEST_CODE)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            REQUEST_CODE -> {
                for (result in grantResults) {
                    if (result == PackageManager.PERMISSION_DENIED) {
                        Toast.makeText(this@MainActivity, R.string.permission_warning, Toast.LENGTH_LONG).show()
                        setViewVisibility(R.id.showPermissionMsg, View.VISIBLE)
                        return
                    }
                }

                setupSurfaceHolder()
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

    private fun setViewVisibility(id: Int, visibility: Int) {
        val view = findViewById<View>(id)
        view!!.visibility = visibility
    }

    private fun setupSurfaceHolder() {
        setViewVisibility(R.id.startBtn, View.VISIBLE)
        setViewVisibility(R.id.surfaceView, View.VISIBLE)

        surfaceHolder = surfaceView!!.holder
        surfaceHolder!!.addCallback(this)
        setBtnClick()
    }

    private fun setBtnClick() {
        val startBtn = findViewById<Button>(R.id.startBtn)
        startBtn?.setOnClickListener { captureImage() }
    }

    private fun captureImage() {
        if (camera != null) {
            camera!!.takePicture(null, null, this)
        }
    }

    override fun surfaceCreated(surfaceHolder: SurfaceHolder) {
        startCamera()
    }

    private fun startCamera() {
        camera = Camera.open()
        camera!!.setDisplayOrientation(90)
        try {
            camera!!.setPreviewDisplay(surfaceHolder)
            camera!!.startPreview()
        } catch (e: IOException) {
            e.printStackTrace()
        }

    }

    override fun surfaceChanged(surfaceHolder: SurfaceHolder, i: Int, i1: Int, i2: Int) {
        resetCamera()
    }

    private fun resetCamera() {
        if (surfaceHolder!!.surface == null) {
            // Return if preview surface does not exist
            return
        }

        // Stop if preview surface is already running.
        camera!!.stopPreview()
        try {
            // Set preview display
            camera!!.setPreviewDisplay(surfaceHolder)
        } catch (e: IOException) {
            e.printStackTrace()
        }

        // Start the camera preview...
        camera!!.startPreview()
    }

    override fun surfaceDestroyed(surfaceHolder: SurfaceHolder) {
        releaseCamera()
    }

    private fun releaseCamera() {
        camera!!.stopPreview()
        camera!!.release()
        camera = null
    }

    override fun onPictureTaken(bytes: ByteArray, camera: Camera) {
        saveImage(bytes)
        resetCamera()
    }

    private fun saveImage(bytes: ByteArray) {
        val outStream: FileOutputStream
        try {
            val fileName = "TUTORIALWING_" + System.currentTimeMillis() + ".jpg"
            val file = File(Environment.getExternalStorageDirectory(), fileName)
            outStream = FileOutputStream(file)
            outStream.write(bytes)
            outStream.close()
            Toast.makeText(this@MainActivity, "Picture Saved: $fileName", Toast.LENGTH_LONG).show()
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    companion object {
        const val REQUEST_CODE = 100
    }
}

Final code in MainActivity.kt file would be as shown above that includes camera permissions at runtime, setup for
surfaceView using kotlin, camera setup, save taken picture into external folder etc.

Note – You can compare this with your changes in MainActivity.kt file. If you get any problem, you can directly copy/paste this code and check if the app is working as expected.

Since AndroidManifest.xml file is very important in any android application, we are also going to see the content inside this file.

AndroidManifest.xml

Code inside src/main/AndroidManifest.xml file is as below –

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.tutorialwing.surfaceview"
		  xmlns:android="http://schemas.android.com/apk/res/android">

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

	<application
		android:allowBackup="true"
		android:icon="@mipmap/ic_launcher"
		android:label="@string/app_name"
		android:roundIcon="@mipmap/ic_launcher_round"
		android:supportsRtl="true"
		android:theme="@style/AppTheme">
		<activity android:name=".MainActivity">
			<intent-filter>
				<action android:name="android.intent.action.MAIN"/>

				<category android:name="android.intent.category.LAUNCHER"/>
			</intent-filter>
		</activity>
	</application>

</manifest>

When we run the program, we will get output as shown above.

That’s end of tutorial on Android SurfaceView using Kotlin. We have successfully shown camera using surfaceView. If you get any problem, you may download sourceCode of this tutorial.

Support Us

If you like Tutorialwing and would like to contribute, you can email an article on any educational topic at tutorialwing@gmail.com. We would love to publish your article. See your article on Tutorialwing and help others with your knowledge. Follow Facebook, LinkedIn, Google+, Twitter, Youtube for latest updates.
Greetings!
We have recently published 100+ articles on android tutorials with kotlin and java. If you need, you may visit Android Tutorial for beginners page. You can also check Kotlin Tutorial for beginners