Ejemplo: Utilizando el acelerómetro
La aplicación que
realizaremos nos permitirá ver en pantalla los valores del acelerómetro y
mostrar un aviso cuando se detecte cierto movimiento del teléfono.
Es importante notar que el teléfono cuenta con varios sensores
(acelerómetro, orientación, campo magnético, luz, etc.) y de ellos vamos a
utilizar únicamente uno el del acelerómetro. Un acelerómetro es un sensor que mide la aceleración
relativa a la caída libre como marco de referencia.
En este caso,
nuestra interfaz de usuario será muy sencilla por lo que no utilizaremos ningún
código base.
Mostraremos los valores de X, Y y Z del acelerómetro, para ello
necesitamos 3 etiquetas
TextView
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<?xml version="1.0" encoding="utf-8"?> android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Valor
de X" android:id="@+id/txtAccX" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Valor
de Y" android:id="@+id/txtAccY" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Valor
de Z" android:id="@+id/txtAccZ" /> </LinearLayout> |
Agregando código
La clase de la
Activity
principal para
nuestra aplicación implementaráSensorEventListener
para el manejo
de las actualizaciones de los sensores, en nuestro caso específico nos interesa
el acelerómetro.
1
|
public class Main extends Activity
implements SensorEventListener |
Utilizaremos algunas
variables de instancia para el control de los valores de los 3 ejes que mide el
acelerómetro (valor anterior y valor actual) y otras para guardar el timestamp
de la última actualización y la última vez que se detectó movimiento.
1
2
3
|
private long last_update = 0, last_movement = 0; private float prevX = 0, prevY = 0, prevZ = 0; private float curX = 0, curY = 0, curZ = 0; |
Al implementar esta
interface debemos sobrecargar dos métodos:
1
2
3
4
5
|
@Override public void onAccuracyChanged(Sensor sensor, int accuracy) {} @Override public void onSensorChanged(SensorEvent event) {} |
Vamos a trabajar sobre
onSensorChanged
nuestro código
será un synchronized statement (más información en la documentación de Java)
para evitar problemas de concurrencia al estar trabajando con los sensores.
1
2
|
synchronized (this) { } |
A partir del evento
recibido como parámetro vamos a obtener el timestamp de la fecha/hora actual
(se usará más adelante) y los valores para los 3 ejes del acelerómetro (X, Y,
Z).
1
2
3
4
5
|
long current_time = event.timestamp; curX
= event.values[0]; curY
= event.values[1]; curZ = event.values[2]; |
Luego, revisamos si
este código ya se ha ejecutado alguna vez, es decir, si las variables que
guardan los valores anteriores de X, Y y Z tiene valores diferentes de cero.
Esto debería ejecutarse sólo la primera vez.
1
2
3
4
5
6
7
|
if (prevX == 0 &&
prevY == 0 && prevZ == 0) { last_update
= current_time; last_movement
= current_time; prevX
= curX; prevY = curY; prevZ =
curZ; } |
Obtenemos la
diferencia entre la última actualización y el timestamp actual, esto nos
servirá no solo para ver que si existe una diferencia de tiempos en las
mediciones si no también para calcular el movimiento. Para ello, tomamos la
posición actual (con las 3 coordenadas) y la restamos a la posición anterior,
puede ser que el movimiento sea en distintas direcciones por eso nos es útil el
valor absoluto.
1
2
3
4
5
|
long time_difference = current_time - last_update; if (time_difference > 0) { float movement = Math.abs((curX + curY + curZ) - (prevX - prevY - prevZ)) /
time_difference; ... } |
Para decidir en que momento mostramos un aviso
Toast
indicando el
movimiento vamos a usar como valor de frontera de movimiento mínimo 1×10^-6;
este valor es arbitrario mientras mayor sea, se necesitará más movimiento y
mientras menor sea más sensible será el display del aviso.
Manejamos también 2 variables para el control de tiempo, una para
saber la última actualización (last_update) y otra para conocer la última vez
que se registró movimiento
last_movement
y en esta
parte del código actualizamos su valor según sea conveniente.
1
2
3
4
5
6
7
8
|
int limit = 1500; float min_movement = 1E- if (movement > min_movement) { if (current_time - last_movement >= limit) { Toast.makeText(getApplicationContext(),
"Hay movimiento de " + movement, Toast.LENGTH_SHORT).show(); } last_movement
= current_time; } |
Por último
actualizamos los valores de X, Y y Z anteriores para la próxima vez que se
registre cambio en los sensores.
1
2
3
4
|
prevX
= curX; prevY
= curY; prevZ
= curZ; last_update
= current_time; |
También actualizamos
los valores de los 3 ejes del acelerómetro en las etiquetas para visualizarlo
en pantalla.
1
2
3
|
((TextView)
findViewById(R.id.txtAccX)).setText("Acelerómetro X: "
+ curX); ((TextView)
findViewById(R.id.txtAccY)).setText("Acelerómetro Y: "
+ curY); ((TextView)
findViewById(R.id.txtAccZ)).setText("Acelerómetro Z: "
+ curZ); |
El código completo
queda de la siguiente forma:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
@Override public void onSensorChanged(SensorEvent event) { synchronized (this) { long current_time = event.timestamp; curX
= event.values[0]; curY
= event.values[1]; curZ
= event.values[2]; if (prevX == 0 &&
prevY == 0 && prevZ == 0) { last_update
= current_time; last_movement
= current_time; prevX
= curX; prevY
= curY; prevZ
= curZ; } long time_difference = current_time - last_update; if (time_difference > 0) { float movement = Math.abs((curX + curY + curZ) - (prevX - prevY - prevZ)) /
time_difference; int limit = 1500; float min_movement = 1E- if (movement > min_movement) { if (current_time - last_movement >= limit) { Toast.makeText(getApplicationContext(),
"Hay movimiento de " + movement, Toast.LENGTH_SHORT).show(); } last_movement
= current_time; } prevX
= curX; prevY
= curY; prevZ
= curZ; last_update
= current_time; } ((TextView)
findViewById(R.id.txtAccX)).setText("Acelerómetro X: " + curX); ((TextView)
findViewById(R.id.txtAccY)).setText("Acelerómetro Y: " + curY); ((TextView)
findViewById(R.id.txtAccZ)).setText("Acelerómetro Z: " + curZ); } } |
Una vez listo esto, es necesario registrar y anular el registro
del
Listener
para el sensor según
corresponda, esto lo haremos en los métodos onResume
y onStop
de la
actividad (más información del ciclo de vida de las Activities en la documentación oficial)
Para el registro,
obtenemos primero el servicio del sistema de sensores para asignarlo en un
SensorManager y a partir de él obtenemos el acceso al acelerómetro. Al realizar
el registro del acelerómetro es necesario indicar una tasa de lectura de datos,
en nuestro caso vamos a utilizar SensorManager.SENSOR_DELAY_GAME que es la
velocidad mínima para que el acelerómetro pueda usarse en un juego.
1
2
3
4
5
6
7
8
9
|
@Override protected void onResume() { super.onResume(); SensorManager
sm = (SensorManager) getSystemService(SENSOR_SERVICE); List<Sensor>
sensors = sm.getSensorList(Sensor.TYPE_ACCELEROMETER); if (sensors.size() > 0) { sm.registerListener(this,
sensors.get(0), SensorManager.SENSOR_DELAY_GAME); } } |
Para anular el
registro únicamente es necesario indicar que la clase actual (que implementa a
SensorEventListener) ya no está interesada en recibir actualizaciones de
sensores.
1
2
3
4
5
6
|
@Override protected void onStop() { SensorManager
sm = (SensorManager) getSystemService(SENSOR_SERVICE); sm.unregisterListener(this); super.onStop(); } |
Para terminar, en el método
onCreate
de la Activity
vamos a
bloquear la orientación para que al mover el teléfono la pantalla no se mueva y
la interfaz permanezca tal como la vemos.
1
2
3
4
5
6
|
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } |
Conclusión
Hoy hemos aprendido
a trabajar con el acelerómetro, uno de los varios sensores incluídos en los
teléfonos, a través de un Listener, nuestro código de respuesta al evento se
ejecuta cuando hay algún cambio en los sensores y detectamos cuando hay cierto
movimiento.
No hay comentarios:
Publicar un comentario