LXF151:Android: Музыка на марше
Ssr (обсуждение | вклад) (Новая страница: «# Android: Музыка на марше Android Хорошо документированный API позволяет проигрывать файлы MP3 <blo…») |
Ssr (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
− | + | Android: Музыка на марше | |
− | Android Хорошо документированный API позволяет проигрывать файлы MP3 | + | Android Хорошо документированный API позволяет проигрывать файлы MP3 |
− | + | {{Врезка|Ширина=30%|Заголовок=Наш эксперт|Содержание=У Джульетты Кемп гораздо больше MP3-файлов, чем она не поленилась загрузить в эмулятор Android.}} | |
+ | |||
+ | :Часть 1: В первой из двух статей цикла '''Джульетта Кемп''' обращает свой взгляд на MediaPlayer и создает и запускает простой MP3-плейер. | ||
− | |||
Если вы программист, то, безусловно, оцените одну из лучших возможностей Android – удобный, хорошо задокументированный обширный API: огромное количество классов и интерфейсов, которыми можно воспользоваться с минимумом усилий. И если у вас нет веской причины этого не делать, лучше обойтись им, чем разворачивать собственные классы, отчасти ради экономии времени и усилий, а отчасти потому, что классы и интерфейсы API стабильно работают с устройствами Android. | Если вы программист, то, безусловно, оцените одну из лучших возможностей Android – удобный, хорошо задокументированный обширный API: огромное количество классов и интерфейсов, которыми можно воспользоваться с минимумом усилий. И если у вас нет веской причины этого не делать, лучше обойтись им, чем разворачивать собственные классы, отчасти ради экономии времени и усилий, а отчасти потому, что классы и интерфейсы API стабильно работают с устройствами Android. | ||
Строка 11: | Строка 12: | ||
<blockquote>Скорая помощь</blockquote><blockquote>Помните, что пример кода на DVD не компилируется «как есть»; нужно создать его либо как мы объяснили, либо с помощью ''Eclipse'', чтобы компилятор знал, где найти локальную установку SDK.</blockquote> | <blockquote>Скорая помощь</blockquote><blockquote>Помните, что пример кода на DVD не компилируется «как есть»; нужно создать его либо как мы объяснили, либо с помощью ''Eclipse'', чтобы компилятор знал, где найти локальную установку SDK.</blockquote> | ||
− | + | == Начальные установки: приступаем == | |
Я компилировала в версии 10, и она нужна по меньшей мере одному примененному здесь классу, но все базовые классы должны быть доступны и в более ранних версиях: | Я компилировала в версии 10, и она нужна по меньшей мере одному примененному здесь классу, но все базовые классы должны быть доступны и в более ранних версиях: | ||
Строка 122: | Строка 123: | ||
<code>}</code> | <code>}</code> | ||
− | + | == Создание ArrayAdapter == | |
Вернемся к updateMP3List(). Оператор if проверяет, есть ли в каталоге на SD-карте MP3-файлы. Если их нет, будет использоваться строка «empty». Хотя блок else пуст, стоит задокументировать, что это сделано сознательно – проще будет сопровождать код потом. | Вернемся к updateMP3List(). Оператор if проверяет, есть ли в каталоге на SD-карте MP3-файлы. Если их нет, будет использоваться строка «empty». Хотя блок else пуст, стоит задокументировать, что это сделано сознательно – проще будет сопровождать код потом. | ||
Строка 179: | Строка 180: | ||
<code>}</code> | <code>}</code> | ||
− | <code>});</code> | + | <code>});</code> |
+ | |||
+ | {{Врезка|Ширина=30%|Заголовок=Нет SD-карты?|Содержание=Если в системе нет SD-карты (т. е. нельзя не только «получить список MP3-файлов», но и «получить содержимое каталога»), вы получите NullPointerException. К сожалению, этого нельзя избежать проверками home.exists() или home.isDirectory(), так как каталог SD-карты виден и отображается как каталог, даже если на самом деле его не существует. Ошибка возникнет только при попытке получить список файлов. Вместо этого нужно поместить оператор if/else в блок try/catch: | ||
+ | <code>try {</code> | ||
+ | <code>if (home.listFiles [ ... ] ) { [ ... ] }</code> | ||
+ | <code>else { [ /// ] }</code> | ||
+ | <code>} catch (NullPointerException e) {</code> | ||
+ | <code>return;</code> | ||
+ | <code>}</code> | ||
+ | Код тихо завершится, и TextView использует строку «empty» с ID «empty». Пожалуй, можно слегка улучшить этот пример, показав в TextView сообщение о том, что SD-карта не найдена.}} | ||
+ | |||
+ | |||
+ | [[Файл:LXF151.code_android.lig_opt.png|thumb|Приложение, запущенное с темой ‘light’ — некоторым пользователям она может показаться нагляднее.]] | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | Опять же, API делает львиную долю работы, предоставляя метод setOnCompletionListener и класс OnCompletionListener. Нам остается написать метод nextMp3(): | ||
<code>private void nextMp3() {</code> | <code>private void nextMp3() {</code> |
Версия 07:19, 27 января 2018
|
|
|
Android: Музыка на марше Android Хорошо документированный API позволяет проигрывать файлы MP3
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
- Часть 1: В первой из двух статей цикла Джульетта Кемп обращает свой взгляд на MediaPlayer и создает и запускает простой MP3-плейер.
Если вы программист, то, безусловно, оцените одну из лучших возможностей Android – удобный, хорошо задокументированный обширный API: огромное количество классов и интерфейсов, которыми можно воспользоваться с минимумом усилий. И если у вас нет веской причины этого не делать, лучше обойтись им, чем разворачивать собственные классы, отчасти ради экономии времени и усилий, а отчасти потому, что классы и интерфейсы API стабильно работают с устройствами Android.
На двух следующих уроках мы коснемся API для работы со звуком/мультимедиа, который позволяет проигрывать файлы MP3 и других форматов. На этом уроке мы проиграем файлы, расположенные на SD-карте, а в следующем займемся потоковым воспроизведением. Мы предполагаем, что вы хотя бы отдаленно знакомы со средой разработки и развертывания программ для Android, и употребим командную строку вместо Eclipse – но если вы предпочтете Eclipse, он прекрасно подойдет.
Скорая помощь
Помните, что пример кода на DVD не компилируется «как есть»; нужно создать его либо как мы объяснили, либо с помощью Eclipse, чтобы компилятор знал, где найти локальную установку SDK.
Начальные установки: приступаем
Я компилировала в версии 10, и она нужна по меньшей мере одному примененному здесь классу, но все базовые классы должны быть доступны и в более ранних версиях:
android create project --target android-10 --name mp3 \\
--path ~/android/mp3 --activity AndroidMP3 --package com.example.androidmp3
Для первой версии нашего проекта мы просто возьмем список MP3-файлов, имеющихся на SD-карте. Сначала создайте list.xml в res/layout, чтобы настроить представление [View], которое будет использовать программа:
<?xml version=”1.0” encoding=”UTF-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”>
<ListView android:id=”@id/android:list”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:layout_weight=”1”
android:drawSelectorOnTop=”false”/>
<TextView android:id=”@id/android:empty”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:text=”@string/emptylist”/>
</LinearLayout>
Обратите внимание на автоматический компонент android:
empty – он задает текст, отображаемый, если список пуст (текст задайте в res/values/strings.xml). Нам также понадобится файл item.xml, определяющий, как будут отображаться элементы списка:
<?xml version=”1.0” encoding=”utf-8”?>
<TextView android:id=”@+id/title” xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”/>
Вернемся к файлу AndroidMP3.java. Базовый список MP3-файлов должен наследовать ListActivity. Создавая действие, зададим его представление и обновим список доступных MP3-файлов:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list);
updateMP3List();
}
Мы уже создали R.layout.list; теперь запишем updateMP3List():
private static final String MEDIA_PATH = new String(“/sdcard/”);
private List<String> mp3list = new ArrayList<String>();
[ ... ]
public void updateMP3List() {
File home = new File(MEDIA_PATH);
if ( home.listFiles( new Mp3Filter() ).length > 0 ) {
for ( File file : home.listFiles( new Mp3Filter() ) ) {
mp3list.add(file.getName());
}
ArrayAdapter<String> mp3ListAdapter =
new ArrayAdapter<String>(this, R.layout.item, mp3list);
setListAdapter(mp3ListAdapter);
}
else {
// No files here; ‘empty’ string will be seen
}
}
Строка MEDIA_PATH задается всему классу, а затем применяется для создания нового объекта File. Метод listFiles() возвращает массив файлов в заданный каталог, соответствующий фильтру. Стало быть, нужно также создать Mp3Filter – легко реализовав его как вспомогательный класс внутри главного:
class Mp3Filter implements FilenameFilter {
public boolean accept(File dir, String name) {
return (name.endsWith(“.mp3”));
}
}
Создание ArrayAdapter
Вернемся к updateMP3List(). Оператор if проверяет, есть ли в каталоге на SD-карте MP3-файлы. Если их нет, будет использоваться строка «empty». Хотя блок else пуст, стоит задокументировать, что это сделано сознательно – проще будет сопровождать код потом.
Если файлы есть, мы по очереди проходим их, добавляя в список. Затем мы должны объявить ArrayAdapter, чтобы работать со списком mp3List как с массивом, который Android может отобразить, пользуясь TextView из файла item.xml для каждого элемента. Это еще одна часть стандартного API Android, и мы будем применять ее часто. Она выступает как интерфейс между TextView и массивом объектов любого типа. (Ее можно использовать и как интерфейс для более сложных типов представлений, перегрузив ее метод getView(); здесь нам достаточно TextView.)
Раздобыв список MP3-файлов, нужно позаботиться, чтобы при щелчке пользователя на элементе списка что-то произошло – а именно, воспроизвелся данный MP3-файл. Следующий фрагмент кода использует стандартный метод onListItemClick:
private int currentPosition = 0;
[ ... ]
protected void onListItemClick(ListView list, View view, int position, long id) {
currentPosition = position;
playMp3(MEDIA_PATH + mp3List.get(position));
}
Мы записали текущее положение в currentPosition, чтобы знать, где мы находимся в списке, и вызвали метод playMp3() для воспроизведения нужного файла:
private MediaPlayer mp = new MediaPlayer();
[ ... ]
private void playMp3(String mp3Path) {
try {
mp.reset();
mp.setDataSource(mp3Path);
mp.prepare();
mp.start();
} catch (IOException e) {
Log.v(TAG, e.getMessage());
}
}
В начале класса устанавливается MediaPlayer, а затем почти всю работу выполняет предоставляемый Android API MediaPlayer, благодаря чему метод довольно ясен. Код сбрасывает MediaPlayer на случай, если кто-то еще одновременно проигрывает файл, или, если возникла ошибка, устанавливает источник данных (путь до файла, на котором щелкнул пользователь MP3); подготавливает проигрыватель и начинает воспроизведение MP3-файла. Вот и все, не считая блока catch (в нем мы просто выводим ошибку в файл журнала). Во врезке более подробно рассматриваются цикл жизни MediaPlayer и возможные состояния проигрывателя.
Но что делать проигрывателю, когда песня закончилась? Как насчет того, чтобы сразу перейти к следующей? Добавьте следующий код в конец блока try:
mp.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mplayer) {
nextMp3();
}
});
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
Опять же, API делает львиную долю работы, предоставляя метод setOnCompletionListener и класс OnCompletionListener. Нам остается написать метод nextMp3():
private void nextMp3() {
if (++currentPosition >= mp3List.size()) {
// No more songs! Reset currentPosition
currentPosition = 0;
} else {
playMp3(MEDIA_PATH + mp3List.get(currentPosition));
}
}
ВРЕЗКА Типы звуковых файлов
Типы звуковых файлов
Кроме MP3, Android поддерживает семь различных форматов звуковых файлов, в том числе AAC (.3gp, .mp4, и .m4a; сам .aac поддерживается только в Android 3.1+), FLAC (.flac) и Ogg Vorbis (.ogg). Наше приложение находит только MP3-файлы, что может не вполне удовлетворять вашим запросам.
Для получения списка всех доступных файлов без перечисления всех возможных расширений, можно посмотреть на класс MediaStore.Audio, и в частности, на контент-провайдер MediaStore.Audio.Media.EXTERNAL_CONTENT_URI. Android автоматически сканирует внешние SD-карты на наличие файлов мультимедиа, и для доступа к ним и для получения информации о них можно воспользоваться этим контент-провайдером.
Мы воспользуемся этим подходом в следующей статье, улучшая наш проигрыватель. Пока переменные и методы в этом классе названы так, чтобы было ясно, что мы берем с SD-карты только MP3-файлы.
Если MP3-файлы кончились, сверьтесь с размером списка и сбросьте currentPosition (и больше ничего не делайте; если вы хотите зациклить воспроизведение, можно перейти к проигрыванию первого MP3-файла). Иначе – вернитесь к методу playMP3 с путем до следующего MP3-файла.
Заметьте, что в операторе if мы употребили предварительный инкремент – это означает, что currentPosition увеличивается на единицу до проверки значения логического выражения. (В конструкции currentPosition++ >= mp3List.size() переменная увеличилась бы после этого, а нам такого не надо: мы проверяем, есть ли в списке следующая песня). Кроме того, когда мы дойдем до блока else, currentPosition уже увеличена на единицу. Важно четко понимать разницу между операторами пре- и постинкремента в Java.
Если на вашей SD-карте есть MP3-файлы, можно быстро проверить наш код – настроить устройство на USB-тестирование и установить программу прямо на него. Программу можно запустить и на эмуляторе, но тогда прежде всего понадобится установить SD-карту. Перейдите в каталог, где хранится карта, и скомандуйте
mksdcard -l 512MCard 512M mysdcard.img
Параметр -l необязателен – он задает метку диска для карты; размер и имя файла (с расширением .img) обязательны. После создания SD-карты запустите Android SDK/AVD Manager (android), выберите подходящее AVD (Android Virtual Device – виртуальное устройство Android) и измените его параметры, указав путь до SD-карты. Затем запустите AVD.
Ну, сейчас на карте нет MP3-файлов, и попробовав запустить программу, вы получите сообщение «На карте нет MP3-файлов [No MP3s on SD card]». Чтобы записать их на карту, воспользуйтесь командой adb push:
adb push getready.mp3 /sdcard/
(Чтобы эта команда сработала, эмулятор должен быть запущен.) Файлы можно добавить и через обозреватель файлов DDMS, но, по-моему, из командной строки это делается быстрее и удобнее.
Еще одно замечание по проверке: выбирайте MP3-файлы, запускаемые легко и быстро, для уверенности в том, что все работает должным образом.
ВРЕЗКА Скорая помощь
Для быстрого изменения стиля вашего приложения воспользуйтесь атрибутом android:theme элемента application в AndroidManifest.xml. Полный список доступных по умолчанию стилей можно найти на странице документации R.style.html.
Создаем несколько кнопок
На данном этапе нельзя понять, которая песня играет в данный момент; нет и кнопок паузы и перемещения между песнями. Давайте создадим три кнопки (воспроизведение/пауза, следующий трек, предыдущий трек) и текстовое поле для отображения песни, проигрываемой в данный момент.
Сейчас для упрощения работы переключим в list.xml представление расположения элементов с LinearLayout на RelativeLayout; прочие элементы оставим без изменений. Просто добавьте три кнопки и TextView:
<Button android:id=”@+id/playpause”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignParentBottom=”true”
android:text=”@string/playpause_button” />
<Button android:id=”@+id/next”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignParentBottom=”true”
android:layout_toRightOf=”@id/playpause”
android:text=”@string/next_button” />
<Button android:id=”@+id/prev”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignParentBottom=”true”
android:layout_toRightOf=”@id/next”
android:text=”@string/prev_button” />
<TextView android:id=”@+id/currentmp3”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_above=”@id/playpause”
android:text=”” />
ВРЕЗКА Состояния MediaPlayer
Важно знать, в каком состоянии MediaPlayer находится в каждый конкретный момент, потому что его текущее состояние влияет на последующее.
Цикл начинается с состояния Idle (либо после его создания, либо после вызова reset()), а заканчивается, после вызова release(), состоянием End. Обратите внимание, что хотя из состояния Idle нельзя вызывать довольно много методов (включая prepare() и start()), сообщения об ошибках, которые вы получите, если MediaPlayer был сброшен методом reset(), будут немного отличаться от аналогичных сообщений в случае, если MediaPlayer только что был создан.
По окончании работы с MediaPlayer важно вызвать release(), чтобы освободить все ресурсы.
Все кнопки выровнены по нижней части экрана (атрибут layout_alignParentBottom) и расположены одна за другой (атрибут layout_toRightOf). TextView находится над кнопками, но объявлять его нужно последним, иначе будет ошибка из-за ссылки на @id/playpause: ее нужно сперва объявить, а потом уж на нее ссылаться.
Вернемся к коду. Метод onCreate() вызывает метод setupButtons() – так код будет опрятнее. Все три кнопки настраиваются весьма похоже, в методе setupButtons(), и здесь я приведу код только для одной из них (код для других см. на DVD):
playpauseButton = (Button)this.findViewById(R.id.playpause);
playpauseButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mp.isPlaying()) {
pauseMp3();
} else {
playMp3(MEDIA_PATH + mp3List.get(currentPosition));
}
}
});
Опять же, большая часть работы с кнопками выполняется интерфейсом. Мы приостанавливаем или воспроизводим MP3-файл в зависимости от того, проигрывает ли MediaPlayer файл в данный момент или нет. Эту логику можно бы перенести в собственный метод playpause, но лучше разделять методы, особенно если вы захотите вызывать метод приостановки/воспроизведения из других частей кода.
У нас уже есть метод playMp3, но давайте отобразим в нем название песни, проигрываемой в данный момент, добавив следующую строку перед mp.start():
currentmp3.setText(mp3List.get(currentPosition));
Учтите, что она отображает имя файла песни, а не тэг MP3. Как получить метаданные файла, рассказано во врезке вверху справа.
Android и метаданные аудиофайлов
Для получения метаданных уже существует специальный класс MediaMetadataRetriever. Однако он доступен только в API начиная с уровня 10, т. е. 2.3.3/2.3.4, которым мы пользуемся в этом проекте. Если вы хотите отправить свою программу в свободное плавание, то захотите убедиться, что существует альтернатива для более ранних версий Android.
Заменим строку currentmp3.setText() в playMp3() на
setTrackInfo(mp3Path);
и создадим следующий метод setTrackInfo():
private void setTrackInfo(String mp3Path) {
TextView currentmp3 = (TextView)this.findViewById(R.id.currentmp3);
MediaMetadataRetriever metadata = new
MediaMetadataRetriever();
metadata.setDataSource(mp3Path);
String artist = metadata.extractMetadata
(MediaMetadataRetriever.METADATA_KEY_ARTIST);
String title = metadata.extractMetadata
(MediaMetadataRetriever.METADATA_KEY_TITLE);
currentmp3.setText(title + “ - “ + artist);
metadata.release();
}
Опять же, все весьма очевидно. Мы создаем свой класс MediaMetadataRetriever, передаем ему путь к файлу (MediaMetadataRetriever также может принимать источники данных других типов, таких как FileDescriptor или Uri) и забираем исполнителя и название песни из метаданных файла, чтобы записать их в TextView. Затем мы освобождаем созданный класс, чтобы зря не тратить ресурсы (помните, что мы создаем новый класс для каждого нового трека; можно было бы создать один на все время жизни приложения и освобождать его в методе onStop(), но нужды в этом нет – класс довольно легкий, и мы пользуемся им только в данном методе).
Конечно, если в файле нет корректных метаданных, произойдет ошибка (будет возвращен null), и нужно предусмотреть отход на случай, если и «исполнитель», и «трек» возвратят null (очевидный вариант – «название трека»).
Также можно применить метод getDuration() из MediaPlayer для получения длительности файла. Обратите внимание, что она выдается в миллисекундах, и вам придется преобразовать ее в минуты и секунды. Тут поможет java.util.concurrent.TimeUnit, но увы – он не сможет преобразовать время в форму минуты:секунды, вместо этого округлив все до ближайшей минуты. Попробуйте такой вариант:
long durationMillis = mp.getDuration();
long durationSecs = TimeUnit.SECONDS.convert(durationMillis, TimeUnit.MILLISECONDS);
long durationMins = TimeUnit.MINUTES.convert(durationMillis, TimeUnit.MILLISECONDS);
String duration = durationMins + “:” +
(durationSecs - durationMins*60);
currentmp3.setText(title + “ - “ + artist + “
(“ + duration + “)”);
Заметьте, что mp.getDuration() вызвать можно, поскольку setTrackInfo() вызывается уже после подготовки MediaPlayer – иначе появилось бы сообщение об ошибке.
Метод pauseMp3 до смешного прост:
private void pauseMp3() {
mp.pause();
}
Методы nextMp3() и prevMp3() также довольно просты – это вызовы playMp3() с новым текущим положением. Перекомпилируйте и запустите программу; теперь в ней должно быть текстовое поле с текущей песней (пока вы ничего не запустили, оно будет пустым) и три кнопки для перемещения взад-вперед по списку (а также выбора песни с помощью сенсорного экрана).
Одна из проблем в том, что если нажать паузу, а затем воспроизведение, файл MP3 будет проигрываться с самого начала, а не с места остановки. Это легко исправить, добавив приватную булевскую переменную pause. Измените начало блока try в playMp3() следующим образом:
if (pause == false) {
mp.reset();
mp.setDataSource(mp3Path);
mp.prepare();
currentmp3.setText(mp3List.get(currentPosition));
}
mp.start();
pause = false;
и добавьте строку pause = true в pauseMp3(). Вам также понадобится добавить строку pause = false в prevMp3() и nextMp3(), иначе при нажатии кнопок «предыдущий/следующий трек» в состоянии паузы вместо запуска следующей песни продолжит проигрываться текущая. Вот и все. (При желании можете добавить кнопку «стоп» с методом mp.stop().)
Другая проблема – если выйти из приложения, песня продолжит играть. Если приложение закрыто осознанно, нужно обработать эту ситуацию, применив сервис и передав нужные параметры службе фонового управления процессами. Мы займемся этим через месяц, а пока реализуем onStop(), чтобы корректно освободить ресурсы. Это особенно важно при работе с таким ресурсоемким классом как MediaPlayer, иначе может привести к недостатку памяти и/или ресурсов процессора и «падению» системы. К счастью, API помогает нам прибраться за собой:
protected void onStop() {
mp.release();
mp = null;
super.onStop();
}
КАРТИНКА LXF151.code_android.wit_opt.png Теперь мы добавили кнопки, информацию о треке и его продолжительность!
Учтите, что без вызова метода суперкласса вы получите ошибку времени выполнения. Этот метод освобождает MediaPlayer, и для перезапуска приложения придется создавать новый, в методе onResume():
protected void onResume() {
mp = new MediaPlayer();
updateMP3List();
super.onResume();
}
Обновлять список MP3-файлов не обязательно, но благодаря этому к списку прибавятся все новые файлы, появившиеся с момента последнего запуска приложения. Вздумав попробовать это сейчас, вы обнаружите, что при каждом перезапуске приложения список удваивается, так как он не очищается перед обновлением. Поэтому добавим строку в начало метода updateMP3List():
mp3List.clear();
Теперь наше приложение умеет проигрывать файлы, приостанавливать воспроизведение и перемещаться между песнями, и корректно освобождает ресурсы при выходе. На следующем уроке мы освоим запуск проигрывателя в качестве сервиса – его можно будет перевести в фоновый режим; мы узнаем, как с помощью MediaStore получить медиа-файлы и информацию о них, как установить листенер для обработки ошибок MediaPlayer и как работать с потоковым воспроизведением.