Цель лабораторной работы:
Разработка многооконного приложения, предоставляющего возможности: воспроизведения аудио и видео файлов, создания и отображения фотоснимков.
Задачи лабораторной работы:
Для достижения цели, поставленной в лабораторной работе, сформулируем требования к разрабатываемому приложению, назовем его "Media".
Приложение предоставляет пользователю возможность выбора рода деятельности:
В приложении предполагается реализовать четыре активности:
Создадим новое приложение, активность, полученную при создании проекта, оставим с именем MainActivity, она будет главной активностью приложения. Добавим в приложение еще три активности: New->Other...->Android Activity.
В нашем приложении созданные активности имеют следующие имена:
CameraActivity для работы с камерой и создания снимков;
MediaActivity для воспроизведения видео и аудио;
GalleryActivity для просмотра изображений.
Далее продолжим работу с этими активностями, настроим интерфейс и реализуем логику для каждой из них. Начнем с активностей, отвечающих за тот или иной вид деятельности, главную активность приведем в порядок в самом конце работы.
Настроим интерфейс активности для работы с камерой. Нам понадобится окно предварительного просмотра, добавим в окно активности элемент .
(рис 12.1) Интерфейс активности для работы с камерой
Реализуем логику активности, в данном случае необходимо при нажатии на кнопку выполнить снимок. Работаем с java файлом, описывающим соответствующий класс активности, CameraActivity.java.
Объявим следующие поля класса активности:
private Camera camera; //для проведения всех операций с камерой
private SurfaceHolder surfaceHolder; //для задания preview
private SurfaceView preview; //для отображения окна предпросмотра
private View shotBtn; //для выполнения снимка (кнопка)
Для начала работы с камерой необходимо ее инициализировать, сделать это лучше в методе onResume() класса активности, для инициализации используется следующая конструкция:
camera = Camera.open();
После завершения работы с камерой, необходимо ее освободить для других приложений, сделать это лучше в методе onPause() класса активности, для освобождения камеры используется следующая конструкция:
Camera.release();
Для активности, работающей с камерой, имеет смысл сразу задать расположение экрана следующим образом
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
в противном случае, приходится, например, в методе surfaceCreated() проверять расположение экрана и поворачивать окно предварительного просмотра, что не очень удобно, так как поворот экрана занимает некоторое время.
Выполним настройку окна предварительного просмотра:
preview = (SurfaceView) findViewById(R.id.surfaceCamera);
surfaceHolder = preview.getHolder();
surfaceHolder.addCallback(new MyCallback());
метод addCallback() используется для отслеживания изменений окна предпросмотра. Параметром этого метода служит экземпляр класса реализующего интерфейс SurfaceHolder.Callback, в нашем случае для реализации этого интерфейса создан внутренний класс активности MyCallback.
В этом классе необходимо реализовать методы:
public void surfaceCreated(SurfaceHolder holder);
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height);
public void surfaceDestroyed(SurfaceHolder holder);
Мы реализуем метод surfaceCreated(), оставшиеся два метода запишем с пустой реализацией.
В методе surfaceCreated() зададим окно предварительного просмотра:
camera.setPreviewDisplay(holder);
Выполним необходимые настройки окна предпросмотра (см. Листинг 12.1).
Запустим отображение окна предварительного просмотра:
camera.startPreview();
Пришло время обсудить и реализовать выполнение фотосъемки. Для того, чтобы сделать снимок необходимо вызвать метод takePicture(). Вызов этого метода можно выполнить из метода-обработчика события нажатия кнопки, тогда при нажатии на кнопку сразу будет получена фотография, а можно "растянуть удовольствие" и воспользоваться предварительной автофокусировкой.
Сначала инициализируем кнопку и добавим слушателя события нажатия в методе onCreate() активности:
shotBtn = findViewById(R.id.bCameraShot);
shotBtn.setOnClickListener(new MyViewListener());
В данном случае слушателем является экземпляр внутреннего класса MyViewListener, который реализует интерфейс View.OnClickListener.
class MyViewListener implements View.OnClickListener{
@Override
public void onClick(View v){
if (v == shotBtn){
camera.autoFocus(new MyAutoFocusCallback());
}
}
}
В методе onClick() вызываем обработчик автофокуса, слушателем события автофокусировки в данном случае является экземпляр внутреннего класса MyAutoFocusCallback, который реализует интерфейс Camera.AutoFocusCallback.
class MyAutoFocusCallback implements Camera.AutoFocusCallback{
@Override
public void onAutoFocus(boolean paramBoolean, Camera
paramCamera){
if (paramBoolean){
// если удалось сфокусироваться, делаем снимок
paramCamera.takePicture(null, null, null,
new MyPictureCallback());
}
}
}
Метод takePicture() имеет четыре параметра:
Camera.ShutterCallback shutter |
- вызывается в момент получения изображения с матрицы; |
Camera.PictureCallback raw |
- программе передаются для обработки raw данные (если поддерживается аппаратно); |
Camera.PictureCallback postview |
- программе передаются полностью обработанные данные (если поддерживается аппаратно); |
Camera.PictureCallback jpg |
- программе передается изображение в формате jpg. Здесь может быть организована запись изображения на карту памяти. |
В нашем случае последним параметром является экземпляр класса MyPictureCallback(), который реализует интерфейс Camera.PictureCallback. В этом классе реализован единственный метод (см. Листинг 12.1):
public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera){...}
В листинге 12.1 представлен код активности, реализующей работу с камерой и позволяющей выполнять снимки.
Чтобы разрешить приложению работать с камерой и сохранять фотографии на карте памяти, в манифест необходимо добавить следующие разрешения:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Настроим интерфейс активности для воспроизведения аудио и видео. Нам понадобится окно предварительного просмотра, добавим в окно активности элемент SurfaceView. Для работы приложения необходимо поле для задания расположения медиа контента для проигрывания, добавим элемент EditText, а также потребуются несколько кнопок, добавим в окно активности следующие кнопки:
| b_Start | - для запуска воспроизведения контента с начала, зададим свойству On Click этой кнопки значение onClickStart; |
| b_Pause | - для приостановки воспроизведения, зададим свойству On Click этой кнопки значение onClick; |
| b_Resume | - для продолжения воспроизведения с места приостановки, зададим свойству On Click этой кнопки значение onClick; |
| b_Stop | - для полной остановки воспроизведения, зададим свойству On Click этой кнопки значение onClick. |
Для возможности выбора зацикленного воспроизведения медиа контента добавим в окно активности элемент CheckBox.
Далее предлагаем настроить интерфейс самостоятельно. В нашем случае активность выглядит так, как показано на рис 12.2.
(рис 12.2) Интерфейс активности для воспроизведения аудио и видео
Реализуем логику активности, в данном случае необходимо выполнять следующие действия:
Элемент Loop управляет возможностью повтора воспроизведения.
Работать будем с файлом MediaActivity.java, описывающим соответствующий класс активности.
Объявим следующие поля класса активности:
MediaPlayer mediaPlayer; //управляет воспроизведением
CheckBox chbLoop; //управляет режимом повтора
В методе onCreate() активности настроим чек-бокс так, чтобы он включал/выключал режим повтора для плеера.
chbLoop = (CheckBox) findViewById(R.id.chb_Loop);
chbLoop.setOnCheckedChangeListener(new
OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (mediaPlayer != null)
mediaPlayer.setLooping(isChecked);
}
});
Метод onClickStart() используется для обработки нажатий на кнопку Try to play. В этом метода сначала освобождаем ресурсы текущего проигрывателя, используя вызов метода:
releaseMP();
Этот метод рекомендуется вызывать по окончанию использования плеера, а также при onPause/onStop, если нет острой необходимости держать объект.
Реализация метода releaseMP():
private void releaseMP() {
if (mediaPlayer != null) {
try {
mediaPlayer.release();
mediaPlayer = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
Продолжим рассмотрение метода onClickStart(). В нем после освобождения ресурсов, занимаемся подготовкой плеера к воспроизведению.
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(DATA);
mediaPlayer.setDisplay(((SurfaceView)
findViewById(R.id.surfaceView1)).getHolder());
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync();
Создаем новый объект класса MediaPlayer.
Используем метод setDataSource() для задания источника данных, в качестве параметра передаем строку из поля ввода, сохраненную в переменной DATA.
Используя метод setDisplay(), задаем экран для воспроизведения, в нашем случае элемент SurfaceView, добавленный в окно активности на этапе формирования интерфейса.
Метод prepareAsync() выполняет асинхронную подготовку плеера к воспроизведению и когда подготовка будет завершена сообщает об этом слушателю, указанному в методе setPreparedListener(). В нашем случае слушателем является сам класс активности, для этого он объявлен, как класс реализующий интерфейс OnPreparedListener. В случае готовности плеера вызывается метод onPrepared(MediaPlayer mp), объявленный в указанном интерфейсе и реализованный в классе активности, в этом методе выполняется запуск воспроизведения:
mp.start();
Существует еще метод prepare(), он также выполняет подготовку плеера, но в синхронном режиме. Для прослушивания файлов из интернета, необходимо использовать асинхронный режим, что мы и сделали.
Последние две строки метода onClickStart():
mediaPlayer.setLooping(chbLoop.isChecked());
mediaPlayer.setOnCompletionListener(this);
В первой из них определяется возможность циклического воспроизведения в зависимости от значения чек-бокса.
Во второй - задается слушатель для получения сообщения о достижении конца воспроизводимого контента. В нашем случае этим слушателем будет сам класс активности для этого он объявлен, как класс реализующий интерфейс OnCompletionListener. Полную версию метода onClickStart() можно найти в листинге 12.2.
Метод onClick() вызывается при нажатии на любую кнопку: Pause, Resume, Stop. В этом методе выполняется проверка какая кнопка была нажата и после этого выполняются соответствующие действия.
Если нажата кнопка Pause, выполняется следующая конструкция:
if (mediaPlayer.isPlaying()) mediaPlayer.pause();
Если нажата кнопка Release, выполняется следующая конструкция:
if (!mediaPlayer.isPlaying()) mediaPlayer.start();
Если нажата кнопка Stop, выполняется следующая конструкция:
mediaPlayer.stop();
Имеет смысл обратить внимание на метод активности:
protected void onDestroy() {
super.onDestroy();
releaseMP();
}
В этом методе обязательно необходимо освободить ресурсы, что и выполняется вызовом метода releaseMP().
Полный код класса MediaActivity представлен в листинге 12.2.
Настроим интерфейс активности для просмотра изображений. Нам потребуется элемент для просмотра изображений, добавим в окно активности элемент ImageView. Добавим информационное поле, т. е. элемент TextView, для отображения информации об общем количестве изображений в папке и номере просматриваемого изображения. Добавим две кнопки, для перемещения от одного изображения к другому вперед и назад.
Предлагаем настроить интерфейс самостоятельно. В нашем случае активность выглядит так, как показано на рис 12.3. Но это, разумеется, не единственно возможный вариант.
(рис 12.3) Интерфейс активности для просмотра изображений
Реализуем логику активности, данная активность ориентирована на просмотр снимков, сделанных в этом же приложении и сохраненных в папке: /sdcard/TrainingMedia/. Кнопка Назад выводит в окно просмотра предыдущий снимок, по отношению к уже отображенному, зададим свойству On Click этой кнопки значение onPrevious. Кнопка Вперед выводит в окно просмотра следующий снимок, по отношению к уже отображенному, зададим свойству On Click этой кнопки значение onNext.
Работать будем с файлом GalleryActivity.java, описывающим класс соответствующей активности.
Объявим поля класса активности:
int currentImage=0;
ArrayList<String> images;
ImageView imageView;
TextView nameView;
Настройку основных элементов для вывода изображений на экран выполним в методе onResume() активности, этот метод вызывается каждый раз, перед выводом активности на передний план (см. жизненный цикл активности в лекции 2).
images=new ArrayList<String>();
imageView=((ImageView)findViewById(R.id.image));
try{
File imagesDirectory=new File("/sdcard/TrainingMedia/");
images=searchImage(imagesDirectory);
updatePhoto(Uri.parse(images.get(currentImage)));
}catch(Exception e){
nameView.setText("Ошибка: Папка '/sdcard/TrainingMedia/' не
найдена");
}
Для получения списка изображений в переменную images используется метод searchImage(), этот метод, используя переданный в него в качестве параметра адрес директории с изображениями, находит файл с расширением .jpg, .png или .jpeg и добавляет его к списку изображений. Метод возвращает список доступных файлов с изображениями. Код метода представлен в листинге 12.3.
Метод updatePhoto() выполняет обновление счетчика фотографий в информационном поле и выводит на экран изображение, соответствующее переданному в метод URI. Код метода представлен в листинге 12.3.
В методе onPause() активности выполняется очистка списка изображений и освобождение памяти. Этот метод вызывается каждый раз, как активность теряет фокус ввода. Код метода представлен в листинге 12.3.
Осталось рассмотреть еще два метода: onPrevious() и onNext().
Первый метод вызывается, когда нажата кнопка Назад. В нем уменьшается номер текущего изображения и вызывается метод updatePhoto().
Второй метод вызывается, когда нажата кнопка Вперед. В нем увеличивается номер, текущего изображения и вызывается метод updatePhoto().
Полный код класса GalleryActivity представлен в листинге 12.3.
Настроим интерфейс главной активности приложения. Эта активность содержит три кнопки (Image Button):
| bCamera | - для вызова активности, предоставляющей возможности работы с камерой; |
| bGallery | - для вызова активности, предоставляющей возможности просмотра изображений; |
| bMusic | - для вызова активности, предоставляющей возможности воспроизведения аудио и видео. |
Предлагаем настроить интерфейс самостоятельно. В нашем случае активность выглядит так, как показано на рис 12.4. Но это, разумеется, не единственно возможный вариант.
(рис 12.4) Интерфейс главной активности приложения
Реализуем логику главной активности. В данном случае достаточно настроить обработку событий нажатия на кнопки, таким образом, чтобы при нажатии на кнопку запускалась соответствующая активность. Работаем с java файлом, описывающим класс активности.
btnClick, как реализацию интерфейса-слушателя OnClickListener:
OnClickListener btnClick=new OnClickListener() {
@Override
public void onClick(View v) {
Click(v.getId());
}
};
((ImageButton)findViewById(R.id.bMusic)).setOnClickListener(btnClick);
((ImageButton)findViewById(R.id.bCamera)).setOnClickListener(btnClick);
((ImageButton)findViewById(R.id.bGallery)).setOnClickListener(btnClick);
onClick() слушателя вызываем метод Click(), в котором выполняется запуск соответствующей активности:
protected void Click(int view){
Intent intent=null;
switch (view){
case R.id.bMusic: intent=new Intent(this,mediaPlayer.class); break;
case R.id.bGallery: intent=new Intent(this,GalleryActivity.class); break;
case R.id.bCamera: intent=new Intent(this,CameraActivity.class); break;
default: break;
}
if(intent!=null){
startActivity(intent);
}
}
В листинге 12.4 представлен код главной активности.
package com.example.lab5_4_media;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.ViewGroup.LayoutParams;
public class CameraActivity extends Activity {
private Camera camera;
private SurfaceHolder surfaceHolder;
private SurfaceView preview;
private View shotBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// если хотим, чтобы приложение постоянно имело портретную ориентацию
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// если хотим, чтобы приложение было полноэкранным
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// и без заголовка
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_camera);
preview = (SurfaceView) findViewById(R.id.surfaceCamera);
surfaceHolder = preview.getHolder();
surfaceHolder.addCallback(new MyCallback(this));
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
shotBtn = findViewById(R.id.bCameraShot);
shotBtn.setOnClickListener(new MyViewListener());
}
@Override
protected void onResume() {
super.onResume();
camera = Camera.open();
}
@Override
protected void onPause(){
super.onPause();
if (camera != null){
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
}
class MyCallback implements SurfaceHolder.Callback{
Activity host;
MyCallback(Activity act){
host=act;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height){}
@Override
public void surfaceCreated(SurfaceHolder holder){
try {
camera.setPreviewDisplay(holder);
camera.setPreviewCallback(new MyPreviewCallback());
}
catch (IOException e){
Log.d("myLogs","Ошибка камеры");
e.printStackTrace();
}
Size previewSize = camera.getParameters().getPreviewSize();
float aspect = (float) previewSize.width / previewSize.height;
int previewSurfaceWidth = preview.getWidth();
int previewSurfaceHeight = preview.getHeight();
LayoutParams lp = preview.getLayoutParams();
// здесь корректируем размер отображаемого preview, чтобы не было искажений
if (host.getResources().getConfiguration().orientation !=
Configuration.ORIENTATION_LANDSCAPE){
// портретный вид
camera.setDisplayOrientation(90);
lp.height = previewSurfaceHeight;
lp.width = (int) (previewSurfaceHeight / aspect);
}
else {
// ландшафтный
camera.setDisplayOrientation(0);
lp.width = previewSurfaceWidth;
lp.height = (int) (previewSurfaceWidth / aspect);
}
preview.setLayoutParams(lp);
camera.startPreview();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder){}
}
class MyViewListener implements View.OnClickListener{
@Override
public void onClick(View v) {
if (v == shotBtn) {
// либо делаем снимок непосредственно здесь
// либо включаем обработчик автофокуса
//camera.takePicture(null, null, null, this);
camera.autoFocus(new MyAutoFocusCallback());
}
}
}
class MyAutoFocusCallback implements Camera.AutoFocusCallback{
@Override
public void onAutoFocus(boolean paramBoolean, Camera paramCamera){
if (paramBoolean){
// если удалось сфокусироваться, делаем снимок
paramCamera.takePicture(null, null, null, new MyPictureCallback());
}
}
}
class MyPictureCallback implements Camera.PictureCallback{
@Override
public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera){
// сохраняем полученные jpg в папке /sdcard/CameraExample/
// имя файла - System.currentTimeMillis()
try {
File saveDir = new File("/sdcard/CameraExample/");
if (!saveDir.exists()) {
saveDir.mkdirs();
}
FileOutputStream os = new FileOutputStream(String.format("/sdcard/CameraExample/%d.jpg", System.currentTimeMillis()));
os.write(paramArrayOfByte);
os.close();
}
catch (Exception e) {}
// после того, как снимок сделан, показ превью отключается.
// Необходимо включить его
paramCamera.startPreview();
}
}
class MyPreviewCallback implements Camera.PreviewCallback{
@Override
public void onPreviewFrame(byte[] paramArrayOfByte, Camera paramCamera) {
// здесь можно обрабатывать изображение, показываемое в preview
}
}
}
package com.example.lab5_4_media;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.view.Gravity;
import android.view.SurfaceView;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Toast;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.app.Activity;
public class MediaActivity extends Activity implements OnPreparedListener, OnCompletionListener{
MediaPlayer mediaPlayer;
CheckBox chbLoop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_media);
chbLoop = (CheckBox) findViewById(R.id.chb_Loop);
chbLoop.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (mediaPlayer != null)
mediaPlayer.setLooping(isChecked);
}
});
}
public void onClickStart(View view) {
releaseMP();
String DATA=((EditText)findViewById(R.id.et_MediaPath)).getText().toString();
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(DATA);
mediaPlayer.setDisplay(((SurfaceView)
findViewById(R.id.surfaceView1)).getHolder());
//mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync();
} catch (Exception e) {
showMessage("Ошибка воспроизведения");
}
if (mediaPlayer == null)
return;
mediaPlayer.setLooping(chbLoop.isChecked());
mediaPlayer.setOnCompletionListener(this);
}
private void showMessage(String text){
Toast toast = Toast.makeText(getApplicationContext(), text,
Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
private void releaseMP() {
if (mediaPlayer != null) {
try {
mediaPlayer.release();
mediaPlayer = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void onClick(View view) {
if (mediaPlayer == null)
return;
switch (view.getId()) {
case R.id.b_Pause:
if (mediaPlayer.isPlaying())
mediaPlayer.pause();
break;
case R.id.b_Resume:
if (!mediaPlayer.isPlaying())
mediaPlayer.start();
break;
case R.id.b_Stop:
mediaPlayer.stop();
break;
}
}
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
@Override
public void onCompletion(MediaPlayer mp) {}
@Override
protected void onDestroy() {
super.onDestroy();
releaseMP();
}
}
package com.example.lab5_4_media;
import java.io.File;
import java.util.ArrayList;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
public class GalleryActivity extends Activity {
int currentImage=0;
ArrayList<String> images;
ImageView imageView;
TextView nameView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gallery);
}
@Override
public void onResume(){
super.onResume();
currentImage=0;
Log.d("myLogs","onResume cI="+currentImage);
nameView=((TextView)findViewById(R.id.imageName));
images=new ArrayList<String>();
imageView=((ImageView)findViewById(R.id.image));
try{
File imagesDirectory=new File("/sdcard/TrainingMedia/");
images=searchImage(imagesDirectory);
updatePhoto(Uri.parse(images.get(currentImage)));
}catch(Exception e){
nameView.setText("Ошибка: Папка '/sdcard/TrainingMedia/' не найдена");
Log.d("myLogs","Ошибка");
}
}
@Override
protected void onPause()
{
super.onPause();
images.clear();
Log.d("myLogs","onPause cI="+currentImage);
}
private ArrayList<String> searchImage(File dir){
ArrayList<String> imagesFinded=new ArrayList<String>();
for(File f:dir.listFiles()){
if(!f.isDirectory()){
String fileExt=getFileExt(f.getAbsolutePath());
if(fileExt.equals("png") || fileExt.equals("jpg") || fileExt.equals("jpeg")){
Log.d("myLogs","Файл найден "+f.getAbsolutePath());
imagesFinded.add(f.getAbsolutePath());
}
}
}
return imagesFinded;
}
public static String getFileExt(String filename){
return filename.substring(filename.lastIndexOf(".") + 1);
}
public void updatePhoto(Uri uri){
try{
nameView.setText((currentImage+1)+"/"+images.size());
imageView.setImageURI(uri);
}catch(Exception e){
nameView.setText("Ошибка загрузки файла");
}
}
public void onNext(View v){
if(currentImage+1<images.size() images.size()>0){
currentImage++;
updatePhoto(Uri.parse(images.get(currentImage)));
}
}
public void onPrevious(View v){
if(currentImage>0 images.size()>0){
currentImage--;
updatePhoto(Uri.parse(images.get(currentImage)));
}
}
}
package com.example.lab5_4_media;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
OnClickListener btnClick=new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("myLogs",v.getId()+"");
Click(v.getId());
}
};
((ImageButton)findViewById(R.id.bMusic)).setOnClickListener(btnClick);
((ImageButton)findViewById(R.id.bCamera)).setOnClickListener(btnClick);
((ImageButton)findViewById(R.id.bGallery)).setOnClickListener(btnClick);
}
protected void Click(int view){
Intent intent=null;
Log.d("myLogs",view+"");
switch (view){
case R.id.bMusic: intent=new Intent(this,MediaActivity.class); break;
case R.id.bGallery: intent=new Intent(this,GalleryActivity.class); break;
case R.id.bCamera: intent=new Intent(this,CameraActivity.class); break;
default: break;
}
if(intent!=null){
Log.d("myLogs","Интент = "+intent.toString());
startActivity(intent);
}
}
}
Цель лабораторной работы:
Разработка многооконного приложения, предоставляющего возможности: воспроизведения аудио и видео файлов, создания и отображения фотоснимков.
Задачи лабораторной работы:
Для достижения цели, поставленной в лабораторной работе, сформулируем требования к разрабатываемому приложению, назовем его "Media".
Приложение предоставляет пользователю возможность выбора рода деятельности:
В приложении предполагается реализовать четыре активности:
Создадим новое приложение, активность, полученную при создании проекта, оставим с именем MainActivity, она будет главной активностью приложения. Добавим в приложение еще три активности: New->Other...->Android Activity.
В нашем приложении созданные активности имеют следующие имена:
CameraActivity для работы с камерой и создания снимков;
MediaActivity для воспроизведения видео и аудио;
GalleryActivity для просмотра изображений.
Далее продолжим работу с этими активностями, настроим интерфейс и реализуем логику для каждой из них. Начнем с активностей, отвечающих за тот или иной вид деятельности, главную активность приведем в порядок в самом конце работы.
Настроим интерфейс активности для работы с камерой. Нам понадобится окно предварительного просмотра, добавим в окно активности элемент .
(рис 12.1) Интерфейс активности для работы с камерой
Реализуем логику активности, в данном случае необходимо при нажатии на кнопку выполнить снимок. Работаем с java файлом, описывающим соответствующий класс активности, CameraActivity.java.
Объявим следующие поля класса активности:
private Camera camera; //для проведения всех операций с камерой
private SurfaceHolder surfaceHolder; //для задания preview
private SurfaceView preview; //для отображения окна предпросмотра
private View shotBtn; //для выполнения снимка (кнопка)
Для начала работы с камерой необходимо ее инициализировать, сделать это лучше в методе onResume() класса активности, для инициализации используется следующая конструкция:
camera = Camera.open();
После завершения работы с камерой, необходимо ее освободить для других приложений, сделать это лучше в методе onPause() класса активности, для освобождения камеры используется следующая конструкция:
Camera.release();
Для активности, работающей с камерой, имеет смысл сразу задать расположение экрана следующим образом
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
в противном случае, приходится, например, в методе surfaceCreated() проверять расположение экрана и поворачивать окно предварительного просмотра, что не очень удобно, так как поворот экрана занимает некоторое время.
Выполним настройку окна предварительного просмотра:
preview = (SurfaceView) findViewById(R.id.surfaceCamera);
surfaceHolder = preview.getHolder();
surfaceHolder.addCallback(new MyCallback());
метод addCallback() используется для отслеживания изменений окна предпросмотра. Параметром этого метода служит экземпляр класса реализующего интерфейс SurfaceHolder.Callback, в нашем случае для реализации этого интерфейса создан внутренний класс активности MyCallback.
В этом классе необходимо реализовать методы:
public void surfaceCreated(SurfaceHolder holder);
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height);
public void surfaceDestroyed(SurfaceHolder holder);
Мы реализуем метод surfaceCreated(), оставшиеся два метода запишем с пустой реализацией.
В методе surfaceCreated() зададим окно предварительного просмотра:
camera.setPreviewDisplay(holder);
Выполним необходимые настройки окна предпросмотра (см. Листинг 12.1).
Запустим отображение окна предварительного просмотра:
camera.startPreview();
Пришло время обсудить и реализовать выполнение фотосъемки. Для того, чтобы сделать снимок необходимо вызвать метод takePicture(). Вызов этого метода можно выполнить из метода-обработчика события нажатия кнопки, тогда при нажатии на кнопку сразу будет получена фотография, а можно "растянуть удовольствие" и воспользоваться предварительной автофокусировкой.
Сначала инициализируем кнопку и добавим слушателя события нажатия в методе onCreate() активности:
shotBtn = findViewById(R.id.bCameraShot);
shotBtn.setOnClickListener(new MyViewListener());
В данном случае слушателем является экземпляр внутреннего класса MyViewListener, который реализует интерфейс View.OnClickListener.
class MyViewListener implements View.OnClickListener{
@Override
public void onClick(View v){
if (v == shotBtn){
camera.autoFocus(new MyAutoFocusCallback());
}
}
}
В методе onClick() вызываем обработчик автофокуса, слушателем события автофокусировки в данном случае является экземпляр внутреннего класса MyAutoFocusCallback, который реализует интерфейс Camera.AutoFocusCallback.
class MyAutoFocusCallback implements Camera.AutoFocusCallback{
@Override
public void onAutoFocus(boolean paramBoolean, Camera
paramCamera){
if (paramBoolean){
// если удалось сфокусироваться, делаем снимок
paramCamera.takePicture(null, null, null,
new MyPictureCallback());
}
}
}
Метод takePicture() имеет четыре параметра:
Camera.ShutterCallback shutter |
- вызывается в момент получения изображения с матрицы; |
Camera.PictureCallback raw |
- программе передаются для обработки raw данные (если поддерживается аппаратно); |
Camera.PictureCallback postview |
- программе передаются полностью обработанные данные (если поддерживается аппаратно); |
Camera.PictureCallback jpg |
- программе передается изображение в формате jpg. Здесь может быть организована запись изображения на карту памяти. |
В нашем случае последним параметром является экземпляр класса MyPictureCallback(), который реализует интерфейс Camera.PictureCallback. В этом классе реализован единственный метод (см. Листинг 12.1):
public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera){...}
В листинге 12.1 представлен код активности, реализующей работу с камерой и позволяющей выполнять снимки.
Чтобы разрешить приложению работать с камерой и сохранять фотографии на карте памяти, в манифест необходимо добавить следующие разрешения:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Настроим интерфейс активности для воспроизведения аудио и видео. Нам понадобится окно предварительного просмотра, добавим в окно активности элемент SurfaceView. Для работы приложения необходимо поле для задания расположения медиа контента для проигрывания, добавим элемент EditText, а также потребуются несколько кнопок, добавим в окно активности следующие кнопки:
| b_Start | - для запуска воспроизведения контента с начала, зададим свойству On Click этой кнопки значение onClickStart; |
| b_Pause | - для приостановки воспроизведения, зададим свойству On Click этой кнопки значение onClick; |
| b_Resume | - для продолжения воспроизведения с места приостановки, зададим свойству On Click этой кнопки значение onClick; |
| b_Stop | - для полной остановки воспроизведения, зададим свойству On Click этой кнопки значение onClick. |
Для возможности выбора зацикленного воспроизведения медиа контента добавим в окно активности элемент CheckBox.
Далее предлагаем настроить интерфейс самостоятельно. В нашем случае активность выглядит так, как показано на рис 12.2.
(рис 12.2) Интерфейс активности для воспроизведения аудио и видео
Реализуем логику активности, в данном случае необходимо выполнять следующие действия:
Элемент Loop управляет возможностью повтора воспроизведения.
Работать будем с файлом MediaActivity.java, описывающим соответствующий класс активности.
Объявим следующие поля класса активности:
MediaPlayer mediaPlayer; //управляет воспроизведением
CheckBox chbLoop; //управляет режимом повтора
В методе onCreate() активности настроим чек-бокс так, чтобы он включал/выключал режим повтора для плеера.
chbLoop = (CheckBox) findViewById(R.id.chb_Loop);
chbLoop.setOnCheckedChangeListener(new
OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (mediaPlayer != null)
mediaPlayer.setLooping(isChecked);
}
});
Метод onClickStart() используется для обработки нажатий на кнопку Try to play. В этом метода сначала освобождаем ресурсы текущего проигрывателя, используя вызов метода:
releaseMP();
Этот метод рекомендуется вызывать по окончанию использования плеера, а также при onPause/onStop, если нет острой необходимости держать объект.
Реализация метода releaseMP():
private void releaseMP() {
if (mediaPlayer != null) {
try {
mediaPlayer.release();
mediaPlayer = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
Продолжим рассмотрение метода onClickStart(). В нем после освобождения ресурсов, занимаемся подготовкой плеера к воспроизведению.
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(DATA);
mediaPlayer.setDisplay(((SurfaceView)
findViewById(R.id.surfaceView1)).getHolder());
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync();
Создаем новый объект класса MediaPlayer.
Используем метод setDataSource() для задания источника данных, в качестве параметра передаем строку из поля ввода, сохраненную в переменной DATA.
Используя метод setDisplay(), задаем экран для воспроизведения, в нашем случае элемент SurfaceView, добавленный в окно активности на этапе формирования интерфейса.
Метод prepareAsync() выполняет асинхронную подготовку плеера к воспроизведению и когда подготовка будет завершена сообщает об этом слушателю, указанному в методе setPreparedListener(). В нашем случае слушателем является сам класс активности, для этого он объявлен, как класс реализующий интерфейс OnPreparedListener. В случае готовности плеера вызывается метод onPrepared(MediaPlayer mp), объявленный в указанном интерфейсе и реализованный в классе активности, в этом методе выполняется запуск воспроизведения:
mp.start();
Существует еще метод prepare(), он также выполняет подготовку плеера, но в синхронном режиме. Для прослушивания файлов из интернета, необходимо использовать асинхронный режим, что мы и сделали.
Последние две строки метода onClickStart():
mediaPlayer.setLooping(chbLoop.isChecked());
mediaPlayer.setOnCompletionListener(this);
В первой из них определяется возможность циклического воспроизведения в зависимости от значения чек-бокса.
Во второй - задается слушатель для получения сообщения о достижении конца воспроизводимого контента. В нашем случае этим слушателем будет сам класс активности для этого он объявлен, как класс реализующий интерфейс OnCompletionListener. Полную версию метода onClickStart() можно найти в листинге 12.2.
Метод onClick() вызывается при нажатии на любую кнопку: Pause, Resume, Stop. В этом методе выполняется проверка какая кнопка была нажата и после этого выполняются соответствующие действия.
Если нажата кнопка Pause, выполняется следующая конструкция:
if (mediaPlayer.isPlaying()) mediaPlayer.pause();
Если нажата кнопка Release, выполняется следующая конструкция:
if (!mediaPlayer.isPlaying()) mediaPlayer.start();
Если нажата кнопка Stop, выполняется следующая конструкция:
mediaPlayer.stop();
Имеет смысл обратить внимание на метод активности:
protected void onDestroy() {
super.onDestroy();
releaseMP();
}
В этом методе обязательно необходимо освободить ресурсы, что и выполняется вызовом метода releaseMP().
Полный код класса MediaActivity представлен в листинге 12.2.
Настроим интерфейс активности для просмотра изображений. Нам потребуется элемент для просмотра изображений, добавим в окно активности элемент ImageView. Добавим информационное поле, т. е. элемент TextView, для отображения информации об общем количестве изображений в папке и номере просматриваемого изображения. Добавим две кнопки, для перемещения от одного изображения к другому вперед и назад.
Предлагаем настроить интерфейс самостоятельно. В нашем случае активность выглядит так, как показано на рис 12.3. Но это, разумеется, не единственно возможный вариант.
(рис 12.3) Интерфейс активности для просмотра изображений
Реализуем логику активности, данная активность ориентирована на просмотр снимков, сделанных в этом же приложении и сохраненных в папке: /sdcard/TrainingMedia/. Кнопка Назад выводит в окно просмотра предыдущий снимок, по отношению к уже отображенному, зададим свойству On Click этой кнопки значение onPrevious. Кнопка Вперед выводит в окно просмотра следующий снимок, по отношению к уже отображенному, зададим свойству On Click этой кнопки значение onNext.
Работать будем с файлом GalleryActivity.java, описывающим класс соответствующей активности.
Объявим поля класса активности:
int currentImage=0;
ArrayList<String> images;
ImageView imageView;
TextView nameView;
Настройку основных элементов для вывода изображений на экран выполним в методе onResume() активности, этот метод вызывается каждый раз, перед выводом активности на передний план (см. жизненный цикл активности в лекции 2).
images=new ArrayList<String>();
imageView=((ImageView)findViewById(R.id.image));
try{
File imagesDirectory=new File("/sdcard/TrainingMedia/");
images=searchImage(imagesDirectory);
updatePhoto(Uri.parse(images.get(currentImage)));
}catch(Exception e){
nameView.setText("Ошибка: Папка '/sdcard/TrainingMedia/' не
найдена");
}
Для получения списка изображений в переменную images используется метод searchImage(), этот метод, используя переданный в него в качестве параметра адрес директории с изображениями, находит файл с расширением .jpg, .png или .jpeg и добавляет его к списку изображений. Метод возвращает список доступных файлов с изображениями. Код метода представлен в листинге 12.3.
Метод updatePhoto() выполняет обновление счетчика фотографий в информационном поле и выводит на экран изображение, соответствующее переданному в метод URI. Код метода представлен в листинге 12.3.
В методе onPause() активности выполняется очистка списка изображений и освобождение памяти. Этот метод вызывается каждый раз, как активность теряет фокус ввода. Код метода представлен в листинге 12.3.
Осталось рассмотреть еще два метода: onPrevious() и onNext().
Первый метод вызывается, когда нажата кнопка Назад. В нем уменьшается номер текущего изображения и вызывается метод updatePhoto().
Второй метод вызывается, когда нажата кнопка Вперед. В нем увеличивается номер, текущего изображения и вызывается метод updatePhoto().
Полный код класса GalleryActivity представлен в листинге 12.3.
Настроим интерфейс главной активности приложения. Эта активность содержит три кнопки (Image Button):
| bCamera | - для вызова активности, предоставляющей возможности работы с камерой; |
| bGallery | - для вызова активности, предоставляющей возможности просмотра изображений; |
| bMusic | - для вызова активности, предоставляющей возможности воспроизведения аудио и видео. |
Предлагаем настроить интерфейс самостоятельно. В нашем случае активность выглядит так, как показано на рис 12.4. Но это, разумеется, не единственно возможный вариант.
(рис 12.4) Интерфейс главной активности приложения
Реализуем логику главной активности. В данном случае достаточно настроить обработку событий нажатия на кнопки, таким образом, чтобы при нажатии на кнопку запускалась соответствующая активность. Работаем с java файлом, описывающим класс активности.
btnClick, как реализацию интерфейса-слушателя OnClickListener:
OnClickListener btnClick=new OnClickListener() {
@Override
public void onClick(View v) {
Click(v.getId());
}
};
((ImageButton)findViewById(R.id.bMusic)).setOnClickListener(btnClick);
((ImageButton)findViewById(R.id.bCamera)).setOnClickListener(btnClick);
((ImageButton)findViewById(R.id.bGallery)).setOnClickListener(btnClick);
onClick() слушателя вызываем метод Click(), в котором выполняется запуск соответствующей активности:
protected void Click(int view){
Intent intent=null;
switch (view){
case R.id.bMusic: intent=new Intent(this,mediaPlayer.class); break;
case R.id.bGallery: intent=new Intent(this,GalleryActivity.class); break;
case R.id.bCamera: intent=new Intent(this,CameraActivity.class); break;
default: break;
}
if(intent!=null){
startActivity(intent);
}
}
В листинге 12.4 представлен код главной активности.
package com.example.lab5_4_media;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.ViewGroup.LayoutParams;
public class CameraActivity extends Activity {
private Camera camera;
private SurfaceHolder surfaceHolder;
private SurfaceView preview;
private View shotBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// если хотим, чтобы приложение постоянно имело портретную ориентацию
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// если хотим, чтобы приложение было полноэкранным
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// и без заголовка
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_camera);
preview = (SurfaceView) findViewById(R.id.surfaceCamera);
surfaceHolder = preview.getHolder();
surfaceHolder.addCallback(new MyCallback(this));
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
shotBtn = findViewById(R.id.bCameraShot);
shotBtn.setOnClickListener(new MyViewListener());
}
@Override
protected void onResume() {
super.onResume();
camera = Camera.open();
}
@Override
protected void onPause(){
super.onPause();
if (camera != null){
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
}
class MyCallback implements SurfaceHolder.Callback{
Activity host;
MyCallback(Activity act){
host=act;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height){}
@Override
public void surfaceCreated(SurfaceHolder holder){
try {
camera.setPreviewDisplay(holder);
camera.setPreviewCallback(new MyPreviewCallback());
}
catch (IOException e){
Log.d("myLogs","Ошибка камеры");
e.printStackTrace();
}
Size previewSize = camera.getParameters().getPreviewSize();
float aspect = (float) previewSize.width / previewSize.height;
int previewSurfaceWidth = preview.getWidth();
int previewSurfaceHeight = preview.getHeight();
LayoutParams lp = preview.getLayoutParams();
// здесь корректируем размер отображаемого preview, чтобы не было искажений
if (host.getResources().getConfiguration().orientation !=
Configuration.ORIENTATION_LANDSCAPE){
// портретный вид
camera.setDisplayOrientation(90);
lp.height = previewSurfaceHeight;
lp.width = (int) (previewSurfaceHeight / aspect);
}
else {
// ландшафтный
camera.setDisplayOrientation(0);
lp.width = previewSurfaceWidth;
lp.height = (int) (previewSurfaceWidth / aspect);
}
preview.setLayoutParams(lp);
camera.startPreview();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder){}
}
class MyViewListener implements View.OnClickListener{
@Override
public void onClick(View v) {
if (v == shotBtn) {
// либо делаем снимок непосредственно здесь
// либо включаем обработчик автофокуса
//camera.takePicture(null, null, null, this);
camera.autoFocus(new MyAutoFocusCallback());
}
}
}
class MyAutoFocusCallback implements Camera.AutoFocusCallback{
@Override
public void onAutoFocus(boolean paramBoolean, Camera paramCamera){
if (paramBoolean){
// если удалось сфокусироваться, делаем снимок
paramCamera.takePicture(null, null, null, new MyPictureCallback());
}
}
}
class MyPictureCallback implements Camera.PictureCallback{
@Override
public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera){
// сохраняем полученные jpg в папке /sdcard/CameraExample/
// имя файла - System.currentTimeMillis()
try {
File saveDir = new File("/sdcard/CameraExample/");
if (!saveDir.exists()) {
saveDir.mkdirs();
}
FileOutputStream os = new FileOutputStream(String.format("/sdcard/CameraExample/%d.jpg", System.currentTimeMillis()));
os.write(paramArrayOfByte);
os.close();
}
catch (Exception e) {}
// после того, как снимок сделан, показ превью отключается.
// Необходимо включить его
paramCamera.startPreview();
}
}
class MyPreviewCallback implements Camera.PreviewCallback{
@Override
public void onPreviewFrame(byte[] paramArrayOfByte, Camera paramCamera) {
// здесь можно обрабатывать изображение, показываемое в preview
}
}
}
package com.example.lab5_4_media;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.view.Gravity;
import android.view.SurfaceView;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Toast;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.app.Activity;
public class MediaActivity extends Activity implements OnPreparedListener, OnCompletionListener{
MediaPlayer mediaPlayer;
CheckBox chbLoop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_media);
chbLoop = (CheckBox) findViewById(R.id.chb_Loop);
chbLoop.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (mediaPlayer != null)
mediaPlayer.setLooping(isChecked);
}
});
}
public void onClickStart(View view) {
releaseMP();
String DATA=((EditText)findViewById(R.id.et_MediaPath)).getText().toString();
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(DATA);
mediaPlayer.setDisplay(((SurfaceView)
findViewById(R.id.surfaceView1)).getHolder());
//mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync();
} catch (Exception e) {
showMessage("Ошибка воспроизведения");
}
if (mediaPlayer == null)
return;
mediaPlayer.setLooping(chbLoop.isChecked());
mediaPlayer.setOnCompletionListener(this);
}
private void showMessage(String text){
Toast toast = Toast.makeText(getApplicationContext(), text,
Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
private void releaseMP() {
if (mediaPlayer != null) {
try {
mediaPlayer.release();
mediaPlayer = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void onClick(View view) {
if (mediaPlayer == null)
return;
switch (view.getId()) {
case R.id.b_Pause:
if (mediaPlayer.isPlaying())
mediaPlayer.pause();
break;
case R.id.b_Resume:
if (!mediaPlayer.isPlaying())
mediaPlayer.start();
break;
case R.id.b_Stop:
mediaPlayer.stop();
break;
}
}
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
@Override
public void onCompletion(MediaPlayer mp) {}
@Override
protected void onDestroy() {
super.onDestroy();
releaseMP();
}
}
package com.example.lab5_4_media;
import java.io.File;
import java.util.ArrayList;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
public class GalleryActivity extends Activity {
int currentImage=0;
ArrayList<String> images;
ImageView imageView;
TextView nameView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gallery);
}
@Override
public void onResume(){
super.onResume();
currentImage=0;
Log.d("myLogs","onResume cI="+currentImage);
nameView=((TextView)findViewById(R.id.imageName));
images=new ArrayList<String>();
imageView=((ImageView)findViewById(R.id.image));
try{
File imagesDirectory=new File("/sdcard/TrainingMedia/");
images=searchImage(imagesDirectory);
updatePhoto(Uri.parse(images.get(currentImage)));
}catch(Exception e){
nameView.setText("Ошибка: Папка '/sdcard/TrainingMedia/' не найдена");
Log.d("myLogs","Ошибка");
}
}
@Override
protected void onPause()
{
super.onPause();
images.clear();
Log.d("myLogs","onPause cI="+currentImage);
}
private ArrayList<String> searchImage(File dir){
ArrayList<String> imagesFinded=new ArrayList<String>();
for(File f:dir.listFiles()){
if(!f.isDirectory()){
String fileExt=getFileExt(f.getAbsolutePath());
if(fileExt.equals("png") || fileExt.equals("jpg") || fileExt.equals("jpeg")){
Log.d("myLogs","Файл найден "+f.getAbsolutePath());
imagesFinded.add(f.getAbsolutePath());
}
}
}
return imagesFinded;
}
public static String getFileExt(String filename){
return filename.substring(filename.lastIndexOf(".") + 1);
}
public void updatePhoto(Uri uri){
try{
nameView.setText((currentImage+1)+"/"+images.size());
imageView.setImageURI(uri);
}catch(Exception e){
nameView.setText("Ошибка загрузки файла");
}
}
public void onNext(View v){
if(currentImage+1<images.size() images.size()>0){
currentImage++;
updatePhoto(Uri.parse(images.get(currentImage)));
}
}
public void onPrevious(View v){
if(currentImage>0 images.size()>0){
currentImage--;
updatePhoto(Uri.parse(images.get(currentImage)));
}
}
}
package com.example.lab5_4_media;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
OnClickListener btnClick=new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("myLogs",v.getId()+"");
Click(v.getId());
}
};
((ImageButton)findViewById(R.id.bMusic)).setOnClickListener(btnClick);
((ImageButton)findViewById(R.id.bCamera)).setOnClickListener(btnClick);
((ImageButton)findViewById(R.id.bGallery)).setOnClickListener(btnClick);
}
protected void Click(int view){
Intent intent=null;
Log.d("myLogs",view+"");
switch (view){
case R.id.bMusic: intent=new Intent(this,MediaActivity.class); break;
case R.id.bGallery: intent=new Intent(this,GalleryActivity.class); break;
case R.id.bCamera: intent=new Intent(this,CameraActivity.class); break;
default: break;
}
if(intent!=null){
Log.d("myLogs","Интент = "+intent.toString());
startActivity(intent);
}
}
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.