The
accelerometer sensor is meant to simulate the acceleration forces like the speed changes that you see while playing games on phones. The values returned by the accelerometer for each axis range between (+/-)0–10. Let's see how to design this app. With three axes in our
co-ordinate system, we will need 6 text fields to display 6 different values for (+/-) X, Y and Z axes.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_gravity="center"
android:background="@android:color/white">
<TextView android:layout_width="match_parent" android:text="@string/x_axis"
android:layout_height="wrap_content" android:gravity="center"
android:textSize="25sp" android:padding="10dip" android:textColor="@android:color/black" />
<LinearLayout android:id="@+id/x_axis"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:background="@android:color/black">
<TextView android:text="X-AXIS (+)ve:" android:layout_width="wrap_content"
android:id="@+id/txtSensorValueLeft" android:textSize="30dip"
android:layout_weight="1" android:layout_height="wrap_content"
android:gravity="left">TextView>
<TextView android:layout_width="wrap_content" android:text="X-AXIS (-)ve:"
android:textSize="30dip" android:layout_weight="1"
android:id="@+id/txtSensorValueRight" android:layout_height="wrap_content"
android:gravity="right" />
LinearLayout>
Here, we have laid out a single TextView to display the axis name, followed by two text views to display the positive and negative values of the axis, repeated for Y and Z axes.
<TextView android:layout_width="match_parent" android:text="@string/y_axis"
android:layout_height="wrap_content" android:gravity="center"
android:textSize="25sp" android:padding="10dip" android:textColor="@android:color/black" />
<LinearLayout android:id="@+id/y_axis"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:orientation="vertical" android:gravity="center"
android:background="@android:color/black">
<TextView android:layout_width="fill_parent" android:text="Y-AXIS (+)ve:"
android:layout_height="wrap_content" android:textSize="30dip"
android:layout_weight="1" android:id="@+id/txtSensorValueUp"
android:gravity="center" />
<TextView android:layout_width="fill_parent" android:text="Y-AXIS (-)ve:"
android:layout_height="wrap_content" android:textSize="30dip"
android:layout_weight="1" android:id="@+id/txtSensorValueDown"
android:gravity="center" />
LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content" android:background="@android:color/black"
android:padding="5dip">
LinearLayout>
<TextView android:layout_width="match_parent" android:text="@string/z_axis"
android:layout_height="wrap_content" android:gravity="center"
android:textSize="25sp" android:padding="10dip" android:textColor="@android:color/black"/>
<LinearLayout android:id="@+id/z_axis"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:orientation="vertical" android:background="@android:color/black">
<TextView android:layout_width="wrap_content" android:text="Z-AXIS (+)ve"
android:layout_height="wrap_content" android:textSize="30dip"
android:layout_weight="1" android:id="@+id/txtSensorZValueUp" />
<TextView android:layout_width="wrap_content" android:text="Z-AXIS (-)ve"
android:layout_height="wrap_content" android:textSize="30dip"
android:layout_weight="1" android:id="@+id/txtSensorZValueDown" />
LinearLayout>
After laying out the UI, we have to actually get access to the sensor and display the values it returns. First, we need to reference the UI widgets in our Java class, so we declare variables for each:
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private TextView mSensorValuesLeft;
private TextView mSensorValuesRight;
private TextView mSensorValuesUp;
private TextView mSensorValuesDown;
private TextView mSensorValuesZUp;
private TextView mSensorValuesZDown;
Next, we need to reference them in our Java class; this can be done with the following code.
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mSensorValuesLeft = (TextView) findViewById(R.id.txtSensorValueLeft);
mSensorValuesRight = (TextView) findViewById(R.id.txtSensorValueRight);
mSensorValuesUp = (TextView) findViewById(R.id.txtSensorValueUp);
mSensorValuesDown = (TextView) findViewById(R.id.txtSensorValueDown);
mSensorValuesZUp = (TextView) findViewById(R.id.txtSensorZValueUp);
mSensorValuesZDown = (TextView) findViewById(R.id.txtSensorZValueDown);
The basic classes that we use to access sensors are: SensorManager, Sensor, SensorListener and SensorEvent. The SensorManager class provides an app the ability to access sensors and interact with them. The Sensor class is the model class for sensors, and provides the basic API to get values from the underlying sensor. The SensorListener class is a listener class (like a button onClick listener). It allows the app to register itself with the listener, to receive notifications of changes on the sensors, with the new values. The SensorEvent class interacts with the sensors, and has some basic methods that access sensors and retrieve data in case of any event or change.
Sensors are provided by the system service, so first we have to claim the sensor service from the system service with the following code:
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Now let's go ahead and use the above to implement our app:
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_UI);
We start by getting hold of the sensor the using getDefaultSensor() method, which returns the default sensor for the type passed as a parameter. We requested Sensor.TYPE_ACCELEROMETER. After getting the sensor, we register our app with the sensor listener, to be able to listen to changes detected by the sensor, with RegisterListener(). SensorManager.SENSOR_DELAY_UI represents the delay in listening to sensor events. This constant specifies listening at speeds normal for a User Interface, because continuous listening to sensors drains battery very quickly. Other constants are SensorManager.SENSOR_DELAY_FASTEST, SensorManager.SENSOR_DELAY_NORMAL, and SensorManager.SENSOR_DELAY_GAME, whose purposes are obvious.
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
/*
* You could have values defined for x, y and z axes and add these
* values to the original values while using it in your app
*/
}
@Override
public void onSensorChanged(SensorEvent event) {
}
There are two methods that need to be overridden: onAccuracyChanged() and onSensorChanged(). The first is used to calibrate the sensor if wanted (you implement it if you want a custom calibration, not the default). OnSensorChanged() is invoked every time a change is detected by the sensor. The SensorEvent parameter will hold the values for all three axes.
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
// X-axis
if (event.values[0] > 0) {
mSensorValuesLeft.setText("X-axis (+)ve: "
+ Integer.toString((int) event.values[0]));
} else if (event.values[0] < 0) {
mSensorValuesRight.setText("X-axis (-)ve:: "
+ Integer.toString(((int) event.values[0]) * -1));
}
float y = event.values[1];
if (y > 0) {
mSensorValuesUp.setText("Y-axis (+)ve: "
+ Integer.toString((int) y));
} else {
mSensorValuesDown.setText("Y-axis (-)ve: "
+ Integer.toString((int) y * -1));
}
float z = event.values[2];
if (z > 0) {
mSensorValuesZUp.setText("Z-axis (+)ve: "
+ Integer.toString((int) z));
} else {
mSensorValuesZDown.setText("Z-axis (-)ve: "
+ Integer.toString((int) z * -1));
}
}
The logic is very simple: the SensorEvent variable is an array holding 3 values corresponding to the X axis (element 0) to Z axis (element 2) respectively. We use simple if-else logic to set the values to their corresponding TextViews, If the value is positive then assign it to the positive value of axis otherwise set the negative value of axis. The negative value is multiplied by (-1) so that it will be displayed as a (+)ve value for (-)ve axis.
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
mSensorManager.unregisterListener(this);
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_UI);
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
mSensorManager.unregisterListener(this);
}
As discussed earlier, we should not be listening to sensors all the time, because it could lead to serious battery drain—so we register in onResume() and un-register in onPause() and onStop(). Now, we can extend this app by showing a list of sensors that when a user clicks one, the respective sensor demo app is opened. To accomplish the above task, I will create another activity, and show a list of sensors supported by the device.
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
ArrayList mSensorNames = new ArrayList();
for (Sensor sensor : mSensorList) {
mSensorNames.add(sensor.getName());
}
adapter = new ArrayAdapter(MainListActivity.this,
android.R.layout.simple_list_item_1, mSensorNames);
setListAdapter(adapter);
Here, we get the instance of the Sensor service, and then get a list of available sensors using the SensorManager method getSensorList(). We then set up an adapter for the list, and display it to the user.