Mostrar una imagen de la cámara o de la galería

La aplicación que realizaremos nos permitirá mostrar una imagen, podemos obtenerla desde la cámara o desde la galería del teléfono. Queremos que al finalizar se vea así:

Con una imagen cargada se verá de la siguiente forma:

Disposición inicial

Empezaremos descargando el código que debe importarse hacia un proyecto nuevo. Trabajaremos sobre un teléfono con Android y cámara para desarrollar el ejemplo de este artículo. Para el deployment hacia el teléfono es necesario que el sistema operativo lo reconozca y además debe colocarse en el manifest como una propiedad de la etiqueta <application> el valor android:debuggable="true".

La etiqueta debe lucir de la siguiente forma:

<application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">

En la parte de diseño vamos a empezar con un RelativeLayout, el archivo /res/layout/main.xmldebe estar así:

<!--?xml version="1.0" encoding="utf-8"?-->

Diseño

Trabajaremos con otra disposición de elementos para el diseño llamado RelativeLayout y agregaremos otros elementos de interfaz de usuario.

  • RelativeLayout: con este esquema los elementos se colocan en posición relativa a otros elementos o hacia el padre.
  • RadioButton: es un botón de dos estados (marcado y des-marcado) a diferencia del CheckButton este no permite des-marcarlo y cuando se encuentra en grupo solo uno de los botones del grupo puede estar marcado a la vez.
  • RadioGroup: permite agrupar un conjunto de RadioButtons para que solo uno a la vez esté seleccionado.

Nuestro diseño tendrá un botón para adquirir la imagen un RadioGroup que contendrá a 3 botones y un ImageView. Los 3 botones serán seleccionar de donde proviene la imagen ya sea de la cámara (como vista previa o como imagen completa) o de la galería.

El código completo del layout es el siguente:

<!--?xml version="1.0" encoding="utf-8"?-->
	<button>
	</button>

Hemos orientado el botón hacia la derecha y el RadioGroup hacia la izquierda. Luego el ImageView abajo del RadioGroup.

El diseño de interfaces de usuario en ocasiones se vuelve complicado con eclipse por ello utilizaremos la herramienta gratuita DroidDraw que permite exportar e importar archivos XML para luego solo colocarlos en el archivo de diseño en eclipse, además tiene ejecutables para Windows, Linux y OS X.

Agregando código para funcionalidad

Definimos 3 constantes, con dos de ellas vamos a identificar la acción realizada (tomar una fotografía o bien seleccionarla de la galería) y con la otra estableceremos un nombre para el archivo donde escribiremos la fotografía de tamaño completo al tomarla.

private static int TAKE_PICTURE = 1;
private static int SELECT_PICTURE = 2;
private String name = "";

La forma más sencilla de tomar fotografías es utilizar un intent con ACTION_IMAGE_CAPTURE, acción que pertenece al Media Store y luego sobrecargar el método onActivityResult para realizar algo con el archivo recibido de la cámara.

Dentro del método onCreate asignaremos a la variable de instancia name y luego vamos a trabajar sobre la acción al click del botón. Este nombre, inicializado con una llamada a getExternalStorageDirectory() guardará un archivo en la tarjeta SD del teléfono y el archivo se llamará test.jpg cada vez que grabemos una fotografía de tamaño completo se sobre escribe.

name = Environment.getExternalStorageDirectory() + "/test.jpg";
Button btnAction = (Button)findViewById(R.id.btnPic);
     btnAction.setOnClickListener(new OnClickListener() {
    		@Override
    		public void onClick(View v) {
			...
			}
	}
}

Primero obtenemos los botones de imagen completa y de galería para revisar su estatus más adelante. Luego construimos un intent que es necesario si accesamos la cámara con la acción ACTION_IMAGE_CAPTURE, si accesamos la galería con la acción ACTION_PICK. En el caso de la vista previa (thumbnail) no se necesita más que el intent, el código e iniciar la Activity correspondiente. Por eso inicializamos las variables intent y code con los valores necesarios para el caso del thumbnail así de ser el botón seleccionado no validamos nada en un if.

RadioButton rbtnFull = (RadioButton)findViewById(R.id.radbtnFull);
RadioButton rbtnGallery = (RadioButton)findViewById(R.id.radbtnGall);
Intent intent =  new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

Asignamos el código a tomar fotografía, este código junto al intent se utilizarán adelante para iniciar la Activity.

int code = TAKE_PICTURE;

Si el chequeado es el botón de vista previa no necesitamos agregar nada más. Si el chequeado es el botón de imagen completa, además del intent y código agregamos un URI para guardar allí el resultado. Si el chequeado es el de la galería necesitamos un intent y código distintos que asignamos en la consecuencia del if.

if (rbtnFull.isChecked()) {
	Uri output = Uri.fromFile(new File(name));
	intent.putExtra(MediaStore.EXTRA_OUTPUT, output);
} else if (rbtnGallery.isChecked()){
	intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
	code = SELECT_PICTURE;
}

Luego, con todo preparado iniciamos la Activity correspondiente.

startActivityForResult(intent, code);

Además, es necesario sobrecargar la función onActivityResult para indicar que queremos hacer con la imagen recibida (ya sea de la cámara o de la galería) una vez ha sido seleccionada. Es necesario revisar si la imagen viene de la cámara TAKE_PICTURE o de la galería SELECT_PICTURE.

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	if (requestCode == TAKE_PICTURE) {
		…
	} else if (requestCode == SELECT_PICTURE){
		…
	}
}

Si viene de la cámara, verificamos si es una vista previa o una foto completa:

if (data != null) {
   …
} else {
   …
}

En el caso de una vista previa, obtenemos el extra “data” del intent y lo mostramos en el ImageView:

if (data.hasExtra("data")) {
	ImageView iv = (ImageView)findViewById(R.id.imgView);
	iv.setImageBitmap((Bitmap) data.getParcelableExtra("data"));
}

En el caso de una fotografía completa, a partir del nombre del archivo ya definido lo buscamos y creamos el bitmap para el ImageView:

ImageView iv = (ImageView)findViewById(R.id.imgView);
iv.setImageBitmap(BitmapFactory.decodeFile(name));

Si quisiéramos incluir esa imagen en nuestra galería, utilizamos un MediaScannerConnectionClient.

new MediaScannerConnectionClient() {
	private MediaScannerConnection msc = null; {
		msc = new MediaScannerConnection(getApplicationContext(), this); msc.connect();
	}
	public void onMediaScannerConnected() {
		msc.scanFile(fileName, null);
	}
	public void onScanCompleted(String path, Uri uri) {
		msc.disconnect();
	}
};

Si viene de la galería recibimos el URI de la imagen y construimos un Bitmap a partir de un stream de bytes:

Uri selectedImage = data.getData();
InputStream is;
try {
	is = getContentResolver().openInputStream(selectedImage);
	BufferedInputStream bis = new BufferedInputStream(is);
	Bitmap bitmap = BitmapFactory.decodeStream(bis);
	ImageView iv = (ImageView)findViewById(R.id.imgView);
	iv.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {}

Si estamos utilizando la cámara la vista será de la siguiente forma:

Si estamos capturando de la galería será así:

Descarga:

Puedes descargar el código de la aplicación completa y funcional en: Trabajado con imágenes (cámara y galería).

Conclusión

Foro Android

  • RelativeLayout, RadioButton y RadioGroup: aprendimos cómo pueden acomodarse componentes ubicándolos en el diseño con posiciones relativas con respecto a otros componentes y también los controles necesarios para el manejo de radio buttons y su agrupación.
  • Utilización de intents para utilizar la cámara y accesar la galería: se utilizaron intents para realizar acciones específicas, la existencia de intents para acciones comunes predefinidas nos facilitan muchas tareas, en este caso no fue necesario accesar directamente el hardware de la cámara si no pudimos tomar una fotografía de una manera más sencilla. Algo similar sucede con el acceso a la galería de fotos.
  • Utilización de la cámara de fotos: a través de un ejemplo sencillo, aprendimos como utilizar parte del hardware del teléfono. A partir de aquí podemos trabajar los imagénes guardadas y modificarlas o utilizarla para lo que nos interese.