Узнайте, как использовать датчики среды Android для обнаружения информации о среде пользователя, включая температуру окружающей среды, давление, влажность и свет.
Система Android поддерживает ряд датчиков устройств, некоторые из которых реализованы аппаратно, а некоторые — программно. Датчики окружающей среды — это все аппаратные функции, обеспечивающие доступ к информации о температуре, давлении, влажности и освещенности окружающей среды. Эти датчики возвращают значения следующим образом: температура измеряется в градусах Цельсия, атмосферное давление в гПа, миллибар, относительная влажность окружающего воздуха в процентах и окружающий свет в единицах СИ люкс. В этом руководстве мы рассмотрим базовый процесс использования этих четырех основных датчиков окружающей среды. Мы не будем использовать датчик температуры устройства, так как теперь он устарел с Android 4.0.
Существует множество возможных применений этих датчиков, таких как барометры и термометры. Возможно, вы уже сталкивались с такими приложениями в Google Play, но стоит отметить, что они не обязательно реализуют свои функции с помощью датчиков окружающей среды. Например, погодные приложения часто используют данные о местоположении, полученные через Интернет, для определения информации об окружающей среде в зависимости от того, где вы находитесь.
Поскольку эти датчики предоставляются с помощью аппаратного обеспечения пользователя, поддержка зависит от устройства и производителя. На момент написания этой статьи очень немногие смартфоны или планшеты на базе Android поддерживали все датчики окружающей среды, но многие более поздние модели поддерживают одну или несколько из них. Крайне важно выполнить проверку того, имеет ли пользователь конкретные датчики, и избегать использования функций, которые полностью зависят от них. Единственное исключение — если вы гарантируете, что ваше приложение смогут загружать только пользователи с необходимым оборудованием — вы можете сделать это с помощью фильтров для приложения, перечисленных в магазине Google Play.
Шаг 1. Создайте новый проект Android
Создайте новый проект Android в Eclipse и назовите его по вашему выбору. Позвольте Eclipse создать основной Activity, так как это единственный класс, который нам понадобится. Для кода, используемого в этом руководстве, мы нацелены на уровень API Android 14 (Android 4.0 — Ice Cream Sandwich), но вы можете выбрать более свежую версию, если хотите. Вам не нужно вносить изменения в файл манифеста. Ваш основной класс Activity должен иметь следующую исходную структуру с выбранным именем класса:
1
2
3
4
5
6
7
8
9
|
public class EnvironmentCheckerActivity extends Activity {
/** Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
|
Мы собираемся реализовать пару интерфейсов, поэтому расширите строку объявления вашего начального класса следующим образом:
1
|
public class EnvironmentCheckerActivity extends Activity implements OnClickListener, SensorEventListener {
|
Прослушиватель щелчков предназначен для взаимодействия с пользователем, а прослушиватель событий датчиков — для получения данных от датчиков устройства. Eclipse должен был предоставить операторы импорта для классов Activity и Bundle, но вам также необходимо добавить следующее в список:
01
02
03
04
05
06
07
08
09
10
|
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
|
Мы будем добавлять код в класс позже.
Шаг 2: Дизайн взаимодействия с пользователем
Чтобы продемонстрировать базовый процесс использования датчиков окружающей среды, мы собираемся создать простой пользовательский интерфейс. Приложение отобразит список из четырех кнопок, по одной для каждого из датчиков, которые мы будем использовать. Когда пользователь выбирает кнопку, приложение пытается извлечь соответствующую информацию и представить ее в текстовом представлении. Сначала давайте определим некоторые текстовые строки, которые мы будем использовать в интерфейсе. Откройте файл «res / values / strings.xml» и отредактируйте его, чтобы он содержал следующее:
1
2
3
4
5
6
7
8
9
|
<resources>
<string name=»hello»>Choose from the options to check your environment</string>
<string name=»app_name»>Environment Checker</string>
<string name=»ambient_label»>Ambient Temperature</string>
<string name=»light_label»>Light</string>
<string name=»pressure_label»>Pressure</string>
<string name=»humidity_label»>Relative Humidity</string>
<string name=»text_placeholder»>-</string>
</resources>
|
Они представляют заголовок, вводный текст, метки кнопок и заполнитель для текстовых представлений. Мы собираемся использовать несколько доступных ресурсов для дизайна, но вы можете опустить их, если хотите. Чтобы использовать их, в каждой из папок drawables вашего приложения вам нужно будет создать два дополнительных файла, «back.xml» и «btn.xml» (выберите каждую папку drawable по очереди и выберите «File»> «New»> « Файл », затем введите имя файла). Для файла «back.xml» введите следующий код:
1
2
3
4
5
6
7
8
|
<shape xmlns:android=»http://schemas.android.com/apk/res/android»
android:dither=»true»>
<gradient
android:startColor=»#FF000033″
android:endColor=»#FF000033″
android:centerColor=»#FF000066″
android:angle=»180″ />
</shape>
|
Для файла «btn.xml» введите следующее:
01
02
03
04
05
06
07
08
09
10
|
<shape xmlns:android=»http://schemas.android.com/apk/res/android»
android:dither=»true»>
<gradient
android:startColor=»#FF00CC00″
android:endColor=»#FF66CC66″
android:angle=»90″ />
<padding android:left=»10dp» android:top=»10dp»
android:right=»10dp» android:bottom=»10dp» />
<corners android:radius=»2dp» />
</shape>
|
Рисуемое на оборотной стороне — для фона действия, а нарисованное на btn — для фона кнопки. Не стесняйтесь изменять эти рисунки любым удобным для вас способом — обязательно скопируйте их в каждую папку для рисования в вашем приложении.
Теперь откройте файл макета вашего приложения «main.xml» (res / layout / main.xml). Введите вид прокрутки и линейный макет следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
|
<ScrollView xmlns:android=»http://schemas.android.com/apk/res/android»
android:layout_width=»fill_parent»
android:layout_height=»fill_parent»
android:background=»@drawable/back»>
<LinearLayout xmlns:android=»http://schemas.android.com/apk/res/android»
android:layout_width=»fill_parent»
android:layout_height=»wrap_content»
android:padding=»10dp»
android:orientation=»vertical» >
</LinearLayout>
</ScrollView>
|
Внутри линейного макета добавьте введение, а затем кнопку и текстовое представление для каждого из четырех датчиков:
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
<TextView android:layout_width=»fill_parent»
android:layout_height=»wrap_content»
android:paddingBottom=»10dp»
android:textColor=»#FFFFFF00″
android:text=»@string/hello» />
<Button android:id=»@+id/ambient_btn»
android:layout_height=»wrap_content»
android:layout_width=»wrap_content»
android:text=»@string/ambient_label»
android:background=»@drawable/btn» />
<TextView android:id=»@+id/ambient_text»
android:paddingBottom=»20dp»
android:layout_width=»fill_parent»
android:layout_height=»wrap_content»
android:text=»@string/text_placeholder»
android:textColor=»#FFFF66FF»
android:textStyle=»bold» />
<Button android:id=»@+id/light_btn»
android:layout_height=»wrap_content»
android:layout_width=»wrap_content»
android:text=»@string/light_label»
android:background=»@drawable/btn» />
<TextView android:id=»@+id/light_text»
android:paddingBottom=»20dp»
android:layout_width=»fill_parent»
android:layout_height=»wrap_content»
android:text=»@string/text_placeholder»
android:textColor=»#FFFF66FF»
android:textStyle=»bold» />
<Button android:id=»@+id/pressure_btn»
android:layout_height=»wrap_content»
android:layout_width=»wrap_content»
android:text=»@string/pressure_label»
android:background=»@drawable/btn» />
<TextView android:id=»@+id/pressure_text»
android:paddingBottom=»20dp»
android:layout_width=»fill_parent»
android:layout_height=»wrap_content»
android:text=»@string/text_placeholder»
android:textColor=»#FFFF66FF»
android:textStyle=»bold» />
<Button android:id=»@+id/humidity_btn»
android:layout_height=»wrap_content»
android:layout_width=»wrap_content»
android:text=»@string/humidity_label»
android:background=»@drawable/btn» />
<TextView android:id=»@+id/humidity_text»
android:paddingBottom=»20dp»
android:layout_width=»fill_parent»
android:layout_height=»wrap_content»
android:text=»@string/text_placeholder»
android:textColor=»#FFFF66FF»
android:textStyle=»bold» />
|
Каждая пара Button и Text View практически идентична, с атрибутами ID для их идентификации в коде Java. Конечно, вы можете изменить любой из элементов дизайна, если хотите. Макет ссылается на доступные для рисования ресурсы и строки.
Шаг 3. Обработка взаимодействия с пользователем.
Откройте основной класс Activity вашего приложения. В верхней части объявления класса перед методом «onCreate» добавьте следующие переменные экземпляра:
1
2
|
private Button ambientBtn, lightBtn, pressureBtn, humidityBtn;
private TextView ambientValue, lightValue, pressureValue, humidityValue;
|
Они представляют кнопки и текстовые представления, которые мы создали в макете. Мы собираемся использовать массив для отслеживания элементов Text View, поскольку они будут обновляться информацией, когда датчики ее возвращают, поэтому добавьте следующую переменную массива:
1
|
private TextView[] valueFields = new TextView[4];
|
Теперь добавьте эти константы для ссылки на каждый тип датчика:
1
2
3
4
|
private final int AMBIENT=0;
private final int LIGHT=1;
private final int PRESSURE=2;
private final int HUMIDITY=3;
|
Внутри метода «onCreate» после строки, в которой установлено представление содержимого, извлекайте ссылку на каждую кнопку, используя атрибуты ID, которые мы включили в макет, следующим образом:
1
2
3
4
|
ambientBtn = (Button)findViewById(R.id.ambient_btn);
lightBtn = (Button)findViewById(R.id.light_btn);
pressureBtn = (Button)findViewById(R.id.pressure_btn);
humidityBtn = (Button)findViewById(R.id.humidity_btn);
|
Теперь установите каждый из них, чтобы использовать класс Activity в качестве прослушивателя кликов:
1
2
3
4
|
ambientBtn.setOnClickListener(this);
lightBtn.setOnClickListener(this);
pressureBtn.setOnClickListener(this);
humidityBtn.setOnClickListener(this);
|
Когда эти кнопки будут нажаты, будет запущен метод Activity «onClick». Затем извлеките элементы Text View и добавьте ссылку на каждый элемент в объявленном нами массиве, используя константы для указания каждого индекса:
1
2
3
4
5
6
7
8
|
ambientValue = (TextView)findViewById(R.id.ambient_text);
valueFields[AMBIENT]=ambientValue;
lightValue = (TextView)findViewById(R.id.light_text);
valueFields[LIGHT]=lightValue;
pressureValue = (TextView)findViewById(R.id.pressure_text);
valueFields[PRESSURE]=pressureValue;
humidityValue = (TextView)findViewById(R.id.humidity_text);
valueFields[HUMIDITY]=humidityValue;
|
Теперь нам нужно предоставить метод onClick, добавив его в класс после метода onCreate:
1
2
3
4
|
public void onClick(View v)
{
}
|
Внутри метода нам нужно определить, какая кнопка была нажата с помощью условия:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
if (v.getId()==R.id.ambient_btn)
{
//ambient temperature
}
else if(v.getId()==R.id.light_btn)
{
//light
}
else if(v.getId()==R.id.pressure_btn)
{
//pressure
}
else if(v.getId()==R.id.humidity_btn)
{
//humidity
}
|
Внутри каждого из них мы попытаемся получить соответствующие данные среды.
Шаг 4: Настройка датчика среды
В верхней части класса добавьте пару переменных для процесса определения состояния окружающей среды:
1
2
|
private SensorManager senseManage;
private Sensor envSense;
|
Вернувшись к методу onCreate, после существующего кода добавьте следующее, чтобы создать экземпляр класса Sensor Manager :
1
|
senseManage = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
|
Нам нужен Sensor Manager для всех процессов, чувствительных к окружающей среде. Мы используем его для извлечения конкретных датчиков. Внутри метода «onClick» «if» для температуры окружающей среды попытайтесь получить датчик температуры окружающей среды следующим образом:
1
|
envSense = senseManage.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
|
Теперь нам нужно позаботиться о случаях, когда датчик не предоставляется пользовательским устройством, поэтому добавьте следующий тест:
1
2
3
4
|
if(envSense==null)
Toast.makeText(this.getApplicationContext(),
«Sorry — your device doesn’t have an ambient temperature sensor!»,
Toast.LENGTH_SHORT).show();
|
Мы просто выводим сообщение об ошибке. Если датчик присутствует, нам нужно зарегистрироваться, чтобы получить данные, которые он возвращает, поэтому добавьте следующее после этого оператора «if»:
1
2
|
else
senseManage.registerListener(this, envSense, SensorManager.SENSOR_DELAY_NORMAL);
|
Теперь выполните один и тот же процесс для каждой из кнопок в методе «onClick». Для светлой кнопки (в ее выражении «if» внутри «onClick»):
1
2
3
4
5
6
7
|
envSense = senseManage.getDefaultSensor(Sensor.TYPE_LIGHT);
if(envSense==null)
Toast.makeText(this.getApplicationContext(),
«Sorry — your device doesn’t have a light sensor!»,
Toast.LENGTH_SHORT).show();
else
senseManage.registerListener(this, envSense, SensorManager.SENSOR_DELAY_NORMAL);
|
Обратите внимание, что здесь мы запрашиваем датчик «TYPE_LIGHT» и настраиваем сообщение об ошибке в соответствии с типом датчика. Для кнопки давления:
1
2
3
4
5
6
7
|
envSense = senseManage.getDefaultSensor(Sensor.TYPE_PRESSURE);
if(envSense==null)
Toast.makeText(this.getApplicationContext(),
«Sorry — your device doesn’t have a pressure sensor!»,
Toast.LENGTH_SHORT).show();
else
senseManage.registerListener(this, envSense, SensorManager.SENSOR_DELAY_NORMAL);
|
Наконец, в выражении «if» для кнопки влажности:
1
2
3
4
5
6
7
|
envSense = senseManage.getDefaultSensor(Sensor.TYPE_RELATIVE_HUMIDITY);
if(envSense==null)
Toast.makeText(this.getApplicationContext(),
«Sorry — your device doesn’t have a humidity sensor!»,
Toast.LENGTH_SHORT).show();
else
senseManage.registerListener(this, envSense, SensorManager.SENSOR_DELAY_NORMAL);
|
Шаг 5: Получить данные о точности
В дополнение к возврату запрошенных данных об окружающей среде датчик также возвращает данные о точности. Добавьте следующий метод в ваш класс, который требуется при реализации интерфейса прослушивателя событий датчика:
1
2
3
4
|
@Override
public final void onAccuracyChanged(Sensor sensor, int accuracy) {
}
|
Внутри метода начните создавать сообщение о точности:
1
|
String accuracyMsg = «»;
|
Мы будем использовать оператор switch для переданного целочисленного параметра точности:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
switch(accuracy){
case SensorManager.SENSOR_STATUS_ACCURACY_HIGH:
accuracyMsg=»Sensor has high accuracy»;
break;
case SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM:
accuracyMsg=»Sensor has medium accuracy»;
break;
case SensorManager.SENSOR_STATUS_ACCURACY_LOW:
accuracyMsg=»Sensor has low accuracy»;
break;
case SensorManager.SENSOR_STATUS_UNRELIABLE:
accuracyMsg=»Sensor has unreliable accuracy»;
break;
default:
break;
}
|
Мы адаптируем сообщение к уровню точности датчика, используя класс Sensor Manager. Выведите точность при получении следующим образом, но после оператора switch:
1
2
|
Toast accuracyToast = Toast.makeText(this.getApplicationContext(), accuracyMsg, Toast.LENGTH_SHORT);
accuracyToast.show();
|
Внутри метода «onAccuracyChanged» вы также можете определить тип датчика по переданному параметру, если вам нужно.
Шаг 6: Получить данные датчика
Теперь мы можем наконец получить возвращенные данные от датчиков, используя Sensor Event — мы делаем это методом «onSensorChanged», который также необходим для реализации интерфейса:
1
2
3
4
|
@Override
public final void onSensorChanged(SensorEvent event) {
}
|
Событие датчика возвращает свои данные различными способами в зависимости от типа датчика. Для всех четырех типов, которые мы используем, он извлекается одинаково из первого элемента в массиве значений с плавающей запятой. Добавьте следующее внутри метода:
1
|
float sensorValue = event.values[0];
|
Теперь мы собираемся создать текстовую строку, включающую эти данные, и записать ее в соответствующее текстовое представление для просмотра пользователем. Сначала мы создаем переменную Text View и присваиваем ей значение по умолчанию (это значение будет перезаписано — мы включим его, чтобы Eclipse был доволен):
1
|
TextView currValue = ambientValue;
|
Далее мы объявляем строку:
1
|
String envInfo=»»;
|
Содержимое строки зависит от типа датчика, поэтому давайте выясним, какой это датчик:
1
|
int currType=event.sensor.getType();
|
Теперь мы можем использовать оператор switch для этого значения:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
switch(currType){
case Sensor.TYPE_AMBIENT_TEMPERATURE:
envInfo=sensorValue+» degrees Celsius»;
currValue=valueFields[AMBIENT];
break;
case Sensor.TYPE_LIGHT:
envInfo=sensorValue+» SI lux units»;
currValue=valueFields[LIGHT];
break;
case Sensor.TYPE_PRESSURE:
envInfo=sensorValue+» hPa (millibars)»;
currValue=valueFields[PRESSURE];
break;
case Sensor.TYPE_RELATIVE_HUMIDITY:
envInfo=sensorValue+» percent humidity»;
currValue=valueFields[HUMIDITY];
break;
default: break;
}
|
В каждом случае мы строим информативную строку, используя полученное значение датчика и текстовую выдержку, относящуюся к типу. Мы также устанавливаем текстовое представление для соответствующего элемента пользовательского интерфейса, используя массив и константы. После оператора switch выведите информацию:
1
|
currValue.setText(envInfo);
|
Теперь сбросьте переменную датчика и прекратите прослушивать обновления, чтобы избежать ненужного использования батареи:
1
2
|
envSense=null;
senseManage.unregisterListener(this);
|
Наконец, мы не хотим, чтобы приложение использовало ненужные ресурсы, когда оно приостановлено, поэтому добавьте этот метод в класс:
1
2
3
4
5
|
@Override
protected void onPause() {
super.onPause();
senseManage.unregisterListener(this);
}
|
Шаг 7: попробуйте
Это полное демонстрационное приложение! Нет смысла запускать это приложение на эмуляторе Android по умолчанию, поскольку оно не предоставляет датчики среды. Однако вы можете запустить его на реальном устройстве или использовать инструмент «Симулятор датчика» , который позволяет имитировать определенные аспекты среды. Ниже приведен скриншот приложения, работающего на Samsung Galaxy S III сразу после получения данных о освещении и давлении:
Вот для двух других датчиков, которые не поддерживаются:
Вывод
Датчики окружающей среды — захватывающая, но все еще развивающаяся особенность платформы Android. Однако пока еще рано на них ориентироваться. Если вы хотите изучить использование этих датчиков более продвинутыми способами, ознакомьтесь с разделом Руководства разработчика по расчету точки росы и абсолютной влажности на основе относительной влажности и температуры окружающей среды. Кроме этого — постарайтесь быть терпеливым!