Главная страница
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
qrcode

Программирование под Android. Для профессионалов


Скачать 19.35 Mb.
НазваниеПрограммирование под Android. Для профессионалов
АнкорBrayn Khardi Bill Fillips - Programmirovanie po.
Дата23.05.2017
Размер19.35 Mb.
Формат файлаpdf
Имя файлаBrayn_Khardi_Bill_Fillips_-_Programmirovanie_po.pdf
оригинальный pdf просмотр
ТипДокументы
#21061
страница23 из 55
КаталогОбразовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
1   ...   19   20   21   22   23   24   25   26   ...   55
Глава 13. Воспроизведение звука и MediaPlayer
Откройте файл res/values-11/styles.xml и замените значение атрибута parent у
AppBas- eTheme на android:Theme.Holo
. Эта тема должна использоваться на всех устройствах с API 11 и выше, поэтому каталог res/values-14/
только мешает. Удалите его из про- екта HelloMoon.
Сохраните файлы и снова просмотрите макет. Теперь он должен иметь темный фон, который хорошо сочетается с графическим файлом.
Создание класса HelloMoonFragment
Создайте новый класс с именем
HelloMoonFragment и назначьте его суперклассом android.support.v4.app.Fragment
Переопределите метод
HelloMoonFragment.onCreateView(…)
, чтобы заполнить только что определенный макет и получить ссылки на кнопки.
Листинг 13.3. Исходная версия HelloMoonFragment (HelloMoonFragment.java)
public class HelloMoonFragment extends Fragment {
private Button mPlayButton;
private Button mStopButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_hello_moon, parent, false);
mPlayButton = (Button)v.findViewById(R.id.hellomoon_playButton);
mStopButton = (Button)v.findViewById(R.id.hellomoon_stopButton);
return v;
}
}
Использование фрагмента макета
В приложении CriminalIntent хостинг фрагментов осуществлялся добавлением их в код активности. В приложении HelloMoon вместо этого будет использоваться фрагмент макета, при использовании которого разработчик задает класс фрагмента в элементе fragment
Откройте файл activity_hello_moon.xml и замените его содержимое элементом frag- ment
, приведенным в листинге 13.4.
Листинг 13.4. Создание фрагмента макета (activity_hello_moon.xml)


android:id="@+id/helloMoonFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.bignerdranch.android.hellomoon.HelloMoonFragment">


Определение макета HelloMoonFragment
245
Прежде чем вы сможете запустить код, необходимо внести еще одно изменение в код
HelloMoonActivity
. Измените суперкласс
HelloMoonActivity
— им должен быть класс
FragmentActivity
:
Листинг 13.5. Преобразование HelloMoonActivity в FragmentActivity (HelloMoonActivity.java)
public class HelloMoonActivity extends Activity FragmentActivity {
/** Вызывается при исходном создании активности. */
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_moon);
}
}
Запустите приложение HelloMoon. На этот раз
HelloMoonActivity становится хостом представления
HelloMoonFragment
Вот и все, что необходимо для хостинга фрагмента макета. Мы указали имя класса фрагмента в макете, он был добавлен в активность и выведен на экран. А вот что при этом происходило «за кулисами»: когда класс вызвал метод
.setContentView(…)
и заполнил макет activity_hello_moon.xml
, он обнаружил элемент fragment
. В этой точке
FragmentManager создал экземпляр
HelloMoonFragment и добавил его в список. Затем он вызвал для
HelloMoonFragment метод onCreateView(…)
и поместил представление, возвращенное этим методом, в место, подготовленное тегом fragment
Выполнение
Приостановка
Остановка
Создание
(активность/фрагмент возвращается на передний план)
(активность/фрагмент снова становится видимой)
Уничтожение
Запуск
(завершение активности)
(все методы вызываются в setContentView() для фрагментов макетов)
Рис. 13.5. Жизненный цикл фрагмента макета
Чем приходится расплачиваться за эту простоту? Вы теряете гибкость и широту возможностей, характерные для прямой работы с
FragmentManager
:

246
Глава 13. Воспроизведение звука и MediaPlayer

Вы можете переопределять методы жизненного цикла фрагмента, чтобы реаги- ровать на события, но не можете управлять тем, когда эти методы вызываются.

Вы не можете закреплять транзакции, которые удаляют, заменяют или отсо- единяют фрагмент макета. Приходится довольствоваться тем, что было сделано при создании активности.

К фрагментам макетов нельзя присоединять аргументы. Присоединение аргу- ментов должно осуществляться после создания фрагмента и до его включения в
FragmentManager
. С фрагментами макетов все эти события вам недоступны.
Однако в простом приложении или в статической части сложного приложения использование фрагмента макета может быть вполне разумным.
Теперь, когда мы организовали хостинг
HelloMoonFragment
, обратимся к воспро- изведению аудио.
Воспроизведение аудио
Создайте в пакете com.bignerdranch.android.hellomoon новый класс с именем
AudioPlayer
. Оставьте его суперклассом java.lang.Object
В файле
AudioPlayer.java добавьте поле для хранения экземпляра
MediaPlayer и методы для остановки и воспроизведения этого экземпляра.
Листинг 13.6. Простой код воспроизведения аудио с использованием MediaPlayer (AudioPlayer.java)
public class AudioPlayer {
private MediaPlayer mPlayer;
public void stop() {
if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
}
public void play(Context c) {
mPlayer = MediaPlayer.create(c, R.raw.one_small_step);
mPlayer.start();
}
}
В методе play(Context)
вызывается метод
MediaPlayer.create(Context,
int)
Объект
Context нужен
MediaPlayer для опознания идентификатора ресурса аудио- файла. (Также существуют другие методы
MediaPlayer.create(…)
, используемые при получении аудио из других источников, например из Интернета или по ло- кальному URI.)
В методе
AudioPlayer.stop()
экземпляр
MediaPlayer освобождается, а полю mPlayer присваивается null
. Вызов
MediaPlayer.release()
уничтожает экземпляр.

Воспроизведение аудио
247
Уничтожение кажется слишком агрессивной интерпретацией «остановки», но для этого есть веские причины. Класс
MediaPlayer удерживает аудиооборудование и другие системные ресурсы до вызова release()
. Эти ресурсы совместно ис- пользуются всеми приложениями. Класс
MediaPlayer включает метод stop()
для перевода экземпляра
MediaPlayer в остановленное состояние, из которого он может быть перезапущен. Однако при простом воспроизведении аудиоданных корректнее уничтожить экземпляр вызовом release()
, а потом создать его заново.
Простое правило: удерживайте ровно один экземпляр
MediaPlayer и только на то время, в котором он что-то воспроизводит.
Для соблюдения этого правила мы внесем пару изменений в play(Context)
. Добавьте исходный вызов stop()
и заставьте слушателя вызывать stop()
при завершении воспроизведения.
Листинг 13.7. Только один экземпляр (AudioPlayer.java)
public void play(Context c) {
stop();
mPlayer = MediaPlayer.create(c, R.raw.one_small_step);
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
stop();
}
});
mPlayer.start();
}
}
Вызов stop()
в начале play(Context)
предотвращает возможное создание несколь- ких экземпляров
MediaPlayer
, если пользователь щелкнет на кнопке
Play повторно.
Вызов stop()
при завершении воспроизведения файла освобождает экземпляр
MediaPlayer
, как только он перестает использоваться.
Также вызов
AudioPlayer.stop()
необходимо включить в
HelloMoonFragment
, чтобы экземпляр
MediaPlayer не продолжал воспроизведение после уничтожения фраг- мента. В классе
HelloMoonFragment переопределите метод onDestroy()
и включите в него вызов
AudioPlayer.stop()
Листинг 13.8. Переопределение onDestroy() (HelloMoonFragment.java)
@Override
public void onDestroy() {
super.onDestroy();
mPlayer.stop();
}
}

248
Глава 13. Воспроизведение звука и MediaPlayer
Класс
MediaPlayer может продолжить воспроизведение после уничтожения
Hel- loMoonFragment
, потому что
MediaPlayer работает в другом программном потоке
(thread). Сейчас мы намеренно игнорируем этот многопоточный аспект HelloMoon.
Управление потоками более подробно рассматривается в главе 26.
Подключение кнопок воспроизведения и остановки
Вернитесь к файлу
HelloMoonFragment.java
. Пора заняться воспроизведением аудио: создайте экземпляр класса
AudioPlayer и назначьте слушателей для кнопок вос- произведения и остановки.
Листинг 13.9. Подключение кнопки Play (HelloMoonFragment.java)
public class HelloMoonFragment extends Fragment {
private AudioPlayer mPlayer = new AudioPlayer();
private Button mPlayButton;
private Button mStopButton;
@Override public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_hello_moon, parent, false);
mPlayButton = (Button)v.findViewById(R.id.hellomoon_playButton);
mPlayButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mPlayer.play(getActivity());
}
});
mStopButton = (Button)v.findViewById(R.id.hellomoon_stopButton);
mStopButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mPlayer.stop();
}
});
return v;
}
}
Запустите приложение HelloMoon, нажмите кнопку
Play и насладитесь живой историей.
В этой главе мы едва затронули тему использования
MediaPlayer
. За информацией о возможностях
MediaPlayer обращайтесь к руководству Android «MediaPlayer
Developer Guide» по адресу https://developer.android.com/guide/topics/media/mediaplayer.html
Упражнение. Приостановка воспроизведения
Предоставьте пользователю возможность приостановить воспроизведение аудио.
Описания необходимых методов можно найти в справочном описании класса
Me- diaPlayer

Упражнение. Воспроизведение видео в HelloMoon
249
Для любознательных: воспроизведение видео
В том, что касается воспроизведения видео, можно выбирать из нескольких вари- антов. Можно использовать класс
MediaPlayer
, как мы только что сделали. Для этого достаточно подключить место для воспроизведения.
Часто обновляемые изображения (как видео) в Android отображаются на виджете
SurfaceView
. Точнее, они отображаются на виджете
Surface
, хостом которого явля- ется
SurfaceView
. Чтобы получить доступ к
Surface
, следует получить экземпляр
SurfaceHolder для
SurfaceView
. Эта тема более подробно рассматривается в главе 19.
А пока достаточно знать, что подключение
SurfaceHolder к
MediaPlayer осущест- вляется вызовом
MediaPlayer.setDisplay(SurfaceHolder)
Часто для воспроизведения видео проще использовать экземпляр
VideoView
Класс
VideoView не взаимодействует с
MediaPlayer
, как
SurfaceView
. Однако он взаимодействует с
MediaController
, что позволяет легко организовать интерфейс воспроизведения.
Единственный нюанс с использованием
VideoView заключается в том, что класс не принимает идентификаторы ресурсов — только пути к файлам или объекты
Uri
. Чтобы создать объект
Uri
, ссылающийся на ресурс Android, используйте код следующего вида:
Uri resourceUri = Uri.parse("android.resource://" +
"com.bignerdranch.android.hellomoon/raw/apollo_17_stroll");
Создайте URI со схемой android.resource
, именем вашего пакета вместо хоста, ти- пом и именем вашего ресурса вместо пути. Результат может быть передан
VideoView
Упражнение. Воспроизведение видео в HelloMoon
Измените программу HelloMoon так, чтобы она также позволяла воспроизводить видеоролики. Если вы не загрузили файл apollo_17_stroll.mpg ранее, вернитесь к файлу решений и скопируйте его из проекта главы в каталог res/raw
. Затем воспроизведите его одним из описанных способов.

Сохранение фрагментов
В настоящее время приложение HelloMoon некорректно обрабатывает повороты.
Запустите его, включите воспроизведение и поверните устройство. Воспроизве- дение прерывается.
Дело в том, что при повороте
HelloMoonActivity уничтожается. Когда это происхо- дит,
FragmentManager отвечает за уничтожение
HelloMoonFragment
FragmentManager вызывает методы угасающего жизненного цикла фрагмента: onPause()
, onStop()
и onDestroy()
. В
HelloMoonFragment.onDestroy()
освобождается экземпляр
Media-
Player
, что приводит к остановке воспроизведения.
В главе 3 мы решили проблему обработки поворотов в приложении Geo-
Quiz переопределением
Activity.onSaveInstanceState(Bundle)
. Данные со- хранялись, а новая активность загружала их. Класс
Fragment содержит метод onSaveInstanceState(Bundle)
, который работает аналогичным образом. Однако сохранение состояния объекта
MediaPlayer и его последующее восстановление все равно прерывает воспроизведение и раздражает пользователей.
Сохранение фрагмента
К счастью, у фрагментов имеется механизм, благодаря которому экземпляр
Me- diaPlayer может «пережить» изменение конфигурации. Переопределите метод
HelloMoonFragment.onCreate(…)
и задайте свойство фрагмента.
14

Повороты и сохраненные фрагменты
251
Листинг 14.1. Вызов setRetainInstance(true) (HelloMoonFragment.java)
private Button mPlayButton;
private Button mStopButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
По умолчанию свойство retainInstance фрагмента содержит false
. Это означает, что при поворотах фрагмент не сохраняется, а уничтожается и создается заново вместе с активностью-хостом. Вызов setRetainInstance(true)
сохраняет фрагмент, который не уничтожается вместе с активностью, а передается новой активности в неизменном виде.
При сохранении фрагмента можно рассчитывать на то, что все его поля (включая mPlayButton
, mPlayer и mStopButton
) сохранят прежние значения. Вы к ним обра- щаетесь, а они просто находятся на своем месте.
Снова запустите HelloMoon. Включите воспроизведение, поверните устройство и убедитесь в том, что файл воспроизводится без прерывания.
Повороты и сохраненные фрагменты
Давайте поближе познакомимся с тем, как работают сохраненные фрагменты. Они используют то обстоятельство, что представление фрагмента может уничтожаться и создаваться заново без необходимости уничтожать сам фрагмент.
При изменении конфигурации
FragmentManager сначала уничтожает представле- ния фрагментов в своем списке. Представления фрагментов всегда уничтожаются и создаются заново по тем же причинам, по которым уничтожаются и создаются заново представления активности: в новой конфигурации могут потребоваться новые ресурсы. На случай, если для нового варианта существуют более подходящие ресурсы, представление строится «с нуля».
Затем
FragmentManager проверяет свойство retainInstance каждого фрагмента.
Если оно равно false
(по умолчанию),
FragmentManager уничтожает экземпляр фрагмента. Фрагмент и его представление будут созданы заново новым экземпля- ром
FragmentManager новой активности.

252
Глава 14. Сохранение фрагментов
До поворота
После поворота
Рис. 14.1. Реализация поворота по умолчанию с UI-фрагментом
С другой стороны, если значение retainInstance равно true
, представление фраг- мента уничтожается, но сам фрагмент остается. При создании новой активности новый экземпляр
FragmentManager находит сохраненный фрагмент и воссоздает его представление.
До поворота
Во время поворота
После поворота
Рис. 14.2. Поворот с сохраненным UI-фрагментом
Сохраненный фрагмент не уничтожается, но отсоединяется (detached) от «уми- рающей» активности. В сохраненном состоянии фрагмент все еще существует, но не имеет активности-хоста.

Сохранение фрагментов: действительно так хорошо?
253
Выполняется
Новый
Уничтоженный
Сохраненный
Создан
Сохранен?
Остановлен
Приостановлен
Рис. 14.3. Жизненный цикл фрагмента
Переход в сохраненное состояние происходит только при выполнении двух условий:

для фрагмента был вызван метод setRetainInstance(true)
;

активность-хост уничтожается для изменения конфигурации (обычно поворот).
Фрагмент находится в сохраненном состоянии очень недолго — от момента отсо- единения от старой активности до повторного присоединения к новой, немедленно создаваемой активности.
Сохранение фрагментов: действительно
так хорошо?
Сохраненные фрагменты: удобно, верно? Да! Действительно удобно. На первый взгляд они решают все проблемы, связанные с уничтожением активностей и фраг- ментов при поворотах. При изменении конфигурации устройства для подбора наи- более подходящих ресурсов создается новое представление, а в вашем распоряжении имеется простой способ сохранения данных и объектов.

254
Глава 14. Сохранение фрагментов
Тогда почему не сохранять все фрагменты подряд и почему фрагменты не сохра- няются по умолчанию? Похоже, Android без энтузиазма относится к сохранению фрагментов в пользовательских интерфейсах. Мы не знаем, почему это так, но если группа Android ни во что не ставит эту возможность, в будущем могут возникнуть проблемы.
Помните, что сохраненные фрагменты продолжают существовать только при унич- тожении активности при изменения конфигурации. Если активность уничтожается из-за того, что ОС потребовалось освободить память, то все сохраненные фрагменты также будут уничтожены.
Повороты и onSaveInstanceState(Bundle)
Метод onSaveInstanceState(Bundle)
— еще один инструмент, используемый для обработки поворотов. Собственно, если у вашего приложения нет проблем с поворо- тами, то только благодаря работе поведения onSaveInstanceState(…)
по умолчанию.
Хорошим примером служит приложение CriminalIntent. Фрагмент
CrimeFragment не сохраняется, но если внести изменения в краткое описание преступления или переключить флажок, новые состояния объектов
View автоматически сохраняются и восстанавливаются после поворота. Метод onSaveInstanceState(…)
проектиро- вался именно для решения этой задачи — сохранения и восстановления состояния пользовательского интерфейса приложения.
Главное отличие между переопределением
Fragment.onSaveInstanceState(…)
и со- хранением фрагмента — продолжительность существования сохраненных данных.
Если данные должны только пережить изменения конфигурации, сохранение фрагмента потребует существенно меньшей работы. Это особенно справедливо при сохранении объекта; разработчику не нужно беспокоиться о том, реализует объект
Serializable или нет.
Но если данные должны существовать дольше, сохранение фрагмента не помо- жет. Если активность уничтожается для освобождения памяти после бездействия пользователя, все сохраненные фрагменты уничтожаются так же, как и их несо- храненные родственники.
Чтобы разница стала более наглядной, вспомните приложение GeoQuiz. Проблема поворота заключалась в том, что индекс вопроса обнулялся при повороте. На каком бы вопросе ни находился пользователь, при повороте устройства он возвращался к первому вопросу. Чтобы пользователь видел правильный вопрос, мы сохраняли значение индекса, а затем снова загружали его.
В приложении GeoQuiz не использовались фрагменты, но представьте себе пере- работанную версию GeoQuiz с фрагментом
QuizFragment
, хостом которого является
QuizActivity
. Как быть — переопределить
Fragment.onSaveInstanceState(…)
для сохранения индекса или сохранить
QuizFragment и оставить переменную живой?
На рис. 14.4 показаны три разных жизненных цикла, с которыми вам придется работать: жизнь объекта активности (и его несохраненных фрагментов), жизнь сохраненного фрагмента и жизнь записи активности.

Повороты и onSaveInstanceState(Bundle)
255
Уничтожения
Запись активности
Объект активности
Сохраненный фрагмент активности
(процесс завершается)
Повороты
Рис. 14.4. Три жизненных цикла
Срок жизни объекта активности слишком мал; это и является источником проблемы поворота. Индекс определенно должен пережить объект активности.
Если сохранить
QuizFragment
, индекс будет существовать на протяжении срока жизни сохраненного фрагмента. Если GeoQuiz содержит только пять вопросов, сохранение
QuizFragment проще реализуется и требует меньше кода. Мы инициали- зируем индекс в поле, а затем вызываем setRetainInstance(true)
в
QuizFragment.
onCreate(…)
Листинг 14.2. Сохранение гипотетического фрагмента QuizFragment public class QuizFragment extends Fragment {
private int mCurrentIndex = 0;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
}

256
1   ...   19   20   21   22   23   24   25   26   ...   55

перейти в каталог файлов

Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей

Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей