Это практическое занятие по первой лекции курса по архитектуре андроид приложений.

  • Чтобы выполнить практическое задание, нужно скачать проект LoaderWeather по ссылке.
  • Разархивируйте проект и откройте его в среде разработки Android Studio.
  • Описание практического задания находится в классе WeatherListActivity
  • Нужно загрузить погоду во всех городах при старте приложения
  • Сделать это наиболее быстрым способом
  • Добавить возможность обновления через SwipeRefreshLayout.
  • Реализовать обработку пересоздания Activity.

Как видим, здесь у нас есть уже готовый проект, который много чего умеет делать, нам только нужно его слегка модифицировать, добавить функциональность.

Если запустить приложение на устройстве, то мы увидим на первом экране список городов, а при переходе по пунктам списка открывается экран с детализированной информацией о погоде в указанном городе.

Наша задача – реализовать отображение погоды на первом экране, прямо в списке напротив каждого города.

Для загрузки погоды мы будем использовать лоадер, аналогично как это реализовано в классе WeatherActivity. Только мы будем использовать не RetrofitWeatherLoader,  а WeatherLoader, который здесь приготовлен заранее.

Если мы откроем разметку  экрана WeatherListActivity, то увидим, что используется старый добрый RecyclerView. А в разметке пункта списка уже есть поле для отображения температуры.

В пакете weatherlist видим 2 класса – CitiesAdapter и CityHolder.  В адаптере реализован метод changeDataSet, который принимает список объектов класса City, хранящих полученную информацию о погоде, обновляет список городов и вызывает метод notifyDataSetChanged(), который сигнализирует, что данные обновились и нужно обновить список.

В классе CityHolder в методе bind формируется строка для поля температуры через проверку условия, что информация о погоде присутствует в объекте City.

Приступим к реализации. В классе WeatherListActivity создадим приватный внутренний класс WeatherCallbacks, в реализующий интерфейс LoaderCallbacks абстрактного класса LoaderManager, с параметром City.

Нам нужно реализовать три метода. Вспоминаем материал лекции — в методе onCreateLoader вы должны вернуть нужный лоадер в зависимости от переданного id и используя аргументы в Bundle. В методе onLoadFinished в параметре City вам придет результат работы лоадера. В методе onLoaderReset вы должны очистить все данные, которые связаны с этим лоадером.

Нам понадобится переменная класса City и строковая переменная cityName. Будем их инициализировать в конструкторе.

Создадим два списочных массива:

Первый – для городов с загруженной информацией о погоде. Второй будем инициализировать в onCreate через метод getInitialCities(), который получает список городов из ресурсов.

Теперь в методе onCreateLoader класса WeatherCallbacks  проверяем, что параметр id меньше или равен размеру списка городов, и создаем для каждого города отдельный лоадер.

Далее в основном классе создаем метод loadWeather с такими параметрами: restart для определения, возвращать кешированные данные или загружать новые; объект класса City; строковый параметр cityName и параметр id лоадера.

Здесь будем отображать индикатор загрузки,  и вызывать WeatherCallbacks.

В зависимости от переданного флага определяем, какой метод вызывать, initLoader или restartLoader. Вызов restartLoader требуется, например, для обработки ошибки, когда нужно выполнить запрос повторно, и при обновлении данных (например, Callback от SwipeRefreshLayout).

В противном случае вызывается метод initLoader. Вспоминаем лекцию. Если лоадер еще не был создан, то метод initLoader создает и стартует его. Однако, если лоадер уже был создан, то при повторном вызове initLoader, LoaderManager не пересоздаст лоадер и не будет его перезапускать. Вместо этого он заменит экземпляр LoaderCallbacks на новый переданный, и если данные уже загрузились, передаст их в onLoadFInished. И при всем этом нам не нужно заниматься ручной проверкой на null для Bundle в onCreate. Мы просто можем вызывать initLoader и не беспокоиться о пересоздании, решая таким образом проблему обработки смены конфигурации.

Теперь напишем метод load(), который принимает список городов и флаг restart для метода loadWeather(), который мы будем здесь вызывать.

Мы в цикле для каждого города из списка вызываем метод loaderWeather(), которому отдаем флаг restart, объект класса City, строковую переменную cityName и идентификатор для лоадера. Здесь i увеличиваем на 1, поскольку id не может быть 0. Метод load() вызовем в onCreate() с передачей ему списочного массива городов и значения флага false, поскольку лоадеры будут создаваться в первый раз.

Теперь реализуем метод showWeather(), в который будем передавать результат запроса от сервера.

Здесь мы проверяем поля полученного объекта на null, и если хоть одно из них пустое, показываем пользователю ошибку.

Затем формируем списочный массив для адаптера. Как только массив городов заполнится, мы прячем индикатор загрузки и передаем массив методу changeDataSet() адаптера списка и очищаем массив от элементов.

Есть небольшая проблема, которая заключается в том, что мы получаем данные от сервера в случайном порядке. Чтобы города в списке не отображались вперемешку, добавим метод сортировки массива, который использует метод sort() класса Collections Ему мы передаем массив и компаратор, который и сортирует массив в методе compare() по полю Name каждого элемента.

По нажатию на кнопку действия вызываем метод load() с параметрами для повторной загрузки данных.

Теперь пропишем вызов метода showWeather() в методе onLoadFinished() нашего коллбека.

Нам осталось реализовать обновление списка с помощью SwipeRefreshLayout. Для начала добавим его в разметку макета activity_weather_list:

Не забудьте добавить идентификатор.

Теперь объявим компонент в классе WeatherListActivity и заодно свяжем его с разметкой при помощи аннотации @BindView (такую возможность нам предоставляет библиотека butterknife, кто не в курсе):

Затем инициализируем компонент в методе onCreate(), присвоим ему слушатель и определим цветовую схему:

Слушателем будет активити, поэтому нужно реализовать интерфейс SwipeRefreshLayout.OnRefreshListener. Также нам нужно переопределить метод этого интерфейса onRefresh().

В методе onRefresh() будем вызывать метод load():

В этот раз методу load() передаем true, поскольку нам нужно, чтобы данные загрузились заново. Далее мы создаем новый поток через Handler, чтобы анимация крутилась в фоне.

Запустим приложение, чтобы убедиться, что все работает как нужно. Теперь в списке отображается температура, работает обновление. При повороте экрана также все отрабатывает корректно. Вы можете экспериментировать, например, добавить другие города в ресурсы, чтобы увеличить список, также можно добавить свой город.

Весь код класса WeatherListActivity:

Скачать проект полностью можно по ссылке

Практика по лекции 1 Курса по архитектуре андроид-приложений обновлено: Июнь 14, 2017 автором: admin

  1. При создании объекта new WeatherLoader(WeatherListActivity.this, cityName) в методе public Loader onCreateLoader(int id, Bundle args) студия выдает ошибку «Incompatible type. Required: android.content.Loader. Found: ru.gdgkazan.simpleweather.screen.weather.WeatherLoader

    Подскажите, пожалуйста, что это?
    Спасибо

Добавить комментарий