SurfaceView

Android SurfaceView Example

In Android, all simple layout views are all drawn on the same GUI thread which is also used for all user interaction. So if we need to update GUI rapidly or if the rendering takes too much time and affects user experience then we should use SurfaceView.

The Android SurfaceView provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface, however, the SurfaceView takes care of placing the surface at the correct location on the screen.

So, in this example, we are going to see the use of Android SurfaceView through a camera appliction that will use the SurfaceView to preview the camera angle to the screen of the mobile device.

For our example will use the following tools in a Windows 64-bit or an OS X platform:

  • JDK 1.7
  • Eclipse 4.2 Juno
  • Android SDK 4.4.2

Let’s take a closer look:

1. Create a New Android Application Project

Tip
You may skip project creation and jump directly to the beginning of the example below.

Open Eclipse IDE and go to File → New → Project → Android Application Project.

Figure 2. Create a new Android project
Figure 1. Create a new Android project

Specify the name of the application, the project and the package and then click Next.

Figure 2. Create a new Android project name
Figure 2. Create a new Android project name

In the next window, the “Create Activity” option should be checked. The new created activity will be the main activity of your project. Then press Next button.

Create Activity
Figure 3. Configure the project

In “Configure Launcher Icon” window you should choose the icon you want to have in your app. We will use the default icon of android, so click Next.

Configure Launcher Icon
Figure 4. Configure the launcher icon

Select the “Blank Activity” option and press Next.

Blank Activity
Figure 5. Create the activity and select its type

You have to specify a name for the new Activity and a name for the layout description of your app. The .xml file for the layout will automatically be created in the res/layout folder. It will also be created a fragment layout xml, that we are not going to use in this project and you can remove it if you want. Then press Finish.

Figure 6. Create a new blank activity
Figure 6. Create a new blank activity

Here you can see, how will the structure of the project become when finished:

Figure 7. The tree of the project
Figure 7. The tree of the project

2. Creating the layout of the main AndroidSurfaceViewExample

We are going to make a very simple layout xml for the AndroidSurfaceViewExample.class, that only consists of a LinearLayout that contains the SurfaceView and a clickable LinearLayout.

Open res/layout/activity_main.xml, go to the respective xml tab and paste the following:

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.javacodegeeks.androidsurfaceviewexample.AndroidSurfaceviewExample" >

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:id="@+id/capture"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:gravity="center"
        android:onClick="captureImage"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp"
            android:text="Capture"
            android:textAppearance="?android:attr/textAppearanceLarge" />
    </LinearLayout>

</LinearLayout>

3. Creating the source code of the main AndroidSurfaceViewExample Activity

Open src/com.javacodegeeks.androidcursorexample/AndroidSurfaceViewExample.java file and paste the code below.

AndroidSurfaceViewExample.java

package com.javacodegeeks.androidsurfaceviewexample;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidSurfaceviewExample extends Activity implements SurfaceHolder.Callback {
	TextView testView;

	Camera camera;
	SurfaceView surfaceView;
	SurfaceHolder surfaceHolder;

	PictureCallback rawCallback;
	ShutterCallback shutterCallback;
	PictureCallback jpegCallback;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_main);

		surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
		surfaceHolder = surfaceView.getHolder();

		// Install a SurfaceHolder.Callback so we get notified when the
		// underlying surface is created and destroyed.
		surfaceHolder.addCallback(this);

		// deprecated setting, but required on Android versions prior to 3.0
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

		jpegCallback = new PictureCallback() {
			public void onPictureTaken(byte[] data, Camera camera) {
				FileOutputStream outStream = null;
				try {
					outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
					outStream.write(data);
					outStream.close();
					Log.d("Log", "onPictureTaken - wrote bytes: " + data.length);
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
				}
				Toast.makeText(getApplicationContext(), "Picture Saved", 2000).show();
				refreshCamera();
			}
		};
	}

	public void captureImage(View v) throws IOException {
		//take the picture
		camera.takePicture(null, null, jpegCallback);
	}

	public void refreshCamera() {
		if (surfaceHolder.getSurface() == null) {
			// preview surface does not exist
			return;
		}

		// stop preview before making changes
		try {
			camera.stopPreview();
		} catch (Exception e) {
			// ignore: tried to stop a non-existent preview
		}

		// set preview size and make any resize, rotate or
		// reformatting changes here
		// start preview with new settings
		try {
			camera.setPreviewDisplay(surfaceHolder);
			camera.startPreview();
		} catch (Exception e) {

		}
	}

	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		// Now that the size is known, set up the camera parameters and begin
		// the preview.
		refreshCamera();
	}

	public void surfaceCreated(SurfaceHolder holder) {
		try {
			// open the camera
			camera = Camera.open();
		} catch (RuntimeException e) {
			// check for exceptions
			System.err.println(e);
			return;
		}
		Camera.Parameters param;
		param = camera.getParameters();

		// modify parameter
		param.setPreviewSize(352, 288);
		camera.setParameters(param);
		try {
			// The Surface has been created, now tell the camera where to draw
			// the preview.
			camera.setPreviewDisplay(surfaceHolder);
			camera.startPreview();
		} catch (Exception e) {
			// check for exceptions
			System.err.println(e);
			return;
		}
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		// stop preview and release camera
		camera.stopPreview();
		camera.release();
		camera = null;
	}

}

Let’s see in detail the code above.

We set the activity_main.xml layout and we initialize our main layout by:

setContentView(R.layout.activity_main);

In these lines, we find our SurfaceView from our main layout and we get a holder, an abstract interface to someone holding a display surface. This, allows us to control the surface size and format, edit the pixels in the surface, and monitor changes to the surface. We also install a SurfaceHolder.Callback so we get notified when the underlying surface is created and destroyed. We have also written the surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); deprecated setting, but required on Android versions prior to 3.0.

	surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
	surfaceHolder = surfaceView.getHolder();
	surfaceHolder.addCallback(this);
	surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

We have implemented the basic methods of the SurfaceView, public void surfaceCreated(SurfaceHolder holder), public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) and public void surfaceDestroyed(SurfaceHolder holder)).

In public void surfaceCreated(SurfaceHolder holder), we are trying to open the camera service, and also to set the camera parameters and when the Surface has been created, to draw the preview.

	public void surfaceCreated(SurfaceHolder holder) {
		try {			
			camera = Camera.open();
		} catch (RuntimeException e) {
			System.err.println(e);
			return;
		}
		Camera.Parameters param;
		param = camera.getParameters();

		param.setPreviewSize(352, 288);
		camera.setParameters(param);
		try {
			camera.setPreviewDisplay(surfaceHolder);
			camera.startPreview();
		} catch (Exception e) {
			System.err.println(e);
			return;
		}
	}

In public void surfaceChanged(SurfaceHolder holder, int format, int w, int h), we are trying to reset the preview size and make any resize, rotate or reformatting changes, in order to start the preview with new settings.

	public void refreshCamera() {
		if (surfaceHolder.getSurface() == null) {
			return;
		}
		try {
			camera.stopPreview();
		} catch (Exception e) {
			
		}
		try {
			camera.setPreviewDisplay(surfaceHolder);
			camera.startPreview();
		} catch (Exception e) {

		}
	}

In public void surfaceDestroyed(SurfaceHolder holder), we stop the preview and we release the camera service, in order to be used from other applications.

	public void surfaceDestroyed(SurfaceHolder holder) {
		// stop preview and release camera
		camera.stopPreview();
		camera.release();
		camera = null;
	}

4. Android Manifest

Because this application uses the hardware camera and tries to save images to the device’s external storage (SD Card), we must also specify this in the manifest. The AndroidManifest.xml of our project is simple and contains the permissions:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.javacodegeeks.androidsurfaceviewexample"
    android:versionCode="1"
    android:versionName="1.0" >

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

    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name="com.javacodegeeks.androidsurfaceviewexample.AndroidSurfaceviewExample"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
Tip
Note, that if an essential function of your application is taking pictures, then restrict its visibility on Google Play to devices that have a camera. To advertise that your application depends on having a camera, put a tag in your manifest file.

5. Build, compile and run

When we build, compile and run our project, the main AndroidSurfaceViewExample should look like this:

Figure 8. Figure This is how the main Activity looks like
Figure 8. Figure This is how the main Activity looks like.

Download the Eclipse Project

This was an example of Android AndroidSurfaceViewExample.

Download
You can download the full source code of this example here: AndroidSurfaceViewExample

Chryssa Aliferi

Chryssa is a Computer Science graduate from Athens University of Economic and Business. During her studies, Chryssa carried out a great variety of projects ranging from networking to software engineering. She is very keen on front end development especially on mobile technologies and web applications. She has worked as a junior Software Engineer in the telecommunications area and currently works as an Android Developer.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Darren Mossman
5 years ago

Excellent tutorial. Thanks!

tongdee
tongdee
5 years ago

it work fine. thanks you

Back to top button