It would be helpful for you if you already know about TextView Widget.
In this tutorial, we will show camera using android surfaceView and take picture. Then, we will save taken picture into external storage.
Output
Sourcecode(Java)
You can download source code of this tutorial on android SurfaceView by entering the details –
[emaillocker id=”4963″]
SurfaceView Java Sourcecode
[/emaillocker]
Getting Started
Android SurfaceView widget can be defined as below –
SurfaceView is a subclass of View class that provides a drawing surface embedded inside of a view hierarchy. You can also control the surface of the view or it’s size or the place where surfaceView will be placed in the window.
You can access the underlying surface using SurfaceHolder interface by calling getHolder().
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 | Sets background of the view. |
2 | android:theme | Sets a theme of the view |
3 | android:visibility | Sets visibility (VISIBLE, INVISIBLE or GONE) of the view |
4 | android:elevation | Sets z-depth of the view |
5 | android:id | Sets id of the view |
6 | android:padding | Sets padding of the view |
Example of Android SurfaceView Widget
At first, we will create android application. Then, we will use surfaceView widget in this application.
1. Creating New Project
Follow steps below to create new project. Please ignore the steps if you have already created a new application.
Step | Description |
---|---|
1. | Open Android Studio. |
2. | Go to File => New => New Project. Write application name as SurfaceView. Then, 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. | If you have followed above process correctly, you will get a newly created project successfully. However, you can also visit post to create a new project to know steps in detail. |
Now, we will modify xml and java file to use android surfaceView widget in the application.
2. Modify Values folder
Open res/values/strings.xml file. Then, add below 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 and 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 modified the code in xml file to update the ui according the permission granted by the user.
– If all the required permission are not granted by the user, then, we will show a message to the user accordingly.
4. Setup For Required Permission at runtime.
In this application, we need two permissions – CAMERA and WRITE_EXTERNAL_STORAGE permissions. As, we know already that we need to provide runtime permissions to the user on or after android version 2.6 . So, prior to android version 2.6, we write the required permissions name in AndroidManifest.xml file.
Now, we will modify the code for required permissions.
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"/>
Permission after Android 6.0
We will write code in java file to get permissions at runtime. So, open main/java/com.tutorialwing.surfaceview/MainActivity.java file. Then, write below code into it.
package com.tutorialwing.surfaceview; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import java.util.ArrayList; import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Camera.PictureCallback { public static final int REQUEST_CODE = 100; private String[] neededPermissions = new String[]{CAMERA, WRITE_EXTERNAL_STORAGE}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); boolean result = checkPermission(); if (result) { setupSurfaceHolder(); } } private boolean checkPermission() { int currentAPIVersion = Build.VERSION.SDK_INT; if (currentAPIVersion >= android.os.Build.VERSION_CODES.M) { ArrayList<String> permissionsNotGranted = new ArrayList<>(); for (String permission : neededPermissions) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { permissionsNotGranted.add(permission); } } if (permissionsNotGranted.size() > 0) { boolean shouldShowAlert = false; for (String permission : permissionsNotGranted) { shouldShowAlert = ActivityCompat.shouldShowRequestPermissionRationale(this, permission); } if (shouldShowAlert) { showPermissionAlert(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()])); } else { requestPermissions(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()])); } return false; } } return true; } private void showPermissionAlert(final String[] permissions) { AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this); alertBuilder.setCancelable(true); alertBuilder.setTitle(R.string.permission_required); alertBuilder.setMessage(R.string.permission_message); alertBuilder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { requestPermissions(permissions); } }); AlertDialog alert = alertBuilder.create(); alert.show(); } private void requestPermissions(String[] permissions) { ActivityCompat.requestPermissions(MainActivity.this, permissions, REQUEST_CODE); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_CODE: for (int result : grantResults) { if (result == PackageManager.PERMISSION_DENIED) { // Not all permissions granted. Show message to the user. return; } } // All permissions are granted. So, do the appropriate work now. break; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }
Here, we have written the code to get CAMERA and WRITE_EXTERNAL_STORAGE permissions from the user. If all the permissions are not granted by the user, we will show a message to the user. If all permissions are granted, we will go ahead with the setup of surfaceView, camera etc.
5. Setup SurfaceView and Show Camera in java file
Till now, we have done all the setup (view modification, required permissions setup etc.). Now, we will write the code to setup android surfaceView and show camera. So, open src/main/java/com.tutorialwing.surfaceview/MainActivity.java file. Then, add below code into it.
package com.tutorialwing.surfaceview; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.hardware.Camera; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.support.annotation.NonNull; 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.ArrayList; import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Camera.PictureCallback { private SurfaceHolder surfaceHolder; private Camera camera; public static final int REQUEST_CODE = 100; private SurfaceView surfaceView; private String[] neededPermissions = new String[]{CAMERA, WRITE_EXTERNAL_STORAGE}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); surfaceView = findViewById(R.id.surfaceView); if (surfaceView != null) { boolean result = checkPermission(); if (result) { setupSurfaceHolder(); } } } //...Other code related to permissions //...Other code related to view setup //...etc. private void setupSurfaceHolder() { surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(this); setBtnClick(); } private void setBtnClick() { Button startBtn = findViewById(R.id.startBtn); if (startBtn != null) { startBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { captureImage(); } }); } } public void captureImage() { if (camera != null) { camera.takePicture(null, null, this); } } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { startCamera(); } private void startCamera() { camera = Camera.open(); camera.setDisplayOrientation(90); try { camera.setPreviewDisplay(surfaceHolder); camera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { resetCamera(); } public void resetCamera() { if (surfaceHolder.getSurface() == null) { // Return if preview surface does not exist return; } if (camera != null) { // Stop if preview surface is already running. camera.stopPreview(); try { // Set preview display camera.setPreviewDisplay(surfaceHolder); } catch (IOException e) { e.printStackTrace(); } // Start the camera preview... camera.startPreview(); } } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { releaseCamera(); } private void releaseCamera() { if (camera != null) { camera.stopPreview(); camera.release(); camera = null; } } @Override public void onPictureTaken(byte[] bytes, Camera camera) { // Picture had been taken by camera. So, do appropriate action. For example, save it in file. } }
Here, we have implemented SurfaceHolder.Callback interface to receive information about any changes
to the surface. This interface contains three methods –
(a) surfaceChanged() method – This is called immediately after any structural changes have been made to the surface. This method is called at least once after surfaceCreated() method is called. So, reset the camera here so that it is adjusted according to the changes in surface. So, do the stuffs like start camera preview etc. here.
(b) surfaceCreated() method – This is called immediately after surface is first created.
(c) surfaceDestroyed() method – This is called immediately before a surface is being destroyed. So, do the stuffs like releasing camera resources, stopping the camera preview etc. here.
This callback is set using SurfaceHolder.addCallback method. For example, we have set by calling surfaceHolder.addCallback(this); in setupSurfaceHolder() method.
There is another interface – Camera.PictureCallback. This is called when picture is taken by the camera. onPictureTaken() method is called when picture is taken. So, do the stuffs like saving photo in external storage, showing message to the user etc. here.
Final MainActivity.java code
Final code in MainActivity.java file would be like below –
package com.tutorialwing.surfaceview; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.hardware.Camera; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.support.annotation.NonNull; 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.ArrayList; import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Camera.PictureCallback { private SurfaceHolder surfaceHolder; private Camera camera; public static final int REQUEST_CODE = 100; private SurfaceView surfaceView; private String[] neededPermissions = new String[]{CAMERA, WRITE_EXTERNAL_STORAGE}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); surfaceView = findViewById(R.id.surfaceView); if (surfaceView != null) { boolean result = checkPermission(); if (result) { setupSurfaceHolder(); } } } private boolean checkPermission() { int currentAPIVersion = Build.VERSION.SDK_INT; if (currentAPIVersion >= android.os.Build.VERSION_CODES.M) { ArrayList<String> permissionsNotGranted = new ArrayList<>(); for (String permission : neededPermissions) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { permissionsNotGranted.add(permission); } } if (permissionsNotGranted.size() > 0) { boolean shouldShowAlert = false; for (String permission : permissionsNotGranted) { shouldShowAlert = ActivityCompat.shouldShowRequestPermissionRationale(this, permission); } if (shouldShowAlert) { showPermissionAlert(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()])); } else { requestPermissions(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()])); } return false; } } return true; } private void showPermissionAlert(final String[] permissions) { AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this); alertBuilder.setCancelable(true); alertBuilder.setTitle(R.string.permission_required); alertBuilder.setMessage(R.string.permission_message); alertBuilder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { requestPermissions(permissions); } }); AlertDialog alert = alertBuilder.create(); alert.show(); } private void requestPermissions(String[] permissions) { ActivityCompat.requestPermissions(MainActivity.this, permissions, REQUEST_CODE); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_CODE: for (int result : grantResults) { if (result == PackageManager.PERMISSION_DENIED) { Toast.makeText(MainActivity.this, R.string.permission_warning, Toast.LENGTH_LONG).show(); setViewVisibility(R.id.showPermissionMsg, View.VISIBLE); return; } } setupSurfaceHolder(); break; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } private void setViewVisibility(int id, int visibility) { View view = findViewById(id); if (view != null) { view.setVisibility(visibility); } } private void setupSurfaceHolder() { setViewVisibility(R.id.startBtn, View.VISIBLE); setViewVisibility(R.id.surfaceView, View.VISIBLE); surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(this); setBtnClick(); } private void setBtnClick() { Button startBtn = findViewById(R.id.startBtn); if (startBtn != null) { startBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { captureImage(); } }); } } public void captureImage() { if (camera != null) { camera.takePicture(null, null, this); } } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { startCamera(); } private void startCamera() { camera = Camera.open(); camera.setDisplayOrientation(90); try { camera.setPreviewDisplay(surfaceHolder); camera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { resetCamera(); } public void resetCamera() { if (surfaceHolder.getSurface() == null) { // Return if preview surface does not exist return; } if (camera != null) { // Stop if preview surface is already running. camera.stopPreview(); try { // Set preview display camera.setPreviewDisplay(surfaceHolder); } catch (IOException e) { e.printStackTrace(); } // Start the camera preview... camera.startPreview(); } } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { releaseCamera(); } private void releaseCamera() { if (camera != null) { camera.stopPreview(); camera.release(); camera = null; } } @Override public void onPictureTaken(byte[] bytes, Camera camera) { saveImage(bytes); resetCamera(); } private void saveImage(byte[] bytes) { FileOutputStream outStream; try { String fileName = "TUTORIALWING_" + System.currentTimeMillis() + ".jpg"; File file = new File(Environment.getExternalStorageDirectory(), fileName); outStream = new FileOutputStream(file); outStream.write(bytes); outStream.close(); Toast.makeText(MainActivity.this, "Picture Saved: " + fileName, Toast.LENGTH_LONG).show(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
This is final code in MainActivity.java file that includes camera permissions at runtime, surfaceView setup, camera setup, save taken picture into external folder etc.
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 Widget.
You must be logged in to post a comment.