Главная страница
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 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
оригинальный pdf просмотр
ТипДокументы
#21061
страница35 из 55
КаталогОбразовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
1   ...   31   32   33   34   35   36   37   38   ...   55
Глава 22. Двухпанельные интерфейсы
Чтобы определить, интерфейс какого типа был заполнен, можно проверить кон- кретный идентификатор интерфейса. Впрочем, лучше проверить наличие в ма- кете detailFragmentContainer
. Такая проверка будет более точной и надежной.
Имена файлов могут изменяться, и вас на самом деле не интересует, по какому файлу заполнялся макет; необходимо знать лишь то, имеется ли у него контейнер detailFragmentContainer для размещения
CrimeFragment
Если макет содержит detailFragmentContainer
, мы создадим транзакцию фраг- мента, которая удаляет существующий экземпляр
CrimeFragment из detail-
FragmentContainer
(если он имеется) и добавляет экземпляр
CrimeFragment
, который мы хотим там видеть.
В файле
CrimeListActivity.java реализуйте метод onCrimeSelected(Crime)
, который будет обрабатывать выбор преступления в любом варианте интерфейса.
Листинг 22.9. Условный запуск CrimeFragment (CrimeListActivity.java)
public void onCrimeSelected(Crime crime) {
if (findViewById(R.id.detailFragmentContainer) == null) {
// Запуск экземпляра CrimePagerActivity
Intent i = new Intent(this, CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, crime.getId());
startActivity(i);
} else {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment oldDetail = fm.findFragmentById(R.id.detailFragmentContainer);
Fragment newDetail = CrimeFragment.newInstance(crime.getId());
if (oldDetail != null) {
ft.remove(oldDetail);
}
ft.add(R.id.detailFragmentContainer, newDetail);
ft.commit();
}
}
Наконец, в классе
CrimeListFragment мы будем вызывать onCrimeSelected(Crime)
в тех местах, где сейчас запускается новый экземпляр
CrimePagerActivity
В файле
CrimeListFragment.java измените методы onListItemClick(…)
и onOptions-
ItemSelected(MenuItem)
так, чтобы в них вызывался метод
Callbacks.
onCrimeSelected(Crime)
Листинг 22.10. Активизация обратных вызовов (CrimeListFragment.java)
public void onListItemClick(ListView l, View v, int position, long id) {
// Получение объекта Crime от адаптера
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
// Запуск экземпляра CrimePagerActivity
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
startActivity(i);
mCallbacks.onCrimeSelected(c);
}

Активность: управление фрагментами
377
@TargetApi(11)
@Override public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, crime.getId());
startActivity(i);
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
mCallbacks.onCrimeSelected(crime);
return true;
}
}
При обратном вызове в onOptionsItemSelected(…)
содержимое списка также немед- ленно перезагружается после добавления нового преступления. Это необходимо, потому что на планшетах при добавлении нового преступления список остается видимым на экране (прежде его закрывал экран детализации).
Запустите приложение CriminalIntent на планшете. Создайте новое преступление; экземпляр
CrimeFragment добавляется и отображается в detailFragmentContainer
Затем просмотрите какое-либо старое преступление и убедитесь в том, что
Crime-
Fragment заменяется новым экземпляром.
Рис. 22.6. Главное и детализированное представления связаны между собой
Однако внесение изменений в преступление не приводит к обновлению списка.
В настоящее время список перезагружается только после добавления престу- пления и в
CrimeListFragment.onResume()
. При этом на планшетах экземпляр

378
Глава 22. Двухпанельные интерфейсы
CrimeListFragment остается видимым рядом с
CrimeFragment
CrimeListFragment не приостанавливается при появлении
CrimeFragment
, поэтому и возобновления не происходит.
Для решения этой проблемы мы используем другой интерфейс обратного вызова из
CrimeFragment
Реализация CrimeFragment.Callbacks
CrimeFragment определяет следующий интерфейс:
public interface Callbacks {
void onCrimeUpdated(Crime crime);
}
CrimeFragment будет вызывать метод onCrimeUpdated(Crime)
активности-хоста при сохранении любых изменений в
Crime
. Реализация onCrimeUpdated(Crime)
в
CrimeListActivity перезагружает список
CrimeListFragment
Прежде чем браться за интерфейс в
CrimeFragment
, добавьте в
CrimeListFragment метод, который вызывается для перезагрузки списка
CrimeListFragment
Листинг 22.11. Добавление метода updateUI() (CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {
public void updateUI() {
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
}
В файле
CrimeFragment.java добавьте интерфейс обратного вызова, а также поле mCallbacks и реализации onAttach(…)
и onDetach()
Листинг 22.12. Добавление обратных вызовов CrimeFragment (CrimeFragment.java)
private ImageView mPhotoView;
private Button mSuspectButton;
private Callbacks mCallbacks;
/**
* Обязательный интерфейс для активности-хоста
*/
public interface Callbacks {
void onCrimeUpdated(Crime crime);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (Callbacks)activity;
}
@Override
public void onDetach() {
super.onDetach();

Активность: управление фрагментами
379
mCallbacks = null;
}
public static CrimeFragment newInstance(UUID crimeId) {
}
Затем реализуйте
CrimeFragment.Callbacks в
CrimeListActivity
, чтобы список перезагружался в onCrimeUpdated(Crime)
Листинг 22.13. Обновление списка преступлений (CrimeListActivity.java)
public void onCrimeUpdated(Crime crime) {
FragmentManager fm = getSupportFragmentManager();
CrimeListFragment listFragment = (CrimeListFragment)
fm.findFragmentById(R.id.fragmentContainer);
listFragment.updateUI();
}
В файле
CrimeFragment.java добавьте вызовы onCrimeUpdated(Crime)
при изменении заголовка или флага раскрытия
Crime
Листинг 22.14. Вызов onCrimeUpdated(Crime) (CrimeFragment.java)
@Override
@TargetApi(11)
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
mTitleField = (EditText)v.findViewById(R.id.crime_title);
mTitleField.setText(mCrime.getTitle());
mTitleField.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence c, int start, int before, int count)
{
mCrime.setTitle(c.toString());
mCallbacks.onCrimeUpdated(mCrime);
getActivity().setTitle(mCrime.getTitle());
}
});
mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
mSolvedCheckBox.setChecked(mCrime.isSolved());
mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
// Задание признака раскрытия преступления mCrime.setSolved(isChecked);
mCallbacks.onCrimeUpdated(mCrime);
}
});
return v;
}

380
Глава 22. Двухпанельные интерфейсы
Также необходимо вызвать onCrimeUpdated(Crime)
в методе onActivityResult(…)
, где могут измениться дата, фотография и подозреваемый в преступлении. В на- стоящее время фотография и подозреваемый не отображаются в представлении элемента списка, но класс
CrimeFragment все равно должен вести себя корректно и сообщать об этих обновлениях.
Листинг 22.15. Вызов onCrimeUpdated(Crime) (№2) (CrimeFragment.java)
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
Date date = (Date)data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
mCallbacks.onCrimeUpdated(mCrime);
updateDate();
} else if (requestCode == REQUEST_PHOTO) {
// Создание нового объекта Photo и связывание его с Crime
String filename = data
.getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
if (filename != null) {
Photo p = new Photo(filename);
mCrime.setPhoto(p);
mCallbacks.onCrimeUpdated(mCrime);
showPhoto();
}
} else if (requestCode == REQUEST_CONTACT) {
c.moveToFirst();
String suspect = c.getString(0);
mCrime.setSuspect(suspect);
mCallbacks.onCrimeUpdated(mCrime);
mSuspectButton.setText(suspect);
c.close();
}
}
CrimeListActivity теперь содержит реализацию
CrimeFragment.Callbacks
. Однако при попытке запустить CriminalIntent на телефоне произойдет сбой. Помните: любая активность, являющаяся хостом
CrimeFragment
, должна реализовать
CrimeFragment.
Callbacks
. Необходимо реализовать
CrimeFragment.Callbacks в
CrimePagerActivity
Для
CrimePagerActivity достаточно пустой реализации, в которой onCrime-
Updated(Crime)
не делает ничего. Когда
CrimePagerActivity является хостом
CrimeFragment
, необходимая перезагрузка списка уже выполняется в onResume()
Листинг 22.16. Пустая реализация CrimeFragment.Callbacks (CrimePagerActivity.java)
public class CrimePagerActivity extends FragmentActivity
implements CrimeFragment.Callbacks {
...
public void onCrimeUpdated(Crime crime) {
}
}

Для любознательных: подробнее об определении размера экрана
381
Запустите приложение CriminalIntent на планшете и убедитесь в том, что
List-
View обновляется при внесении изменений в
CrimeFragment
. Затем запустите его на телефоне и убедитесь в том, что приложение продолжает работать, как прежде.
Рис. 22.7. Изменения, внесенные в детализированном представлении, отражаются в списке
На этом наше знакомство с CriminalIntent подходит к концу. На протяжении 13 глав мы создали сложное приложение, которое использует фрагменты, взаимодействует с другими приложениями, делает снимки и сохраняет данные. Почему бы не от- праздновать окончание работы куском торта? Только не забудьте убрать за собой крошки, чтобы ваш проступок не попал в CriminalIntent.
Для любознательных: подробнее
об определении размера экрана
До выхода Android 3.2 для предоставления альтернативных ресурсов в зависимости от размера устройства использовался квалификатор размера экрана. Этот квалификатор группирует разные устройства на четыре категории — small
, normal
, large и xlarge
В табл. 22.1 приведены минимальные размеры для каждого квалификатора.
Таблица 22.1. Квалификаторы размера экрана
Имя
Минимальный размер экрана
small
320 x 426dp normal
320 x 470dp large
480 x 640dp xlarge
720 x 960dp

382
Глава 22. Двухпанельные интерфейсы
Квалификаторы размера экрана были объявлены устаревшими в Android 3.2. Они были заменены квалификаторами, позволяющими определять размеры устройства.
В табл. 22.2 перечислены эти новые квалификаторы.
Таблица 22.2. Дискретные квалификаторы размера экрана
Формат квалификатора
Описание
wXXXdp
Доступная ширина: ширина больше либо равна XXX dp hXXXdp
Доступная высота: высота больше либо равна XXX dp swXXXdp
Минимальная ширина: ширина или высота (меньшая из двух) больше либо равна XXX dp
Предположим, вы хотите задать макет, который должен использоваться только в том случае, если ширина экрана не менее 300dp. В этом случае можно поместить файл макета в каталог res/layout-w300dp
(«w» — сокращение от «width», то есть
«ширина»). То же самое можно сделать для высоты при помощи префикса «h»
(«Height», то есть «высота»).
Впрочем, ширина и высота могут меняться местами в зависимости от ориентации устройства. Для обнаружения конкретного размера экрана используется префикс
«sw» («Smallest Width», то есть «минимальная ширина»). Он задает наименьший размер экрана, которым в зависимости от ориентации устройства может быть как ширина, так и высота. Если размеры экрана равны 1024 × 800, то метрика sw равна
800. Если размеры экрана равны 800 × 1024, то метрика sw все равно равна 800.

Подробнее об интентах
и задачах
В этой главе мы используем неявные интенты для создания приложения-лаунчера, заменяющего стандартный лаунчер Android. Чтобы приложение работало правиль- но, нам придется углубить свое понимание интентов, фильтров интентов и схем взаимодействий между приложениями в среде Android.
Создание приложения NerdLauncher
Создайте новый проект (
New

Android
Application
Project
) с теми же параметрами, которые использовались для CriminalIntent (рис. 23.1). Присвойте проекту имя
NerdLauncher и создайте его в пакете com.bignerdranch.android.nerdlauncher
Создайте активность, но без пользовательского значка лаунчера. Выберите созда- ние новой пустой активности. Присвойте активности имя
NerdLauncherActivity и щелкните на кнопке
Finish
Класс
NerdLauncherActivity должен быть субклассом
SingleFragmentActivity
, по- этому этот класс необходимо добавить в проект. На панели
Package
Explorer найдите файл
SingleFragmentActivity.java в пакете
CriminalIntent
. Скопируйте его в пакет com.
bignerdranch.android.nerdlauncher
. При копировании файлов Eclipse автомати- чески обновляет объявления пакетов.
Нам также понадобится макет activity_fragment.xml
. Скопируйте res/layout/activity_frag- ment.xml в каталог res/layout проекта NerdLauncher.
NerdLauncher будет отображать список приложений на устройстве. Пользователь нажимает элемент списка, чтобы запустить соответствующее приложение. А теперь посмотрим, какие объекты для этого понадобятся.
23

384
Глава 23. Подробнее об интентах и задачах
Рис. 23.1. Создание проекта NerdLauncher
Класс
NerdLauncherFragment является субклассом
ListFragment
, а в качестве представления он будет использовать стандартный класс
ListView
, прилагаемый к
ListFragment
Создайте новый класс с именем
NerdLauncherFragment и назначьте его суперклассом android.support.v4.app.ListFragment
. Пока оставьте этот класс пустым.
Откройте файл
NerdLauncherActivity.java и измените суперкласс
NerdLauncherActivity на
SingleFragmentActivity
. Удалите код шаблона и переопределите метод create-
Fragment()
так, чтобы он возвращал
NerdLauncherFragment
Листинг 23.1. Субкласс SingleFragmentActivity (NerdLauncherActivity.java)
public class NerdLauncherActivity extends Activity SingleFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nerd_launcher);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_nerd_launcher, menu);
return true;
}
@Override
public Fragment createFragment() {
return new NerdLauncherFragment();
}
}

Обработка неявного интента
385
Обработка неявного интента
NerdLauncher отображает список приложений на устройстве. Для этого Nerd-
Launcher отправляет неявный интент, на который должна отреагировать главная активность каждого приложения. В интент включается действие
MAIN
и категория
LAUNCHER
. Вы уже видели следующий фильтр интентов в своих проектах:




В файле
NerdLauncherFragment.java переопределите метод onCreate(Bundle)
для создания неявного интента. Получите от
PackageManager список активностей, со- ответствующих интенту. Пока мы ограничимся простой регистрацией количества активностей, возвращенных
PackageManager
Листинг 23.2. Получение информации у PackageManager (NerdLauncherFragment.java)
public class NerdLauncherFragment extends ListFragment {
private static final String TAG = "NerdLauncherFragment";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent startupIntent = new Intent(Intent.ACTION_MAIN);
startupIntent.addCategory(Intent.CATEGORY_LAUNCHER);
PackageManager pm = getActivity().getPackageManager();
List activities = pm.queryIntentActivities(startupIntent, 0);
Log.i(TAG, "I've found " + activities.size() + " activities.");
}
}
Запустите приложение NerdLauncher и посмотрите в данных LogCat, сколько при- ложений вернул экземпляр
PackageManager
В CriminalIntent для отправки отчетов использовался неявный интент. Чтобы представить на экране список выбора приложений, мы создали неявный интент, упаковали его в объект выбора и отправили ОС вызовом startActivity(Intent)
:
Intent i = new Intent(Intent.ACTION_SEND);
... // Создание и размещение дополнений интентов i = Intent.createChooser(i, getString(R.string.send_report));
startActivity(i);
Почему мы не используем этот подход здесь? Вкратце дело в том, что фильтр интентов
MAIN/LAUNCHER
может соответствовать или не соответствовать неявному интенту
MAIN/LAUNCHER
, отправленному из startActivity(…)
Оказывается, вызов startActivity(Intent)
не означает «Запустить активность, соответствующую этому неявному интенту». Он означает «Запустить активность по умолчанию соответствующую этому неявному интенту». Когда вы отправляете

386
1   ...   31   32   33   34   35   36   37   38   ...   55

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