Creando aplicaciones con sensores de entorno
Spanish (Español) translation by CYC (you can also view the original English article)
Aprende a usar los sensores de entorno Android para detectar información sobre el entorno del usuario, incluida la temperatura ambiente, la presión, la humedad y la luz.
El sistema Android admite una variedad de Sensores de dispositivo, algunos implementados en hardware y otros en software. Los sensores de entorno son todas las características de hardware, que proporcionan acceso a la información sobre la temperatura ambiente, la presión, la humedad y la luz. Estos sensores devuelven los valores de la siguiente manera: la temperatura se mide en grados Celsius, la presión atmosférica en hPa milibares, la humedad relativa del aire ambiente como un valor porcentual y la luz ambiental en las unidades SI lux. En este tutorial, vamos a ejecutar el proceso básico para usar estos cuatro sensores de entorno principales. No usaremos el sensor de temperatura del dispositivo, ya que ahora está desaprobado a partir de Android 4.0.
Hay muchas aplicaciones posibles para estos sensores, como barómetros y termómetros. Es posible que ya hayas encontrado estas aplicaciones en Google Play, pero vale la pena señalar que no necesariamente están implementando sus funciones usando los sensores de entorno. Por ejemplo, las aplicaciones meteorológicas a menudo usan datos de ubicación obtenidos a través de la Web para determinar la información del entorno en función de dónde se encuentre.
Dado que estos sensores se proporcionan a través del hardware de los usuarios, el soporte varía según los dispositivos y los fabricantes. En el momento de la redacción, muy pocos teléfonos inteligentes o tabletas con Android admiten todos los sensores de entorno, pero muchos de los modelos más recientes admiten uno o más de ellos. Es vital llevar a cabo verificaciones sobre si el usuario tiene sensores particulares y evitar el uso de una funcionalidad que depende totalmente de ellos. La única excepción a esto es si se asegura de que solo los usuarios con el hardware requerido puedan descargar tu aplicación; puedes hacerlo utilizando los filtros de una aplicación como se indica en la tienda de Google Play.
Paso 1: Crea un nuevo proyecto de Android
Crea un nuevo proyecto Android en Eclipse y asígnale un nombre de tu elección. Permite que Eclipse cree una clase principal como Activity, ya que esta es la única clase que necesitaremos. Para el código utilizado en este tutorial, apuntamos a Android API nivel 14 (Android 4.0 - Ice Cream Sandwich), pero puedes orientar una versión más reciente si lo deseas. No necesitas realizar ninguna modificación en el archivo Manifest. Tu clase principal Activity debe tener la siguiente estructura inicial, con tu nombre de clase elegido:
1 |
public class EnvironmentCheckerActivity extends Activity { |
2 |
|
3 |
/** Called when the activity is first created. */
|
4 |
@Override
|
5 |
public void onCreate(Bundle savedInstanceState) { |
6 |
super.onCreate(savedInstanceState); |
7 |
setContentView(R.layout.main); |
8 |
}
|
9 |
}
|
Vamos a implementar un par de interfaces, de modo que amplía tu línea de declaración de clase de apertura de la siguiente manera:
1 |
public class EnvironmentCheckerActivity extends Activity implements OnClickListener, SensorEventListener { |
El evento click es para la interacción del usuario y el Sensor Event Listener es para recibir datos de los sensores del dispositivo. Eclipse debería haber proporcionado instrucciones de importación para las clases Activity y Bundle, pero también debes agregar lo siguiente a la lista:
1 |
import android.content.Context; |
2 |
import android.hardware.Sensor; |
3 |
import android.hardware.SensorEvent; |
4 |
import android.hardware.SensorEventListener; |
5 |
import android.hardware.SensorManager; |
6 |
import android.view.View; |
7 |
import android.view.View.OnClickListener; |
8 |
import android.widget.Button; |
9 |
import android.widget.TextView; |
10 |
import android.widget.Toast; |
Vamos a agregar código a la clase más tarde.
Paso 2: Diseña la interacción del usuario
Para demostrar el proceso básico para usar los sensores de entorno, vamos a construir una interfaz de usuario simple. La aplicación mostrará una lista de cuatro botones, uno para cada uno de los sensores que usaremos. Cuando el usuario selecciona un botón, la aplicación intentará recuperar la información adecuada y presentarla en un Text View. Primero, definamos algunas cadenas de texto que usaremos dentro de la interfaz. Abre tu archivo "res/values /strings.xml" y edítalo para que contenga lo siguiente:
1 |
<resources>
|
2 |
<string name="hello">Choose from the options to check your environment</string> |
3 |
<string name="app_name">Environment Checker</string> |
4 |
<string name="ambient_label">Ambient Temperature</string> |
5 |
<string name="light_label">Light</string> |
6 |
<string name="pressure_label">Pressure</string> |
7 |
<string name="humidity_label">Relative Humidity</string> |
8 |
<string name="text_placeholder">-</string> |
9 |
</resources>
|
Representan el título, el texto introductorio, las etiquetas de los botones y un marcador de posición para los Text View. Vamos a usar un par de recursos dibujables para el diseño, pero puede omitirlos si lo desea. Para usarlos, en cada una de las carpetas drawables de tu aplicación, deberás crear dos archivos adicionales, "back.xml" y "btn.xml" (selecciona cada carpeta dibujable y selecciona "Archivo" > "Nuevo" > " Archivo ", luego ingresa el nombre del archivo). Para el archivo "back.xml", ingresa el siguiente código:
1 |
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
2 |
android:dither="true"> |
3 |
<gradient
|
4 |
android:startColor="#FF000033" |
5 |
android:endColor="#FF000033" |
6 |
android:centerColor="#FF000066" |
7 |
android:angle="180" /> |
8 |
</shape>
|
Para el archivo "btn.xml", ingresa lo siguiente:
1 |
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
2 |
android:dither="true"> |
3 |
<gradient
|
4 |
android:startColor="#FF00CC00" |
5 |
android:endColor="#FF66CC66" |
6 |
android:angle="90" /> |
7 |
<padding android:left="10dp" android:top="10dp" |
8 |
android:right="10dp" android:bottom="10dp" /> |
9 |
<corners android:radius="2dp" /> |
10 |
</shape>
|
El fondo drawable es para el fondo de Activity y el drawable "btn" es para los fondos de los botones. Siéntete libre de modificar estos diseños de la forma que desees; asegúrate de copiarlos en cada carpeta drawable de tu aplicación.
Ahora abre el archivo de diseño "main.xml" de tu aplicación (res/layout/main.xml). Ingresa una vista de desplazamiento y diseño lineal de la siguiente manera:
1 |
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" |
2 |
android:layout_width="fill_parent" |
3 |
android:layout_height="fill_parent" |
4 |
android:background="@drawable/back"> |
5 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
6 |
android:layout_width="fill_parent" |
7 |
android:layout_height="wrap_content" |
8 |
android:padding="10dp" |
9 |
android:orientation="vertical" > |
10 |
|
11 |
</LinearLayout>
|
12 |
</ScrollView>
|
Dentro del diseño lineal, agrega la introducción, luego un botón y un Text View para cada uno de los cuatro sensores:
1 |
<TextView android:layout_width="fill_parent" |
2 |
android:layout_height="wrap_content" |
3 |
android:paddingBottom="10dp" |
4 |
android:textColor="#FFFFFF00" |
5 |
android:text="@string/hello" /> |
6 |
|
7 |
<Button android:id="@+id/ambient_btn" |
8 |
android:layout_height="wrap_content" |
9 |
android:layout_width="wrap_content" |
10 |
android:text="@string/ambient_label" |
11 |
android:background="@drawable/btn" /> |
12 |
<TextView android:id="@+id/ambient_text" |
13 |
android:paddingBottom="20dp" |
14 |
android:layout_width="fill_parent" |
15 |
android:layout_height="wrap_content" |
16 |
android:text="@string/text_placeholder" |
17 |
android:textColor="#FFFF66FF" |
18 |
android:textStyle="bold" /> |
19 |
|
20 |
<Button android:id="@+id/light_btn" |
21 |
android:layout_height="wrap_content" |
22 |
android:layout_width="wrap_content" |
23 |
android:text="@string/light_label" |
24 |
android:background="@drawable/btn" /> |
25 |
<TextView android:id="@+id/light_text" |
26 |
android:paddingBottom="20dp" |
27 |
android:layout_width="fill_parent" |
28 |
android:layout_height="wrap_content" |
29 |
android:text="@string/text_placeholder" |
30 |
android:textColor="#FFFF66FF" |
31 |
android:textStyle="bold" /> |
32 |
|
33 |
<Button android:id="@+id/pressure_btn" |
34 |
android:layout_height="wrap_content" |
35 |
android:layout_width="wrap_content" |
36 |
android:text="@string/pressure_label" |
37 |
android:background="@drawable/btn" /> |
38 |
<TextView android:id="@+id/pressure_text" |
39 |
android:paddingBottom="20dp" |
40 |
android:layout_width="fill_parent" |
41 |
android:layout_height="wrap_content" |
42 |
android:text="@string/text_placeholder" |
43 |
android:textColor="#FFFF66FF" |
44 |
android:textStyle="bold" /> |
45 |
|
46 |
<Button android:id="@+id/humidity_btn" |
47 |
android:layout_height="wrap_content" |
48 |
android:layout_width="wrap_content" |
49 |
android:text="@string/humidity_label" |
50 |
android:background="@drawable/btn" /> |
51 |
<TextView android:id="@+id/humidity_text" |
52 |
android:paddingBottom="20dp" |
53 |
android:layout_width="fill_parent" |
54 |
android:layout_height="wrap_content" |
55 |
android:text="@string/text_placeholder" |
56 |
android:textColor="#FFFF66FF" |
57 |
android:textStyle="bold" /> |
Cada par de botones y Text View es virtualmente idéntico, con atributos de identificación para identificarlos en el código de Java. Por supuesto, puedes modificar cualquiera de los elementos de diseño si lo deseas. El diseño se refiere a los recursos dibujables y cadenas.
Paso 3: Manejar la interacción del usuario
Abre la clase principal Activity de tu aplicación. En la parte superior de la declaración de clase, antes del método "onCreate", agrega las siguientes variables de instancia:
1 |
private Button ambientBtn, lightBtn, pressureBtn, humidityBtn; |
2 |
private TextView ambientValue, lightValue, pressureValue, humidityValue; |
Estos representan los botones y las vistas de texto que creamos en el diseño. Vamos a usar una matriz para realizar un seguimiento de los elementos Text View, ya que estos se actualizarán con la información cuando los sensores la devuelvan, así que agrega la siguiente variable de matriz a continuación:
1 |
private TextView[] valueFields = new TextView[4]; |
Ahora agrega estas constantes para referirte a cada tipo de sensor:
1 |
private final int AMBIENT=0; |
2 |
private final int LIGHT=1; |
3 |
private final int PRESSURE=2; |
4 |
private final int HUMIDITY=3; |
Dentro del método "onCreate", después de la línea en la que está configurada la vista de contenido, recupera una referencia a cada botón utilizando los atributos de ID que incluimos en el diseño, de la siguiente manera:
1 |
ambientBtn = (Button)findViewById(R.id.ambient_btn); |
2 |
lightBtn = (Button)findViewById(R.id.light_btn); |
3 |
pressureBtn = (Button)findViewById(R.id.pressure_btn); |
4 |
humidityBtn = (Button)findViewById(R.id.humidity_btn); |
Ahora configura cada uno de estos para usar la clase Activity como click listener:
1 |
ambientBtn.setOnClickListener(this); |
2 |
lightBtn.setOnClickListener(this); |
3 |
pressureBtn.setOnClickListener(this); |
4 |
humidityBtn.setOnClickListener(this); |
Cuando se presionan estos botones, se ejecutará el método Activity "onClick". Luego, recupera los elementos Text View y agrega una referencia a cada uno en la matriz que declaramos, usando las constantes para especificar cada índice:
1 |
ambientValue = (TextView)findViewById(R.id.ambient_text); |
2 |
valueFields[AMBIENT]=ambientValue; |
3 |
lightValue = (TextView)findViewById(R.id.light_text); |
4 |
valueFields[LIGHT]=lightValue; |
5 |
pressureValue = (TextView)findViewById(R.id.pressure_text); |
6 |
valueFields[PRESSURE]=pressureValue; |
7 |
humidityValue = (TextView)findViewById(R.id.humidity_text); |
8 |
valueFields[HUMIDITY]=humidityValue; |
Ahora tenemos que proporcionar el método "onClick" y agregarlo a la clase después del método "onCreate":
1 |
public void onClick(View v) |
2 |
{
|
3 |
|
4 |
}
|
Dentro del método, necesitamos determinar en qué botón se hizo clic usando un condicional:
1 |
if (v.getId()==R.id.ambient_btn) |
2 |
{
|
3 |
//ambient temperature
|
4 |
|
5 |
}
|
6 |
else if(v.getId()==R.id.light_btn) |
7 |
{
|
8 |
//light
|
9 |
|
10 |
}
|
11 |
else if(v.getId()==R.id.pressure_btn) |
12 |
{
|
13 |
//pressure
|
14 |
|
15 |
}
|
16 |
else if(v.getId()==R.id.humidity_btn) |
17 |
{
|
18 |
//humidity
|
19 |
|
20 |
}
|
Dentro de cada uno de estos, intentaremos recuperar los relevantes del entorno.
Paso 4: Configurar el sensor del entorno
En la parte superior de la clase, agrega un par de variables para el proceso de detección del entorno:
1 |
private SensorManager senseManage; |
2 |
private Sensor envSense; |
De vuelta en el método "onCreate", después del código existente, agrega lo siguiente para crear una instancia de la clase Sensor Manager:
1 |
senseManage = (SensorManager) getSystemService(Context.SENSOR_SERVICE); |
Necesitamos el Administrador de sensores para todos los procesos de detección del entorno. Lo usamos para recuperar sensores específicos. Dentro de la declaración "if" del método "onClick" para la temperatura ambiente, intenta recuperar el sensor de temperatura ambiente de la siguiente manera:
1 |
envSense = senseManage.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE); |
Ahora tenemos que ocuparnos de los casos en los que el sensor no es provisto por el dispositivo del usuario, así que agrega la siguiente prueba:
1 |
if(envSense==null) |
2 |
Toast.makeText(this.getApplicationContext(), |
3 |
"Sorry - your device doesn't have an ambient temperature sensor!", |
4 |
Toast.LENGTH_SHORT).show(); |
Simplemente emitimos un mensaje de error. Si el sensor está presente, necesitamos registrarnos para recibir los datos que devuelve, así que agrega lo siguiente después de esta declaración "if":
1 |
else
|
2 |
senseManage.registerListener(this, envSense, SensorManager.SENSOR_DELAY_NORMAL); |
Ahora realiza el mismo proceso para cada uno de los botones en el método "onClick". Para el botón de la luz (en tu declaración "if" dentro de "onClick"):
1 |
envSense = senseManage.getDefaultSensor(Sensor.TYPE_LIGHT); |
2 |
if(envSense==null) |
3 |
Toast.makeText(this.getApplicationContext(), |
4 |
"Sorry - your device doesn't have a light sensor!", |
5 |
Toast.LENGTH_SHORT).show(); |
6 |
else
|
7 |
senseManage.registerListener(this, envSense, SensorManager.SENSOR_DELAY_NORMAL); |
Ten en cuenta que aquí estamos solicitando el sensor "TYPE_LIGHT" y estamos adaptando el mensaje de error al tipo de sensor. Para el botón de presión:
1 |
envSense = senseManage.getDefaultSensor(Sensor.TYPE_PRESSURE); |
2 |
if(envSense==null) |
3 |
Toast.makeText(this.getApplicationContext(), |
4 |
"Sorry - your device doesn't have a pressure sensor!", |
5 |
Toast.LENGTH_SHORT).show(); |
6 |
else
|
7 |
senseManage.registerListener(this, envSense, SensorManager.SENSOR_DELAY_NORMAL); |
Finalmente, en la declaración "if" para el botón de humedad:
1 |
envSense = senseManage.getDefaultSensor(Sensor.TYPE_RELATIVE_HUMIDITY); |
2 |
if(envSense==null) |
3 |
Toast.makeText(this.getApplicationContext(), |
4 |
"Sorry - your device doesn't have a humidity sensor!", |
5 |
Toast.LENGTH_SHORT).show(); |
6 |
else
|
7 |
senseManage.registerListener(this, envSense, SensorManager.SENSOR_DELAY_NORMAL); |
Paso 5: Recuperar datos precisos
Además de devolver los datos del entorno solicitado, el sensor también devolverá los datos de precisión. Agrega el siguiente método a tu clase, que se requiere al implementar la interfaz para recibir los eventos del sensor:
1 |
@Override
|
2 |
public final void onAccuracyChanged(Sensor sensor, int accuracy) { |
3 |
|
4 |
}
|
Dentro del método, comienza a generar un mensaje con respecto a la precisión:
1 |
String accuracyMsg = ""; |
Utilizaremos una declaración de interruptor en el parámetro entero de precisión pasado:
1 |
switch(accuracy){ |
2 |
case SensorManager.SENSOR_STATUS_ACCURACY_HIGH: |
3 |
accuracyMsg="Sensor has high accuracy"; |
4 |
break; |
5 |
case SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM: |
6 |
accuracyMsg="Sensor has medium accuracy"; |
7 |
break; |
8 |
case SensorManager.SENSOR_STATUS_ACCURACY_LOW: |
9 |
accuracyMsg="Sensor has low accuracy"; |
10 |
break; |
11 |
case SensorManager.SENSOR_STATUS_UNRELIABLE: |
12 |
accuracyMsg="Sensor has unreliable accuracy"; |
13 |
break; |
14 |
default: |
15 |
break; |
16 |
}
|
Adaptamos el mensaje al nivel de precisión del sensor, usando la clase Sensor Manager. Imprime la precisión cuando se recibe de la siguiente manera, pero después de la declaración de cambio:
1 |
Toast accuracyToast = Toast.makeText(this.getApplicationContext(), accuracyMsg, Toast.LENGTH_SHORT); |
2 |
accuracyToast.show(); |
Dentro del método "onAccuracyChanged" también puedes determinar el tipo de sensor a partir del parámetro pasado, si es necesario.
Paso 6: Recupera los datos del sensor
Ahora podemos finalmente recuperar los datos devueltos de los sensores utilizando un Evento de sensor: lo hacemos en el método "onSensorChanged", que también es necesario para implementar la interfaz:
1 |
@Override
|
2 |
public final void onSensorChanged(SensorEvent event) { |
3 |
|
4 |
}
|
El evento del sensor devuelve sus datos de diferentes maneras dependiendo del tipo de sensor. Para los cuatro tipos que estamos usando, se recupera de la misma manera, desde el primer elemento en una matriz de valores de coma flotante. Agrega lo siguiente dentro del método:
1 |
float sensorValue = event.values[0]; |
Ahora vamos a construir una cadena de texto que incluya estos datos, y la escribiremos en el Text View relevante para que el usuario la vea. Primero, creamos una variable de vista de texto y le damos un valor predeterminado (este valor se sobrescribirá; lo incluimos para mantener feliz a Eclipse):
1 |
TextView currValue = ambientValue; |
A continuación, declaramos la cadena:
1 |
String envInfo=""; |
El contenido del String dependerá del tipo de sensor, así que averigüemos cuál es:
1 |
int currType=event.sensor.getType(); |
Ahora podemos usar una instrucción switch en este valor:
1 |
switch(currType){ |
2 |
case Sensor.TYPE_AMBIENT_TEMPERATURE: |
3 |
envInfo=sensorValue+" degrees Celsius"; |
4 |
currValue=valueFields[AMBIENT]; |
5 |
break; |
6 |
case Sensor.TYPE_LIGHT: |
7 |
envInfo=sensorValue+" SI lux units"; |
8 |
currValue=valueFields[LIGHT]; |
9 |
break; |
10 |
case Sensor.TYPE_PRESSURE: |
11 |
envInfo=sensorValue+" hPa (millibars)"; |
12 |
currValue=valueFields[PRESSURE]; |
13 |
break; |
14 |
case Sensor.TYPE_RELATIVE_HUMIDITY: |
15 |
envInfo=sensorValue+" percent humidity"; |
16 |
currValue=valueFields[HUMIDITY]; |
17 |
break; |
18 |
default: break; |
19 |
}
|
En cada caso, construimos la cadena informativa utilizando el valor del sensor recuperado y un fragmento de texto relevante para el tipo. También configuramos el Text View para el elemento de interfaz de usuario relevante utilizando la matriz y las constantes. Después de la instrucción switch, genera la información:
1 |
currValue.setText(envInfo); |
Ahora reinicia la variable del sensor y deja de recibir las actualizaciones para evitar el uso innecesario de la batería:
1 |
envSense=null; |
2 |
senseManage.unregisterListener(this); |
Finalmente, no queremos que la aplicación utilice recursos innecesarios cuando está en pausa, así que agrega este método a la clase:
1 |
@Override
|
2 |
protected void onPause() { |
3 |
super.onPause(); |
4 |
senseManage.unregisterListener(this); |
5 |
}
|
Paso 7: Pruébalo
¡Esa es la aplicación de demostración completa! No tiene sentido ejecutar esta aplicación en el emulador de Android predeterminado, ya que no proporciona sensores de entorno. Sin embargo, puedes ejecutarlo en un dispositivo real o usar la herramienta Sensor Simulator, que te permite simular ciertos aspectos del entorno. La siguiente es una captura de pantalla de la aplicación que se ejecuta en el Samsung Galaxy S III justo después de recuperar los datos de luz y presión:

Aquí está para los otros dos sensores, que no son compatibles:

Conclusión
Los sensores de entorno son una característica emocionante pero aún en desarrollo de la plataforma Android. Sin embargo, es un poco temprano para enfocarte en ellos. Si deseas explorar el uso de estos sensores de formas más avanzadas, consulta la sección de la Guía del desarrollador para calcular el punto de rocío y los niveles de humedad absoluta en función de la humedad relativa y la temperatura ambiente. Aparte de eso, ¡trata de ser paciente!



