Статьи

Android Torch App со стробоскопическим источником света

В этом посте мы хотим описать, как создать простое приложение для Android. Это хороший пример, потому что мы можем изучить Android API для Android Camera. Мы будем использовать Android Studio в качестве IDE для разработки приложения. Это приложение будет не только использовать вспышку света, но мы также можем реализовать стробоскопический свет, где частота может быть изменена. Мы можем узнать, как использовать некоторые базовые компоненты пользовательского интерфейса, такие как ToggleButton и SeekBar . Мы хотим получить приложение, которое выглядит так:

android_torch_layout_strobo [4]

Макет приложения 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
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    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 мы имеем в результате:

android_torch_layout [4]

Управление вспышкой камеры Включение и выключение

Теперь мы должны обработать событие 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.