Это новая рубрика — как сделать приложение, где мы с вами будем учиться делать простые приложения для android, опираясь на материал бесплатных уроков нашего канала. Начнем с простого приложения — это фонарик. Приложение состоит из одной кнопки на экране, которая включает и выключает вспышку камеры устройства — если она есть, конечно.
1 2 3 |
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.FLASHLIGHT"/> <uses-feature android:name="android.hardware.camera"/> |
Также в активити добавим директиву, которая отключит его пересоздание при повороте экрана:
1 |
android:configChanges="orientation|keyboardHidden|screenSize" |
Нам понадобятся такие строки, добавьте в res/values/strings.xml:
1 2 3 4 5 6 |
<resources> <string name="app_name">Фонарик</string> <string name="error_text">Вспышка камеры недоступна!</string> <string name="error_title">Ошибка! Нет вспышки!</string> <string name="exit_message">Выход</string> </resources> |
1 2 3 4 5 6 7 8 |
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:type="radial" android:startColor="#303030" android:endColor="#050505" android:gradientRadius="450" android:angle="050"/> </shape> |
В папку res/raw (создайте, если ее нет) загрузите звук щелчка по ссылке.
В макете экрана приложения такие компоненты:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<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:background="@drawable/flash_background" tools:context="info.fandroid.flashlight.MainActivity"> <Switch android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/my_switch" android:layout_centerInParent="true"/> </RelativeLayout> |
Здесь используем экранный компонент Switch — переключатель, выравниваем его по центру.
Теперь код главного класса MainActivity.java:
1 2 3 4 5 6 7 8 9 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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
package ... import android.annotation.TargetApi; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.SoundPool; import android.os.Build; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.CompoundButton; import android.widget.Switch; import java.io.IOException; import java.util.List; public class MainActivity extends AppCompatActivity implements SoundPool.OnLoadCompleteListener { private int sound; private SoundPool soundPool; private Camera camera; Parameters parameters; private Switch mySwitch; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { createSoundPoolWithBuilder(); } else { createSoundPoolWithConstructor(); } soundPool.setOnLoadCompleteListener(this); sound = soundPool.load(this, R.raw.click, 1); mySwitch = (Switch) findViewById(R.id.my_switch); mySwitch.setChecked(true); mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { if (isChecked) { setFlashLigthOn(); } else { setFlashLightOff(); } } }); boolean isCameraFlash = getApplicationContext().getPackageManager() .hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); if (!isCameraFlash) { showCameraAlert(); } else { camera = Camera.open(); } } private void showCameraAlert() { new AlertDialog.Builder(this) .setTitle(R.string.error_title) .setMessage(R.string.error_text) .setPositiveButton(R.string.exit_message, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setIcon(android.R.drawable.ic_dialog_alert) .show(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) protected void createSoundPoolWithBuilder() { AudioAttributes attributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_GAME) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build(); soundPool = new SoundPool.Builder().setAudioAttributes(attributes).setMaxStreams(1).build(); } @SuppressWarnings("deprecation") protected void createSoundPoolWithConstructor() { soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0); } private void setFlashLigthOn() { soundPool.play(sound, 1, 1, 0, 0, 1); new Thread(new Runnable() { @Override public void run() { if (camera != null) { parameters = camera.getParameters(); if (parameters != null) { List supportedFlashModes = parameters.getSupportedFlashModes(); if (supportedFlashModes.contains(Parameters.FLASH_MODE_TORCH)) { parameters.setFlashMode(Parameters.FLASH_MODE_TORCH); } else if (supportedFlashModes.contains(Parameters.FLASH_MODE_ON)) { parameters.setFlashMode(Parameters.FLASH_MODE_ON); } else camera = null; if (camera != null) { camera.setParameters(parameters); camera.startPreview(); try { camera.setPreviewTexture(new SurfaceTexture(0)); } catch (IOException e) { e.printStackTrace(); } } } } } }).start(); } private void setFlashLightOff() { soundPool.play(sound, 1, 1, 0, 0, 1); new Thread(new Runnable() { @Override public void run() { if (camera != null) { parameters.setFlashMode(Parameters.FLASH_MODE_OFF); camera.setParameters(parameters); camera.stopPreview(); } } }).start(); } private void releaseCamera() { if (camera != null) { camera.release(); camera = null; } } @Override protected void onStop() { super.onStop(); releaseCamera(); } @Override protected void onPause() { super.onPause(); releaseCamera(); mySwitch.setChecked(false); } @Override protected void onResume() { super.onResume(); if (camera == null) { camera = Camera.open(); } else{ setFlashLigthOn(); } mySwitch.setChecked(true); } @Override public void onLoadComplete(SoundPool soundPool, int i, int i1) { } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
private void showCameraAlert() { new AlertDialog.Builder( this) .setTitle(R.string. error_title ) .setMessage(R.string. error_text ) .setPositiveButton(R.string. exit_message , new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setIcon(android.R.drawable. ic_dialog_alert ) .show(); } |
1 2 3 4 5 6 7 8 9 |
boolean isCameraFlash = getApplicationContext().getPackageManager() .hasSystemFeature(PackageManager. FEATURE_CAMERA_FLASH); if (!isCameraFlash) { showCameraAlert(); } else { camera = Camera.open(); } |
Получаем контекст приложения, PackageManager и выполняем метод hasSystemFeatureс с параметром FEATURE_CAMERA_FLASH. Добавляем условие — если вспышки нет, вызываем метод showCameraAlert(), а если вспышка есть — получаем экземпляр класса Camera.
Звук воспроизводит класс SoundPool. В последнее время рекомендуется создание и настройка экземпляра Soundpool с помощью класса SoundPool.Builder. Soundpool Builder доступен только на API 21+, так что нам нужно будет создать свой Soundpool по разному в зависимости от версии SDK устройства. Для Android 5 и выше будет выполняться этот метод:
1 2 3 4 5 6 7 8 9 10 |
@TargetApi (Build.VERSION_CODES. LOLLIPOP) protected void createSoundPoolWithBuilder(){ AudioAttributes attributes = new AudioAttributes.Builder() .setUsage(AudioAttributes. USAGE_GAME ) .setContentType(AudioAttributes. CONTENT_TYPE_SONIFICATION ) .build(); sp = new SoundPool.Builder().setAudioAttributes(attributes).setMaxStreams( 1).build(); } |
1 2 3 4 |
@SuppressWarnings ("deprecation" ) protected void createSoundPoolWithConstructor(){ sp = new SoundPool( 1, AudioManager. STREAM_MUSIC , 0 ); } |
1 2 3 4 5 6 7 |
if (Build.VERSION. SDK_INT >= Build.VERSION_CODES. LOLLIPOP ) { createSoundPoolWithBuilder(); } else { createSoundPoolWithConstructor(); } sp .setOnLoadCompleteListener( this); sound = sp .load( this, R.raw. click , 1 ); |
1 2 3 4 5 6 7 8 9 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 |
private void setFlashlightOn() { sp .play( sound, 1, 1, 0, 0, 1); new Thread( new Runnable() { public void run() { if ( camera != null) { params = camera .getParameters(); if( params != null ) { List supportedFlashModes = params .getSupportedFlashModes(); if(supportedFlashModes.contains(Parameters. FLASH_MODE_TORCH )) { params .setFlashMode( Parameters. FLASH_MODE_TORCH ); } else if(supportedFlashModes.contains(Parameters. FLASH_MODE_ON )) { params .setFlashMode( Parameters. FLASH_MODE_ON ); } else camera = null; if( camera != null ) { camera .setParameters( params); camera .startPreview(); try { camera .setPreviewTexture( new SurfaceTexture( 0)); } catch (IOException e) { e.printStackTrace(); } } } } } }).start(); } |
1 2 3 4 5 6 7 8 9 10 11 12 |
private void setFlashlightOff() { sp .play( sound, 1, 1, 0, 0, 1); new Thread( new Runnable() { public void run() { if( camera != null ) { params .setFlashMode(Parameters. FLASH_MODE_OFF); camera .setParameters( params); camera .stopPreview(); } } }).start(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mySwitch .setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked){ setFlashlightOn(); } else{ setFlashlightOff(); } } }); |
1 2 3 4 5 6 |
private void releaseCamera() { if ( camera != null) { camera.release(); camera = null ; } } |
1 2 3 4 5 |
@Override protected void onStop() { super .onStop(); releaseCamera(); } |
1 2 3 4 5 6 |
@Override protected void onPause() { super .onPause(); releaseCamera(); mySwitch.setChecked( false); } |
1 2 3 4 5 6 7 8 9 10 |
@Override protected void onResume() { super .onResume(); if ( camera == null) { camera = Camera. open(); } else { setFlashlightOn(); } mySwitch .setChecked( true); } |
При запуске на 7 андроиде пришлось самостоятельно бороться с разрешениями системы, причём такого разрешения как FLASHLIGHT моя студия никак признавать не хотела, всё работает только с android.permission.CAMERA
Для чего мы имплементили интерфейс SoundPool.OnLoadCompleteListener если его не используем?
Привет всем я начинаюший прогромист.