Integrar la cámara en una APP – Android¶
En esta entrada voy a explicar cómo se puede integrar la cámara de los dispositivos Android a una actividad. Esto quiere decir, que si tienes una app que necesita sacar una foto, no hace falta que hagas uso de otra app para tomar la foto, sino que ya viene incluida en la APP que estamos diseñando.
Android nos brinda una API para poder manejar la cámara, ya sea para tomar fotos o grabar un vídeo, y obtener las características de la misma. En esta entrada vamos a ver la clase Camera. Existe otra clase nueva, con mayores funcionalidades que es la Camera2, pero esta trabaja con versiones mayores a Android 5.0 (API 25).
El ejemplo que desarrollaré aquí, pertenece al código de la app Rescate Animal (Github: https://github.com/eamanu/Rescate-Animal/ ). Lo que haremos será, primero tener una actividad (activity_denuncia.xml) que tendrá incrustada un fragment (fragment_camera2.xml). Ademas de esto tendremos, una clase DenunciaActivity.java que controla a activity_denuncia.xml y una CameraFragment.java que controla a fragment_camera2.xml.
Manifest
–
Lo primero que debemos realizar es agregar los permisos necesarios en el AndroidManifest.xml
Permiso de la cámara¶
Le indicamos a Android que vamos a utilizar la cámara
<pre class="prettyprint"><uses-permission android:name="android.permission.CAMERA" />
Uso de características¶
Le indicamos a Android que vamos a utilizar las características de la cámara
<pre class="prettyprint"><uses-feature android:name="android.hardware.camera" />
Layout
activity_denuncia.xml¶
version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/Denuncia"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:background="#DFE0E2">
android:layout_height="wrap_content"
android:id="@+id/bar"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#000818"
app:popupTheme="@style/AppTheme.PopupOverlay" >
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@drawable/selector"
android:layout_alignParentRight="true"
android:id="@+id/btnSiguiente"
android:onClick="btnNext"
android:gravity="end"
android:textAlignment="textEnd"
android:layout_marginTop="20dp"
android:layout_marginRight="10dp"
android:textSize="20dp"/>
>
>
>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/bar"
android:layout_above="@+id/BtnMap">
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container_pic"
android:background="#000"
tools:context="com.example.android.camera2basic.DenunciaActivity"/>
>
>
>
```
version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/texture"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"/>
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/control"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:background="#fff">
android:id="@+id/BtnCamera"
android:background="@drawable/ic_camera"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"/>
>
>
```
Ahora pasamos a lo más importante. Para acceder a la clase Camera e instanciar la cámara debemos usar este sencillo código:
<div class="wp-synhighlighter-outer" id="wpshdo_15"><div class="wp-synhighlighter-expanded" id="wpshdt_15"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_15"></a><a class="wp-synhighlighter-title" href="#codesyntax_15" id="wpshat_15" onclick="javascript:wpsh_toggleBlock(15)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_15" onclick="javascript:wpsh_code(15)" title="Show code only"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" style="border: 0 none"></img></a> <a href="#codesyntax_15" onclick="javascript:wpsh_print(15)" title="Print code"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" style="border: 0 none"></img></a> <a href="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" style="border: 0 none"></img></a> </td></tr></table></div><div class="wp-synhighlighter-inner" id="wpshdi_15" style="display: block;"><pre class="java" style="font-family:monospace;"><ol><li class="li1"><div class="de1"><span class="kw1">public</span> <span class="kw1">static</span> Camera getCameraInstance<span class="br0">(</span><span class="br0">)</span><span class="br0">{</span></div></li><li class="li1"><div class="de1"> Camera c <span class="sy0">=</span> <span class="kw2">null</span><span class="sy0">;</span></div></li><li class="li1"><div class="de1"> </div></li><li class="li1"><div class="de1"> <span class="kw1">try</span> <span class="br0">{</span></div></li><li class="li2"><div class="de2"> c <span class="sy0">=</span> android.<span class="me1">hardware</span>.<span class="me1">Camera</span>.<span class="me1">open</span><span class="br0">(</span>Camera.<span class="me1">CameraInfo</span>.<span class="me1">CAMERA_FACING_BACK</span><span class="br0">)</span><span class="sy0">;</span> <span class="co1">// attempt to get a Camera instance</span></div></li><li class="li1"><div class="de1"> <span class="br0">}</span></div></li><li class="li1"><div class="de1"> <span class="kw1">catch</span> <span class="br0">(</span><a href="http://www.google.com/search?hl=en&q=allinurl%3Aexception+java.sun.com&btnI=I%27m%20Feeling%20Lucky"><span class="kw3">Exception</span></a> e<span class="br0">)</span><span class="br0">{</span></div></li><li class="li1"><div class="de1"> <span class="co1">// Camera is not available (in use or does not exist)</span></div></li><li class="li1"><div class="de1"> Log.<span class="me1">e</span><span class="br0">(</span>TAG, <span class="st0">"Error en la instanciación de la Cámara"</span><span class="br0">)</span><span class="sy0">;</span></div></li><li class="li2"><div class="de2"> <span class="br0">}</span></div></li><li class="li1"><div class="de1"> <span class="kw1">return</span> c<span class="sy0">;</span> <span class="co1">// returns null if camera is unavailable</span></div></li><li class="li1"><div class="de1"><span class="br0">}</span></div></li></ol></div></div>
Vemos que primero creamos un objecto Camera llamado c, y utilizamos el método Camera.open(). CAMERA_FACING_BACK es un int que nos indica que queremos abrir la cámara trasera (Si queremos abrir la cámara frontal utilizamos CAMERA_FACING_FRONT). Lo ideal sería comprobar la cantidad de cámaras que existen con el método Camera.getNumberOfCameras() para conocer si existe una cámara frontal o no.
Creando una Preview
Otro paso importante, una vez que tengamos la cámara instanciada, es necesario mostrarla en un SurfaceView. Creamos una clase PreviewPic que extiende a ViewGroup e implementa un SurfaceHolder.Callback.
public class PreviewPic extends ViewGroup implements SurfaceHolder.Callback {
private final String TAG = "PreviewPic";
SurfaceView mSurfaceView;
SurfaceHolder mHolder;
Camera mCamera;
Camera.Size mPreviewSize;
List<Camera.Size> mSupportedPreviewSizes;
PreviewPic(Context context, SurfaceView sv, Camera cam) {
super(context);
setCamera(cam);
mSurfaceView = sv;
//addView(mSurfaceView);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = mSurfaceView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
// ...
}
```
Necesitamos setear la cámara para se muestre correctamente en el SurfaceView.
<div class="wp-synhighlighter-outer" id="wpshdo_17"><div class="wp-synhighlighter-expanded" id="wpshdt_17"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_17"></a><a class="wp-synhighlighter-title" href="#codesyntax_17" id="wpshat_17" onclick="javascript:wpsh_toggleBlock(17)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_17" onclick="javascript:wpsh_code(17)" title="Show code only"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" style="border: 0 none"></img></a> <a href="#codesyntax_17" onclick="javascript:wpsh_print(17)" title="Print code"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" style="border: 0 none"></img></a> <a href="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" style="border: 0 none"></img></a> </td></tr></table></div><div class="wp-synhighlighter-inner" id="wpshdi_17" style="display: block;"><pre class="java" style="font-family:monospace;"><span class="kw1">public</span> <span class="kw4">void</span> setCamera <span class="br0">(</span> Camera camera <span class="br0">)</span><span class="br0">{</span>
<span class="kw1">if</span> <span class="br0">(</span>camera <span class="sy0">==</span> <span class="kw2">null</span><span class="br0">)</span><span class="br0">{</span><span class="kw1">return</span><span class="sy0">;</span><span class="br0">}</span>
mCamera <span class="sy0">=</span> camera<span class="sy0">;</span>
mSupportedPreviewSizes <span class="sy0">=</span> mCamera.<span class="me1">getParameters</span><span class="br0">(</span><span class="br0">)</span>.<span class="me1">getSupportedPreviewSizes</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
requestLayout<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="co1">// get Camera params</span>
Camera.<span class="me1">Parameters</span> params <span class="sy0">=</span> mCamera.<span class="me1">getParameters</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
List<span class="sy0"><</span>String<span class="sy0">></span> focusMode <span class="sy0">=</span> params.<span class="me1">getSupportedFocusModes</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span><span class="br0">(</span>focusMode.<span class="me1">contains</span><span class="br0">(</span>Camera.<span class="me1">Parameters</span>.<span class="me1">FOCUS_MODE_AUTO</span><span class="br0">)</span><span class="br0">)</span><span class="br0">{</span>
<span class="co1">// set the focus mode</span>
params.<span class="me1">setFocusMode</span><span class="br0">(</span>Camera.<span class="me1">Parameters</span>.<span class="me1">FOCUS_MODE_AUTO</span><span class="br0">)</span><span class="sy0">;</span>
<span class="co1">//set Camera parameters</span>
mCamera.<span class="me1">setParameters</span><span class="br0">(</span>params<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span></div></div>
Implementamos los métodos del Surface.Callback
<div class="wp-synhighlighter-outer" id="wpshdo_18"><div class="wp-synhighlighter-expanded" id="wpshdt_18"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_18"></a><a class="wp-synhighlighter-title" href="#codesyntax_18" id="wpshat_18" onclick="javascript:wpsh_toggleBlock(18)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_18" onclick="javascript:wpsh_code(18)" title="Show code only"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" style="border: 0 none"></img></a> <a href="#codesyntax_18" onclick="javascript:wpsh_print(18)" title="Print code"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" style="border: 0 none"></img></a> <a href="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" style="border: 0 none"></img></a> </td></tr></table></div><div class="wp-synhighlighter-inner" id="wpshdi_18" style="display: block;"><pre class="java" style="font-family:monospace;">@Override
<span class="kw1">public</span> <span class="kw4">void</span> surfaceCreated<span class="br0">(</span>SurfaceHolder holder<span class="br0">)</span> <span class="br0">{</span>
<span class="kw1">try</span><span class="br0">{</span>
<span class="kw1">if</span> <span class="br0">(</span>mCamera <span class="sy0">!=</span> <span class="kw2">null</span><span class="br0">)</span><span class="br0">{</span>
mCamera.<span class="me1">setPreviewDisplay</span><span class="br0">(</span>holder<span class="br0">)</span><span class="sy0">;</span>
mCamera.<span class="me1">startPreview</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="br0">}</span><span class="kw1">catch</span> <span class="br0">(</span><a href="http://www.google.com/search?hl=en&q=allinurl%3Aioexception+java.sun.com&btnI=I%27m%20Feeling%20Lucky"><span class="kw3">IOException</span></a> e<span class="br0">)</span><span class="br0">{</span>
Log.<span class="me1">e</span><span class="br0">(</span>TAG, <span class="st0">"Exception caused by setPreviewDisplay()"</span>, e<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="br0">}</span>
@Override
<span class="kw1">public</span> <span class="kw4">void</span> surfaceChanged<span class="br0">(</span>SurfaceHolder holder, <span class="kw4">int</span> format, <span class="kw4">int</span> width, <span class="kw4">int</span> height<span class="br0">)</span> <span class="br0">{</span>
<span class="kw1">if</span><span class="br0">(</span>mHolder.<span class="me1">getSurface</span><span class="br0">(</span><span class="br0">)</span> <span class="sy0">==</span> <span class="kw2">null</span><span class="br0">)</span><span class="br0">{</span>
<span class="co1">//doesn't exist</span>
<span class="kw1">return</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">try</span> <span class="br0">{</span>
mCamera.<span class="me1">stopPreview</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span><span class="kw1">catch</span><span class="br0">(</span><a href="http://www.google.com/search?hl=en&q=allinurl%3Aexception+java.sun.com&btnI=I%27m%20Feeling%20Lucky"><span class="kw3">Exception</span></a> e<span class="br0">)</span><span class="br0">{</span>
Log.<span class="me1">e</span><span class="br0">(</span>TAG, e.<span class="me1">getMessage</span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">if</span> <span class="br0">(</span>mCamera <span class="sy0">!=</span> <span class="kw2">null</span><span class="br0">)</span><span class="br0">{</span>
Camera.<span class="me1">Parameters</span> params <span class="sy0">=</span> mCamera.<span class="me1">getParameters</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="co1">// Aqui debemos rotar la imagen o cambiar el tamaño </span>
requestLayout<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
mCamera.<span class="me1">setParameters</span><span class="br0">(</span>params<span class="br0">)</span><span class="sy0">;</span>
mCamera.<span class="me1">startPreview</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="br0">}</span>
@Override
<span class="kw1">public</span> <span class="kw4">void</span> surfaceDestroyed<span class="br0">(</span>SurfaceHolder holder<span class="br0">)</span> <span class="br0">{</span>
<span class="kw1">if</span> <span class="br0">(</span>mCamera <span class="sy0">!=</span> <span class="kw2">null</span><span class="br0">)</span>
mCamera.<span class="me1">stopPreview</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span></div></div>
Cuando esto se implementa y se hace funcionar la imagen aparecerá rotada. Para arreglar esto en el método surfaceChanged(…) donde se encuentra el comentario es necesario aplicar el siguiente código:
<div class="wp-synhighlighter-outer" id="wpshdo_19"><div class="wp-synhighlighter-expanded" id="wpshdt_19"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_19"></a><a class="wp-synhighlighter-title" href="#codesyntax_19" id="wpshat_19" onclick="javascript:wpsh_toggleBlock(19)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_19" onclick="javascript:wpsh_code(19)" title="Show code only"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" style="border: 0 none"></img></a> <a href="#codesyntax_19" onclick="javascript:wpsh_print(19)" title="Print code"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" style="border: 0 none"></img></a> <a href="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img border="0" src="https://eamanu.com/blog/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" style="border: 0 none"></img></a> </td></tr></table></div><div class="wp-synhighlighter-inner" id="wpshdi_19" style="display: block;"><pre class="java" style="font-family:monospace;">Display display <span class="sy0">=</span> <span class="br0">(</span><span class="br0">(</span>WindowManager<span class="br0">)</span>getContext<span class="br0">(</span><span class="br0">)</span>.<span class="me1">getSystemService</span><span class="br0">(</span>WINDOW_SERVICE<span class="br0">)</span><span class="br0">)</span>.<span class="me1">getDefaultDisplay</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span><span class="br0">(</span>display.<span class="me1">getRotation</span><span class="br0">(</span><span class="br0">)</span> <span class="sy0">==</span> Surface.<span class="me1">ROTATION_0</span><span class="br0">)</span>
<span class="br0">{</span>
params.<span class="me1">setPreviewSize</span><span class="br0">(</span>height, width<span class="br0">)</span><span class="sy0">;</span>
mCamera.<span class="me1">setDisplayOrientation</span><span class="br0">(</span>90<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">if</span><span class="br0">(</span>display.<span class="me1">getRotation</span><span class="br0">(</span><span class="br0">)</span> <span class="sy0">==</span> Surface.<span class="me1">ROTATION_90</span><span class="br0">)</span>
<span class="br0">{</span>
params.<span class="me1">setPreviewSize</span><span class="br0">(</span>width, height<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">if</span><span class="br0">(</span>display.<span class="me1">getRotation</span><span class="br0">(</span><span class="br0">)</span> <span class="sy0">==</span> Surface.<span class="me1">ROTATION_180</span><span class="br0">)</span>
<span class="br0">{</span>
params.<span class="me1">setPreviewSize</span><span class="br0">(</span>height, width<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="kw1">if</span><span class="br0">(</span>display.<span class="me1">getRotation</span><span class="br0">(</span><span class="br0">)</span> <span class="sy0">==</span> Surface.<span class="me1">ROTATION_270</span><span class="br0">)</span>
<span class="br0">{</span>
params.<span class="me1">setPreviewSize</span><span class="br0">(</span>width, height<span class="br0">)</span><span class="sy0">;</span>
mCamera.<span class="me1">setDisplayOrientation</span><span class="br0">(</span>180<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span></div></div>
Esto hace que la imagen se rote, y se muestre correctamente.
Conclusiones
En este entrada se mostró de forma muy sencilla las partes importantes que el código debe contener a la hora de desarrollar una APP que incluya una cámara. Para ver el código completo lo pudes hacer desde el github. Y te invito a contribuir en el código.