logo

In this tutorial, we will implement one simple camera app with following features:
1) Launch the camera app and it will start the camera with capture and exit button at bottom.
1) On capture, it will capture the picture and show it in ImageView.
2) If you press save, it will save the picture on SD card
3) If you recapture, it will again start the camera to take the picture.
4) If you exit the app, it will exit from the app.

Screenshot

Lets start by creating the project with name CameraApp with CameraAppActivity.

Define Layout XML file

Once the project with Activity, CameraAppActivity is created, first step is to create XML layout file. It is defined as follows:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <LinearLayout android:id="@+id/button_layout"
  	android:layout_alignParentBottom="true"
  	android:orientation="horizontal"
  	android:gravity="center"
  	android:layout_width="fill_parent"
  	android:layout_height="wrap_content">
  <div style="display: none"></div> 	<Button android:id="@+id/exit_button"
  		android:layout_width="wrap_content"
  		android:layout_height="wrap_content"
  		android:layout_gravity="left"
  		android:text="Exit"/>
  	<Button android:id="@+id/ok_button"
  		android:layout_width="wrap_content"
  		android:layout_height="wrap_content"
  		android:layout_gravity="left"
  		android:text="Upload"/>
  	<Button android:id="@+id/recapture_button"
  		android:layout_width="wrap_content"
  		android:layout_height="wrap_content"
  		android:layout_gravity="right"
  		android:text="Recapture"/>
  	<Button android:id="@+id/capture_button"
  		android:layout_width="wrap_content"
  		android:layout_height="wrap_content"
  		android:layout_gravity="right"
  		android:text="Capture"/>
  </LinearLayout>
  <FrameLayout android:id="@+id/preview"
  		android:layout_width="fill_parent"
  		android:layout_height="fill_parent"
  		android:layout_above="@id/button_layout">
  </FrameLayout>
  <ImageView android:id="@+id/img"
  		android:layout_width="wrap_content"
  		android:layout_height="wrap_content"
  		android:layout_above="@id/button_layout"/>
</RelativeLayout>

It has linear horizontal layout of all buttons. All buttons are self-explanatory. FrameLayout to hold the preview of camera. It also have ImageView which will display the preview of the captured picture.

Basic logic is:
1) When application is launched, it will start also the camera preview and will make the ImageView invisible.
2) Camera Preview will be displayed on SurfaceView. So we will create our own SurfaceView preview.
2) When picture is captured, it is displayed in ImageView. So, ImageView is made visible while preview invisible.
3) When OK button is pressed, captured bitmap is saved to SD card.

Create Preview

We will create the SurfaceView on which it will show preview from the camera.

import java.io.IOException;
import java.util.List;

import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class Preview extends SurfaceView implements SurfaceHolder.Callback{

	SurfaceHolder mHolder;
	public Camera camera;

	public Preview(Context context) {
		super(context);
		// Install a SurfaceHolder.Callback so we get notified when the
		// underlying surface is created and destroyed.
		mHolder = getHolder();
		mHolder.addCallback(this);
		mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);	
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		camera.startPreview();
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		// The Surface has been created, acquire the camera and tell it where
		// to draw.
		camera = Camera.open();
		try {
			camera.setPreviewDisplay(holder);
			Camera.Parameters parameters=camera.getParameters();
			List<Size> sizes=parameters.getSupportedPictureSizes();
			parameters.setPictureSize(sizes.get(0).width, sizes.get(0).height);
			camera.setParameters(parameters);
			camera.startPreview();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		// Surface will be destroyed when we return, so stop the preview.
		// Because the CameraDevice object is not a shared resource, it's very
		// important to release it when the activity is paused.
		camera.stopPreview();
		camera.release();
		camera = null;
	}

}

Activity class

Application only have one Activity, CameraAppActivity. It is as below:

public class CameraAppActivity extends Activity{
	private Bitmap bmp=null;
	ProgressDialog pg=null;
	private Preview preview;
	Button ok_button,click_button,cancel_button,try_again_button;
	//public static byte[] bitmapArray=null;
	@Override
	public void onCreate(Bundle bundle) {
		super.onCreate(bundle);
		setContentView(R.layout.camera_layout);
		
		preview=new Preview(this);
		((FrameLayout) findViewById(R.id.preview)).addView(preview);
		findViewById(R.id.preview).setVisibility(View.VISIBLE);
		findViewById(R.id.img).setVisibility(View.GONE);
		
		ButtonListener listener=new ButtonListener();
		click_button=((Button) findViewById(R.id.capture_button));
		click_button.setVisibility(View.VISIBLE);
		click_button.setOnClickListener(listener);
		
		try_again_button=((Button) findViewById(R.id.recapture_button));
		try_again_button.setVisibility(View.GONE);
		try_again_button.setOnClickListener(listener);
		
		cancel_button=((Button) findViewById(R.id.cancel_button));
		cancel_button.setVisibility(View.VISIBLE);
		cancel_button.setOnClickListener(listener);
		
		ok_button=((Button) findViewById(R.id.ok_button));
		ok_button.setVisibility(View.GONE);
		ok_button.setOnClickListener(listener);
	}
	
	private class ButtonListener implements View.OnClickListener{

		@Override
		public void onClick(View v) {
			if(v.equals(findViewById(R.id.capture_button))){
				pg=ProgressDialog.show(CameraAppActivity.this, null, "Capturing Image..");
				pg.show();
				preview.camera.takePicture(shutterCallback, rawCallback, jpegCallback);
			}else if(v.equals(findViewById(R.id.cancel_button))){
				finish();
			}else if(v.equals(findViewById(R.id.ok_button))){
				saveImage();
			}else if(v.equals(findViewById(R.id.recapture_button))){
				findViewById(R.id.img).setVisibility(View.GONE);
				findViewById(R.id.preview).setVisibility(View.VISIBLE);
				preview.camera.startPreview();
				ok_button.setVisibility(View.GONE);
				cancel_button.setVisibility(View.VISIBLE);
				try_again_button.setVisibility(View.GONE);
				click_button.setVisibility(View.VISIBLE);
			}
		}
		
	}

	ShutterCallback shutterCallback = new ShutterCallback() {
		public void onShutter() {
			//Log.d(TAG, "onShutter'd");
			System.out.println("In ShutterCallback");
		}
	};

	/** Handles data for raw picture */
	PictureCallback rawCallback = new PictureCallback() {
		public void onPictureTaken(byte[] data, Camera camera) {
			if(data!=null){
				bmp=BitmapFactory.decodeByteArray(data,0,data.length);
				findViewById(R.id.img).setVisibility(View.VISIBLE);
				((ImageView)findViewById(R.id.img)).setImageBitmap(bmp);
				findViewById(R.id.preview).setVisibility(View.GONE);

				if(pg!=null)
					pg.dismiss();
				ok_button.setVisibility(View.VISIBLE);
				click_button.setVisibility(View.GONE);
				try_again_button.setVisibility(View.VISIBLE);
			}
		}
	};

	/** Handles data for jpeg picture */
	PictureCallback jpegCallback = new PictureCallback() {
		public void onPictureTaken(byte[] data, Camera camera) {
			if(data!=null){
				bmp=BitmapFactory.decodeByteArray(data,0,data.length);
				findViewById(R.id.img).setVisibility(View.VISIBLE);
				((ImageView)findViewById(R.id.img)).setImageBitmap(bmp);
				findViewById(R.id.preview).setVisibility(View.GONE);

				if(pg!=null)
					pg.dismiss();
				ok_button.setVisibility(View.VISIBLE);
				click_button.setVisibility(View.GONE);
				try_again_button.setVisibility(View.VISIBLE);
			}
		}
	};

	public void saveImage(){
		FileOutputStream out;
		try {
			File file=new File(Environment.getExternalStorageDirectory()+"temp.jpg");
			if(!file.exists()) file.createNewFile();
			out = new FileOutputStream(file);
			bmp.compress(CompressFormat.JPEG, 90, out);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

Take Picture

To take the picture, we call takePicture() method of Camera. takePicture() is asynchronous method so we pass series of callbacks like ShutterCallback and PictureCallback (RawCallback & JPEGCallback).

ShutterCallback is a callback for image capture moment.
Raw PictureCallback is a callback for raw image data.
JPEG PictureCallback is a callback for JPEG encoding image data.

It’s upto you to do something with the data such as save it on SD card.

preview.camera.takePicture(shutterCallback, rawCallback, jpegCallback);

Saving picture on SD card

public void saveImage(){
	FileOutputStream out;
	try {
		File file=new File(Environment.getExternalStorageDirectory()+"temp.jpg");
		if(!file.exists()) file.createNewFile();
		out = new FileOutputStream(file);
		bmp.compress(CompressFormat.JPEG, 90, out);
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (Exception e) {
		e.printStackTrace();
	}
}

Finally, please don’t forgot to add following permissions in AndroidManifest.xml which we always forget :-).

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

Founder @CodeToArt, Leads Android Development at CodeToArt.

7 Comments
  • Alejandro

    I have a problem in this part of the code:
    parameters.setPictureSize(sizes.get(0).width, sizes.get(0).height);

    The logcat says

    09-04 14:02:30.686: E/AndroidRuntime(656): Uncaught handler: thread main exiting due to uncaught exception
    09-04 14:02:30.716: E/AndroidRuntime(656): java.lang.NullPointerException
    09-04 14:02:30.716: E/AndroidRuntime(656): at com.android.project.sabebi.media.CameraActivity$Preview.surfaceCreated(CameraActivity.java:176)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.SurfaceView.updateWindow(SurfaceView.java:454)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.SurfaceView.dispatchDraw(SurfaceView.java:287)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewGroup.drawChild(ViewGroup.java:1529)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1258)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewGroup.drawChild(ViewGroup.java:1529)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1258)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewGroup.drawChild(ViewGroup.java:1529)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1258)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewGroup.drawChild(ViewGroup.java:1529)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1258)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.View.draw(View.java:6538)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.widget.FrameLayout.draw(FrameLayout.java:352)
    09-04 14:02:30.716: E/AndroidRuntime(656): at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1830)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewRoot.draw(ViewRoot.java:1349)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewRoot.performTraversals(ViewRoot.java:1114)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.view.ViewRoot.handleMessage(ViewRoot.java:1633)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.os.Handler.dispatchMessage(Handler.java:99)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.os.Looper.loop(Looper.java:123)
    09-04 14:02:30.716: E/AndroidRuntime(656): at android.app.ActivityThread.main(ActivityThread.java:4363)
    09-04 14:02:30.716: E/AndroidRuntime(656): at java.lang.reflect.Method.invokeNative(Native Method)
    09-04 14:02:30.716: E/AndroidRuntime(656): at java.lang.reflect.Method.invoke(Method.java:521)
    09-04 14:02:30.716: E/AndroidRuntime(656): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
    09-04 14:02:30.716: E/AndroidRuntime(656): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
    09-04 14:02:30.716: E/AndroidRuntime(656): at dalvik.system.NativeStart.main(Native Method)

    I don’t know exactly what the problem is. Maybe the problem is the size
    Please I need some help in this part

    September 4, 2012
  • Size size = sizes.get(0);
    parameters.setPictureSize(size.width, size.height);

    //it worked for me

    January 22, 2013
  • Mona

    Great work !
    but i am facing an exception(read only file system ) when trying to save the image

    March 4, 2013
  • mano

    It is not working for me 🙁

    i am using Sony Ericson Xperia Arc LT15i

    March 29, 2013
  • mano

    i even added permissions but it showing that

    CameraAppActivity has stopped

    March 29, 2013
  • mano

    Thank you.. it worked… but i have problem in saving image.. the image is not saved.. :/

    April 2, 2013

Leave a Comment

Your email address will not be published.