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

Программирование под 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
ТипДокументы
#21061
страница26 из 55Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей

С этим файлом связано 55 файл(ов). Среди них: Khant_K_TCP_IP_Setevoe_administrirovanie_3-e_izdanie_1988.pdf, vk_gettoken, Adams_Rob_-_Khoroshiy_uvesisty_pinok_pod_zad.pdf, vk_gettoken и ещё 45 файл(а).
Показать все связанные файлы
1   ...   22   23   24   25   26   27   28   29   ...   55
Глава 16. Панель действий
Запуск CriminalListActivity
Рис. 16.10. FLAG_ACTIVITY_CLEAR_TOP в действии
Однако существует другой, более правильный способ реализации иерархической навигации, основанный на использовании вспомогательного класса
NavUtils и включении метаданных в манифест.
Начнем с метаданных. Откройте файл
AndroidManifest.xml
. Добавьте в объявление
CrimePagerActivity следующий атрибут, назначающий
CrimeListActivity его родителем.
Листинг 16.11. Добавление метаданных родительской активности (AndroidManifest.xml)
android:label="@string/app_name">

android:value=".CrimeListActivity"/>

Тег метаданных — своего рода «листок для заметок», приклеенный к активности.
Такие заметки хранятся в системном объекте
PackageManager
, и любой желающий может получить значение из «заметки», если он знает ее имя. Вы можете создавать собственные пары «имя-значение» и читать их данные по мере необходимости.
Эту конкретную пару определяет класс
NavUtils
, чтобы он мог узнать родителя заданной активности. Она особенно полезна в сочетании со следующим методом класса
NavUtils
:
public static void navigateUpFromSameTask(Activity sourceActivity)
В методе
CrimeFragment.onOptionsItemSelected(…)
сначала проверьте, существует ли родительская активность, обозначенная в метаданных, при помощи вызова
Na- vUtils.getParentActivityName(Activity)
. Если она существует, вызовите navigat eUpFromSameTask(Activity)
для перехода к родительской активности.
Листинг 16.12. Использование NavUtils (CrimeFragment.java)
@Override public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (NavUtils.getParentActivityName(getActivity()) != null) {

Альтернативная команда меню
279
NavUtils.navigateUpFromSameTask(getActivity());
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Если информация о родителе в метаданных отсутствует, отображать стрелку не нужно. Вернитесь к onCreateView(…)
и проверьте наличие родителя перед вызовом setDisplayHomeAsUpEnabled(true)
Листинг 16.13. Нет родителя — нет стрелки (CrimeFragment.java)
@TargetApi(11)
@Override public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (NavUtils.getParentActivityName(getActivity()) != null) {
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
}
Почему использование
NavUtils лучше самостоятельного запуска активности? Во- первых, код
NavUtils компактен и понятен. Кроме того, использование
NavUtils обеспечивает централизацию отношений между активностями в манифесте. Если эти отношения изменятся, достаточно изменить строку в манифесте вместо того, чтобы возиться с кодом Java.
Другое преимущество заключается в том, что иерархические отношения отделяются от кода фрагмента.
CrimeFragment может использоваться в разных активностях, которые могут иметь разных родителей;
CrimeFragment все равно будет работать правильно.
Запустите приложение CriminalIntent. Создайте преступление и нажмите на значке приложения, чтобы вернуться к списку преступлений. Может, из жалкой двух- уровневой иерархии CriminalIntent это и не очевидно, но метод navigateUpFromS
ameTask(Activity)
реализует функциональность «перехода наверх» и поднимает пользователя на один уровень к родителю
CrimePagerActivity
Альтернативная команда меню
В этом разделе все, что мы узнали о меню, совместимости и альтернативных ресур- сах, будет использовано для добавления команды меню, скрывающей и отобража- ющей подзаголовок в панели действий
CrimeListActivity

280
Глава 16. Панель действий
Создание альтернативного файла меню
Команда меню, применяемая к панели действий, не должна быть видимой пользо- вателям без панели действий. Следовательно, первым шагом должно быть создание альтернативного ресурса меню. Создайте в каталоге res проекта папку menu-v11
Скопируйте и вставьте файл fragment_crime_list.xml в эту папку.
В файле res/menu-v11/fragment_crime_list.xml добавьте команду меню
Show
Subtitle
, которая будет отображаться на панели действий при наличии свободного места.
Листинг 16.14. Добавление команды меню Show Subtitle (res/menu-v11/fragment_crime_list.xml)


android:icon="@android:drawable/ic_menu_add"
android:title="@string/new_crime"
android:showAsAction="ifRoom|withText"/>

android:title="@string/show_subtitle"
android:showAsAction="ifRoom"/>

Добавьте в метод onOptionsItemSelected(…)
обработку команды меню — включение подзаголовка на панели действия.
Листинг 16.15. Обработка команды меню Show Subtitle (CrimeListFragment.java)
@TargetApi(11)
@Override public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
return true;
case R.id.menu_item_show_subtitle:
getActivity().getActionBar().setSubtitle(R.string.subtitle);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Обратите внимание: мы только отменяем предупреждения Android Lint, а не заклю- чаем код панели действий в проверочную конструкцию. Проверка не нужна — этот код не может вызываться на старых устройствах, потому что команда меню
R.id.
menu_item_show_subtitle на них отображаться не будет.
Запустите приложение CriminalIntent на новом устройстве и включите подзаго- ловок. Затем запустите его на устройстве Froyo или Gingerbread (физическом или виртуальном). Нажмите кнопку меню и убедитесь в том, что команда
Show
Subtitle не отображается. Добавьте новое преступление и убедитесь в том, что приложение работает так же, как прежде.

«Да, и еще одно…»
281
Переключение текста команды
Теперь подзаголовок отображается, но текст команды меню остался неизменным:
Show
Subtitle
. Было бы лучше, если бы текст команды и функциональность команды меню изменялись в зависимости от текущего состояния подзаголовка.
В методе onOptionsItemSelected(…)
проверьте наличие подзаголовка при выборе команды меню и выполните соответствующие действия.
Листинг 16.16. Обработка в зависимости от наличия подзаголовка (CrimeListFragment.java)
@TargetApi(11)
@Override public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
return true;
case R.id.menu_item_show_subtitle:
if (getActivity().getActionBar().getSubtitle() == null) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
item.setTitle(R.string.hide_subtitle);
} else {
getActivity().getActionBar().setSubtitle(null);
item.setTitle(R.string.show_subtitle);
}
return true; default:
return super.onOptionsItemSelected(item);
}
}
Если панель действий не содержит подзаголовка, мы включаем подзаголовок и за- меняем текст команды меню на
Hide
Subtitle
. Если подзаголовок уже отображается, то он отключается, а команде меню возвращается текст
Show
Subtitle
Запустите приложение CriminalIntent и убедитесь в том, что подзаголовок успешно скрывается и отображается.
«Да, и еще одно…»
Программирование Android часто напоминает беседы с детективом Коломбо из сериала. Вы уже думаете, что все прошло как по маслу и у следствия нет претензий.
Но Android всегда поворачивается у двери и говорит: «Да, и еще одно…»
В нашем случае это повороты. Если отобразить подзаголовок, а потом повернуть устройство, подзаголовок исчезнет при создании интерфейса «с нуля». Для реше- ния этой проблемы нужно определить поле для признака видимости подзаголовка, а также сохранить
CrimeListFragment
, чтобы значение переменной не терялось при поворотах.
В файле
CrimeListFragment.java добавьте логическую переменную, затем в методе on-
Create(…)
сохраните
CrimeListFragment и инициализируйте переменную.

282
Глава 16. Панель действий
Листинг 16.17. Инициализация переменных и сохранение CrimeListFragment
(CrimeListFragment.java)
public class CrimeListFragment extends ListFragment { private ArrayList mCrimes;
private boolean mSubtitleVisible;
private final String TAG = "CrimeListFragment";
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mSubtitleVisible = false;
}
Затем в методе onOptionsItemSelected(…)
задайте эту переменную при обработке выбора команды меню.
Листинг 16.18. Присваивание переменной subtitleVisible при обработке команды меню (CrimeListFragment.java)
@TargetApi(11)
@Override public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
return true;
case R.id.menu_item_show_subtitle:
if (getActivity().getActionBar().getSubtitle() == null) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
mSubtitleVisible = true;
item.setTitle(R.string.hide_subtitle);
}
else {
getActivity().getActionBar().setSubtitle(null);
mSubtitleVisible = false;
item.setTitle(R.string.show_subtitle);
}
return true; default:
return super.onOptionsItemSelected(item);
}
}
Осталось проверить, может ли отображаться подзаголовок. В файле
CrimeListFrag- ment.java переопределите метод onCreateView(…)
и назначьте подзаголовок, если переменная mSubtitleVisible содержит true
Листинг 16.19. Подзаголовок назначается, если поле mSubtitleVisible истинно
(CrimeListFragment.java)
@TargetApi(11)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,

Упражнение. Пустое представление для списка
283
Bundle savedInstanceState) {
View v = super.onCreateView(inflater, parent, savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (mSubtitleVisible) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
}
}
return v;
}
Также необходимо проверить состояние подзаголовка в onCreateOptionsMenu(…)
и убедиться в том, что отображается правильный текст команды меню.
Листинг 16.20. Назначение текста команды меню с истинным значением mSubtitleVisible
(CrimeListFragment.java)
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
MenuItem showSubtitle = menu.findItem(R.id.menu_item_show_subtitle);
if (mSubtitleVisible && showSubtitle != null) {
showSubtitle.setTitle(R.string.hide_subtitle);
}
}
Запустите приложение CriminalIntent. Отобразите подзаголовок, поверните устройство. Подзаголовок должен появиться в воссозданном представлении, как и ожидалось.
Упражнение. Пустое представление для списка
В настоящее время при запуске CriminalIntent отображает пустой список — большую черную пустоту. Мы должны предоставить пользователям что-то для взаимодей- ствия при отсутствии элементов в списке.
Так как класс
ListView является субклассом
AdapterView
, он поддерживает специ- альный вариант
View
, называемый «пустым представлением» и существующий именно для таких ситуаций. Если задать пустое представление,
ListView будет ав- томатически переключаться между этим представлением при отсутствии элементов списка и отображением списка, если в нем есть хотя бы один элемент.
Для изменения пустого представления в коде используется следующий метод
AdapterView
:
public void setEmptyView(View emptyView)
Также можно создать в разметке XML макет, в котором задается как
ListView
, так и пустое представление. Если назначить им идентификаторы ресурсов
@android:id/
list и
@android:id/empty соответственно, будет использоваться функция автома- тического переключения.

284
Глава 16. Панель действий
Текущая реализация
CrimeListFragment не заполняет свой макет в onCreateView(…)
, но для реализации пустого представления в макете это необходимо. Создайте для
CrimeListFragment
XML-ресурс макета, использующий
FrameLayout в качестве корневого контейнера, с представлением
ListView и другим представлением
View
, которое будет использоваться для пустого списка.
Пусть в пустом представлении выводится сообщение (например, «Список пуст»).
Добавьте в представление кнопку, которая будет инициировать создание нового преступления, чтобы пользователю не пришлось обращаться к командному меню или панели действий.

Сохранение и загрузка
локальных файлов
Почти каждому приложению необходимо место для хранения данных. В этой главе мы научим приложение CriminalIntent сохранять и загружать данные из файла
JSON, хранящегося в файловой системе устройства.
Каждое приложение на устройстве Android имеет каталог в своей песочнице (sand- box). Хранение файлов в песочнице защищает их от других приложений и даже любопытных глаз пользователей (если только устройство не было «взломано» — в этом случае пользователь сможет делать все, что ему заблагорассудится).
Песочница каждого приложения представляет собой подкаталог каталога
/data/data
, имя которого соответствует имени пакета приложения. Для CriminalIntent полный путь к каталогу песочницы имеет вид
/data/data/com.bignerdranch.android.criminalintent
Знать путь к песочнице полезно, но запоминать его не нужно; если понадобится, вы всегда сможете получить его при помощи вспомогательных методов API.
Кроме песочницы, ваше приложение может хранить файлы во внешнем хранилище.
Как правило, это SD-карта, которая может быть доступна (или не доступна) на устройство. На SD-карте могут храниться файлы и даже целые приложения, однако при этом приходится учитывать ряд факторов из области безопасности и програм- мирования. Самый важный момент заключается в том, что доступ к файлам на внешнем хранилище не ограничивается вашим приложением — любой желающий может читать, записывать и удалять их. В этой главе основное внимание будет уделяться внутреннему (закрытому) хранилищу, но тот же API при необходимости может использоваться для работы с файлами внешнего хранилища. (Собственно, упражнение в конце этой главы посвящено именно этой теме.)
Сохранение и загрузка данных в CriminalIntent
Поддержка долгосрочного хранения данных в приложении включает два процесса: сохранение данных в файловой системе и их загрузка при запуске приложения.
17

286
Глава 17. Сохранение и загрузка локальных файлов
Каждый процесс состоит из двух фаз. При сохранении данные сначала преобразу- ются в формат хранения, после чего результат записывается в файл. При загрузке все происходит наоборот: отформатированные данные сначала читаются из файла, а затем разбираются в формат, с которым работает приложение.
Для CriminalIntent форматом хранения будет формат JSON, а операции чтения и записи файлов осуществляются методами ввода-вывода класса Android
Context
На рис. 17.1 представлена общая схема реализации сохранения и загрузки данных в CriminalIntent.
crimes.json
(в «песочнице»)
Загрузка…
Сохранение…
Массив объектов Crime в CrimeLab
Разбор данных JSON и создание данных Crime
Чтение из файла
(методы ввода-вывода)
Форматирование данных
Crime в JSON
Запись в файл (методы ввода-вывода)
Рис. 17.1. Сохранение и загрузка в CriminalIntent
Формат JSON (JavaScript Object Notation) стал популярным в последнее время, особенно в области веб-служб. Android включает стандартный пакет org.json
, классы которого предоставляют средства для создания и разбора файлов в формате
JSON. В документации разработчика Android приведено описание org.json
, а более подробную информацию о формате JSON можно получить по адресу https://json.org.
(XML — другой способ форматирования данных для записи в файл. Android также предоставляет классы и методы для работы с XML. Примеры их использования при разборе XML приведены в главе 26.)
Для работы с файлами, доступными для вашего приложения, удобнее всего ис- пользовать методы ввода-вывода, предоставляемые классом
Context
(суперклассом всех ключевых компонентов приложений:
Application
,
Activity и
Service
, а также

Сохранение и загрузка данных в CriminalIntent
287
нескольких других). Эти методы возвращают экземпляры стандартных классов
Java — таких, как java.io.File или java.io.FileInputStream
Сохранение преступлений в файле JSON
В приложении CriminalIntent класс
CrimeLab будет отвечать за инициирование сохранения и загрузки данных, а механика создания и разбора объектов модели в формате JSON будет делегирована новому классу
CriminalIntentJSONSerializer и существующему классу
Crime
Создание класса CriminalIntentJSONSerializer
Обязанности по записи существующего списка
ArrayList объектов Crime в формат
JSON делегированы классу
CriminalIntentJSONSerializer
Создайте этот класс в пакете com.bignerdranch.android.criminalintent
. Оставьте его суперклассом java.lang.Object
Затем добавьте код, приведенный в листинге 17.1. Не забудьте добавить команды import для классов, необходимых для работы кода, командой Eclipse
Organize
Imports
Листинг 17.1. Реализация CriminalIntentJSONSerializer
public class CriminalIntentJSONSerializer {
private Context mContext;
private String mFilename;
public CriminalIntentJSONSerializer(Context c, String f) {
mContext = c;
mFilename = f;
}
public void saveCrimes(ArrayList crimes)
throws JSONException, IOException {
// Построение массива в JSON
JSONArray array = new JSONArray();
for (Crime c : crimes)
array.put(c.toJSON());
// Запись файла на диск
Writer writer = null;
try {
OutputStream out = mContext
.openFileOutput(mFilename, Context.MODE_PRIVATE);
writer = new OutputStreamWriter(out);
writer.write(array.toString());
} finally {
if (writer != null)
writer.close();
}
}
}
Хотя код сериализации можно включить непосредственно в класс
CrimeLab
, вы- деление логики сериализации данных в формат JSON в автономный модуль имеет

288
1   ...   22   23   24   25   26   27   28   29   ...   55
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей