Сегодня мы научимся обрабатывать показания акселерометра. Поскольку за изменением трех чисел наблюдать как то не весело, сделаем это красиво.
Ориентируемся в пространстве
Поскольку мы решили делать красиво, для начала нам понадобиться View для наглядного отображения вертикали. Изобразим этакую матрешку-неваляшку из двух вложенных друг в друга кругляшков:
package com.WhiteRabbit.Sensors;
...
public class MainView extends View {
private int maxX = 10000;
private int maxY = 10000;
private int sz;
private int x0;
private int y0;
private double alfa = Math.PI / 2;
public MainView(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if ((MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) ||
(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED)) {
setMeasuredDimension(maxX, maxY);
} else {
int szX = MeasureSpec.getSize(widthMeasureSpec);
int szY = MeasureSpec.getSize(heightMeasureSpec);
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
if (szX > maxX) {
szX = maxX;
}
}
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
if (szY > maxY) {
szY = maxY;
}
}
setMeasuredDimension(szX, szY);
x0 = szX / 2;
y0 = szY / 2;
if (szX < szY) {
sz = szX / 3;
} else {
sz = szY / 3;
}
}
}
@Override
protected void onDraw(Canvas c) {
c.drawColor(Color.BLACK);
Paint p = new Paint();
p.setColor(Color.WHITE);
p.setAntiAlias(true);
c.drawCircle(x0, y0, sz, p);
p.setColor(Color.BLACK);
double x1 = x0 - (sz /2) * Math.cos(alfa);
double y1 = y0 - (sz /2) * Math.sin(alfa);
c.drawCircle((float)x1, (float)y1, sz / 2, p);
}
}
В этом коде, в переменной sz сохраняется радиус большого кругляшка, в x0 и y0 его координаты, а в alfa угол (в радианах), в направлении которого будет изображен маленький кругяшок. Добавим созданное View в Activity:
package com.WhiteRabbit.Sensors;
...
public class SensorsActivity extends Activity {
MainView view;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout.LayoutParams containerParams =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0.0F);
LinearLayout root = new LinearLayout(this);
root.setOrientation(LinearLayout.VERTICAL);
root.setBackgroundColor(Color.LTGRAY);
root.setLayoutParams(containerParams);
view = new MainView(this);
root.addView(view);
setContentView(root);
}
}
и, запустив на выполнение, пронаблюдаем результат:
Далее, нам нужен какой-то инструмент, для изменения угла alfa при отображении маленького кружочка. От акселерометра мы будем получать три числовых значения, одно из которых (z) будем игнорировать. Судорожно вспоминаем тригонометрию и понимаем, что alfa = arctg(y/x), а также то, что его аргумент будет претерпевать особенности при x = 0. К счастью, он нас позаботились разработчики Java, определив в Math замечательную функцию atan2:
...
public class MainView extends View {
...
private double eps = 0.1;
...
public void setXY(float x, float y) {
if (Math.sqrt(x*x + y*y) < eps) {
alfa = Math.PI / 2;
} else {
alfa = -Math.atan2(y, x);
}
invalidate();
}
}
Константу eps определяем на случай получения очень уж маленьких значений x и y. После этого, проводим на эмуляторе небольшое тестирование, передавая в setXY различные значения для проверки корректности расчета alfa. Убедившись, что все считается правильно, переходим к самой интересной части, работе с акселерометром.
На самом деле, нам требуется всего лишь получить акселерометр из списка предоставляемых устройством сенсоров, написать обработчики его событий и добавить код подписки/отписки на события акселерометра. Попутно зафиксируем ориентацию экрана, чтобы они не вертелся и не мешал нам эстетически наслаждаться:
package com.WhiteRabbit.Sensors;
public class SensorsActivity extends Activity implements SensorEventListener {
...
SensorManager mSensorManager = null;
Sensor mSensor = null;
@Override
public void onCreate(Bundle savedInstanceState) {
...
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
if (mSensorManager != null) {
List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
if(sensors.size() > 0) {
for (Sensor sensor : sensors) {
switch(sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
if(mSensor == null) mSensor = sensor;
break;
default:
break;
}
}
}
}
}
@Override
public void onAccuracyChanged(Sensor event, int value) {}
@Override
public void onSensorChanged(SensorEvent event) {
if (view == null) return;
switch(event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
view.setXY(event.values[0], event.values[1]);
break;
}
}
@Override
public void onStart() {
super.onStart();
if (mSensor != null) {
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);
}
}
@Override
public void onPause() {
super.onPause();
if (mSensor != null) {
mSensorManager.unregisterListener(this);
}
}
}
Осталось добавить в манифест uses-feature, чтобы никто не запустил наше приложение при отсутствии акселерометра:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.WhiteRabbit.Sensors"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="15" />
<uses-feature android:name="android.hardware.sensor.accelerometer"
android:required="true" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".SensorsActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
и можно экспортировать apk-ку.
Комментариев нет:
Отправить комментарий