В этом посте мы хотим описать, как создать простое приложение для Android. Это хороший пример, потому что мы можем изучить Android API для Android Camera. Мы будем использовать Android Studio в качестве IDE для разработки приложения. Это приложение будет не только использовать вспышку света, но мы также можем реализовать стробоскопический свет, где частота может быть изменена. Мы можем узнать, как использовать некоторые базовые компоненты пользовательского интерфейса, такие как ToggleButton и SeekBar . Мы хотим получить приложение, которое выглядит так:
Макет приложения Android
Первое, что нам нужно сделать, это определить макет приложения. Если вы заметили, что при использовании Android Studio он создает в каталоге макета файл с именем фрагмент_основной.xml. Мы должны отредактировать его и реализовать здесь наш макет. Вначале для простоты мы можем предположить, что у нас есть только кнопка ToggleButton для включения и выключения вспышки:
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
|
android:layout_width = "match_parent" android:layout_height = "match_parent" android:paddingLeft = "@dimen/activity_horizontal_margin" android:paddingRight = "@dimen/activity_horizontal_margin" android:paddingTop = "@dimen/activity_vertical_margin" android:paddingBottom = "@dimen/activity_vertical_margin" tools:context = "com.survivingwithandroid.tourch.MainActivity$PlaceholderFragment" > < TextView android:text = "@string/app_name" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_centerHorizontal = "true" android:id = "@+id/textView" /> < TextView android:text = "@string/torchon" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:id = "@+id/textView" android:layout_centerHorizontal = "true" android:layout_marginTop = "43dp" /> < ToggleButton android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:text = "torchonoff" android:id = "@+id/toggleButton" android:layout_below = "@+id/textView" android:layout_centerHorizontal = "true" android:layout_marginTop = "15dp" /> ... </ RelativeLayout > |
В предварительном просмотре IDE мы имеем в результате:
Управление вспышкой камеры Включение и выключение
Теперь мы должны обработать событие ToggleButton. Посмотрев исходный код, сгенерированный нашей IDE, у нас есть класс MainActivity.java, и там есть еще один внутренний класс, используемый для управления фрагментом с именем PlaceHolderFragment. Поскольку мы помним фрагмент как сложный жизненный цикл, который отражается в некоторых методах обратного вызова, вызываемых системой, когда фрагмент перемещается в различные состояния в своем жизненном цикле. Мы можем использовать onActivityCreated, чтобы получить ссылку на камеру смартфона и получить ее параметры, поэтому имеем:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
@Override public void onActivityCreated(Bundle savedInstanceState) { super .onActivityCreated(savedInstanceState); try { Log.d( "TORCH" , "Check cam" ); // Get CAM reference cam = Camera.open(); camParams = cam.getParameters(); cam.startPreview(); hasCam = true ; Log.d( "TORCH" , "HAS CAM [" +hasCam+ "]" ); } catch (Throwable t) { t.printStackTrace(); } } |
В строке 7 мы открываем соединение с камерой, а в строке 8 мы получаем параметры по умолчанию, позже мы будем использовать их для включения и выключения вспышки. В конце в строке 9 мы вызываем startPreview (), чтобы начать захват кадров предварительного просмотра.
Теперь мы готовы обработать событие ToggleButton и изменить свет вспышки в соответствии с запущенным событием. Мы можем сделать это в
Метод onCreateView , который вызывается, когда ОС дает нашему фрагменту возможность установить свой макет:
01
02
03
04
05
06
07
08
09
10
|
View rootView = inflater.inflate(R.layout.fragment_main, container, false ); // Let's get the reference to the toggle ToggleButton tBtn = (ToggleButton) rootView.findViewById(R.id.toggleButton); tBtn.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Log.d( "TORCH" , "IS CHECKED [" +isChecked+ "]" ); turnOnOff(isChecked); } }); |
В строке 1 мы просто раздуваем макет и ищем ToggleButton, используя его идентификатор. Затем мы обрабатываем событие, когда пользователь меняет свой статус, и вызываем метод turnOnOff:
01
02
03
04
05
06
07
08
09
10
11
|
private void turnOnOff( boolean on) { if (on) camParams.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); if (!on) camParams.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); cam.setParameters(camParams); cam.startPreview(); } |
В строке 3 мы используем параметры камеры для включения и выключения вспышки с помощью Camera.Parameters.FLASH_MODE_TORCH
и Camera.Parameters.FLASH_MODE_OFF.
Android Manifest.xml
Чтобы наше приложение могло использовать фонарик, мы должны изменить Android Manifest.xml, чтобы мы могли объявить, что хотим использовать фонарик: мы должны добавить:
1
|
< uses-permission android:name = "android.permission.CAMERA" /> |
Следующим шагом является фильтрация устройства, которое может установить наше приложение в Google Play. Мы хотим, чтобы приложение могли устанавливать только устройства со вспышкой, поэтому мы должны добавить эту строку в Manifest.xml:
1
|
< uses-feature android:name = "android.hardware.camera" /> |
Реализация стробоскопического света
Последний шаг — реализация стробоскопического освещения. Мы хотим, чтобы пользователь мог выбрать частоту освещения, чтобы мы использовали SeekBar. В макет фрагмента добавим:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
< TextView android:text = "@string/freq" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:id = "@+id/textView1" android:layout_centerHorizontal = "true" android:layout_marginTop = "43dp" android:layout_below = "@id/toggleButton" /> < SeekBar android:layout_width = "match_parent" android:layout_height = "wrap_content" android:id = "@+id/seekBar" android:layout_below = "@+id/textView1" android:layout_centerHorizontal = "true" android:layout_marginTop = "25dp" android:layout_marginLeft = "25dp" android:layout_marginRight = "25dp" android:max = "100" /> |
и обрабатывать, даже когда пользователь перемещает уровень прогресса. Как мы делали раньше для ToggleButton, мы реализуем прослушиватель событий в методе onCreateView:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
// Seekbar SeekBar skBar = (SeekBar) rootView.findViewById(R.id.seekBar); skBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { freq = progress; } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); |
где в строке 5 мы получаем текущий уровень. Мы должны изменить метод turnOnOff, потому что, если мы хотим включить свет, мы должны запустить поток, который включает и выключает свет:
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
|
private void turnOnOff(boolean on) { if (on) { if (freq != 0) { sr = new StroboRunner(); sr.freq = freq; t = new Thread(sr); t.start(); return ; } else camParams.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); } if (!on) { if (t != null) { sr.stopRunning = true; t = null; return ; } else camParams.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); } cam.setParameters(camParams); cam.startPreview(); } |
Когда пользователь включает ToggleButton и частота не равна нулю, мы запускаем новый поток, который просто включает и выключает свет:
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
|
private class StroboRunner implements Runnable { int freq; boolean stopRunning = false; @Override public void run() { Camera.Parameters paramsOn = PlaceholderFragment.this.cam.getParameters(); Camera.Parameters paramsOff = PlaceholderFragment.this.camParams; paramsOn.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); paramsOff.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); try { while (!stopRunning) { PlaceholderFragment.this.cam.setParameters(paramsOn); PlaceholderFragment.this.cam.startPreview(); // We make the thread sleeping Thread.sleep(100 - freq); PlaceholderFragment.this.cam.setParameters(paramsOff); PlaceholderFragment.this.cam.startPreview(); Thread.sleep(freq); } } catch(Throwable t) {} } } |
- Исходный код будет доступен @github.