Итак, в прошлом видео мы добавили адаптер, holder, генератор заглушечных данных в наш RecyclerView. И также остановились на том, что в этом видео мы добавим обновление данных и состояние ошибки. Что ж, меньше слов, больше дела, давайте этим займемся. Переходим в RecyclerFragment, прямо на разметку, и оборачиваем наш RecyclerView в так называемый SwipeRefreshLayout. Причем мы можем сделать это двумя способами. Либо мы можем добавить сюда SwipeRefreshLayout, либо мы поступим по-другому: мы просто переименуем LinearLayout, точнее, не переименуем, а поменяем на SwipeRefreshLayout. Так. Зададим ему id, id/refresher. Такое. Так, переходим во Fragment, добавляем refresher в качестве поля. SwipeRefreshLayout mSwipeRefreshLayout. Так, в onViewCreated создаем ссылку. mSwipeRefreshLayout = = view.findViewById(R.id.refresher) Отлично. Мы добавили SwipeRefreshLayout на разметку. Давайте теперь посмотрим, как он работает. Такой подход: сначала добавляем, потом смотрим, как оно работает. На самом деле SwipeRefreshLayout реализует стандартный механизм обновления данных в Android. И, скорее всего, вы с ним сталкивались. Если, будучи на экране, который я могу обновить, я потяну пальцем вниз, в нашем случае я просто зажму левую кнопку мыши, то я вижу, как из-под тулбара вот таким образом появляется индикатор прогресса, или же, как его еще называют, thumb. Если я сейчас дотяну до упора, отпущу палец либо левую кнопку мыши, я вижу, как thumb крутится, говоря пользователю о том, что идет процесс загрузки данных, обновления экрана, либо какой-то другой процесс, смотря что происходит на экране. И так как я добавил, так как я просто добавил SwipeRefreshLayout на разметку, но при этом никак его не обрабатываю, этот кругляш будет крутиться бесконечно. Давайте добавим обработку экрана. Точнее, обработку обновления экрана. Что нам для этого нужно сделать? Во-первых наш экран должен, точнее, наш фрагмент должен имплементить OnRefreshListener. Alt + Enter, Implement methods. Так, нам нужно реализовать метод onRefresh. Соответственно, onRefresh вызывается, когда я, либо вы, либо пользователь дергает пальцем по экрану и отпускает, тем самым вызывая загрузку. Чего мы хотели добиться? Мы хотели обновить данные. Давайте сымитируем, сымитируем доступ в сеть, то есть обновление данных будет происходить с некоторой ошибкой, с некоторой задержкой. mSwipeRefreshLayout.post (new Runnable(). А .post на любом view-элементе работает как любой handler на main thread, то есть просто постит Runnable-сообщение в MessageQueue. То, о чем я рассказывал. Так, что у нас будет в методе run? Во-первых, мы добавим данные в адаптер так, как это делали раньше. И для того чтобы было более наглядно, нагляднее, я удалю код добавления элементов в onActivityCreated и добавлю его сюда полностью. Я не хочу, чтобы было сгенерировано 20 элементов, пускай их будет пять. Так нам будет гораздо заметнее, что данные изменились. Помимо добавления данных, нам нужно еще спрятать индикатор прогресса. Как мы это делаем? Во-первых, мы проверяем, что индикатор прогресса видим: mSwipeRefreshLayout.isRefreshing то есть крутится прямо сейчас. И хайдим: mSwipeRefreshLayout. .setRefreshing(false). Давайте сделаем вместо post postDelayed, добавив тем самым задержку. Так, new Runnable. Так, запятая, четыре секунды. Нет, долго. Две секунды. Точка с запятой. И самое главное: нам нужно связать реализацию OnRefreshListener с нашим Layout. Делается это следующим образом: mRefreshLayout. .setOnRefreshListener, и в нашем случае это this. Запускаем. Проводим пальцем, в моем случае — мышкой. Две секунды, данные обновились. Проводим пальцем еще раз. Бам! Ошибка. Что происходит? Вместо обновления данных мы получили добавление данных к текущему. Это несколько неправильно, потому что SwipeRefreshLayout подразумевает именно обновление данных. Что нам нужно сделать? Нам нужно перейти в адаптер, в метод addData. И перед добавлением новых данных, нового List<Mock>, нужно очистить старый: mMockList. .clear. И после этого addAll. Ну-ка запустим. [ЗВУК] Обновляем. Так, данные получили. Обновляем еще раз. Данные поменялись. Это то, что нам нужно. Но иногда нам нужно и добавлять данные, и обновлять их. Как же поступить в этом случае? Самый простой и распространенный способ — это в addData, помимо самих новых данных, передавать еще и булево значение: boolean refresh. Соответственно, если мы вызываем вместе с данными еще и true, то обновляем наш список. Очищаем список, добавляем новые данные. Если же в addData передать false, то будет только добавление данных. Рекомендую взять на вооружение, кстати. Так, возвращаемся во Fragment и, так как в нашем случае нам нужно обновить данные, то я передаю, вместе с count передаю еще и, точнее, вместе с данными передаю еще и true. То есть «обновите». Так, реализацию обновления данных мы сделали. Теперь нам нужно сделать состояние ошибки. То есть вдруг запрос в сеть вернулся неудачным. Откуда нам не знать? Давайте добавим состояние ошибки для этого случая. Для начала создадим Layout, который будет показываться при ошибке: layout > New > Layout resource file. Родительским элементом у нас будет LinearLayout. Он будет простенький, простенький layout. Так, название. error. Сколько r в слове error? error_view, OK, yes, text. Добавим ImageView размерами 60 x 60. И TextView размерами match_parent, wrap_content. Текст какой-нибудь простенький. Например: что-то пошло не так. Попробуйте еще раз. Точка. И давайте загоним эту строку в Resource. Alt + Enter, extract string resource. error_text, OK. В ImageView неплохо было бы добавить иконку ошибки. Давайте попробуем это сделать. Добавим одну из системных иконок. Так. Правой кнопкой по drawable, new vector asset, щелкаем по андроиду и alert error. Я думаю, вполне себе подойдет, только название поменяем на просто icon error. Щелкаем Next, щелкаем Finish, щелкаем Yes. Так, открываем этот drawable и меняем fillColor на какой-нибудь термоядерный красный. [БЕЗ_ЗВУКА] Да, вот так. Можно чуть-чуть потемнее, на Choose. Можно ли загнать отсюда в Resources? Alt + Enter. Да, можно, Extract color resource. color_error_red. OK. Возвращаемся в ErrorView и добавляем source. И сделаем цвет текста тоже таким же красным. Color red. Сделаем его жирным, textStyle bold и сделаем всю эту красоту по центру, gravity "center". [БЕЗ_ЗВУКА] Поменяем вид на wrap_content. И добавим расстояние между текстом и иконкой: marginTop 8 dp. Хорошо. Теперь мы создали экран ошибки. Теперь нам нужно его добавить в экран с ресайклером. Да, кстати, нужно добавить id. id/error_view. Возвращаемся в fr_recycler и оборачиваем наш RecyclerView во FrameLayout. FrameLayout размерами с родителя, match_parent, match_parent. Ресайклер внутри. Ну а теперь помимо ресайклера во FrameLayout будет еще и state ошибки. Как добавить другой лэйаут в наш лэйаут? Можно воспользоваться нодой include. include_layout= error_view. Добавили. Что мы делаем дальше? Чтобы не было одновременного показа, мы даем RecyclerView visibility visible, тогда здесь ничего не поменяется, а error_view — visibility gone. То есть state ошибки на этом экране как бы есть, но его как бы нет. Забавно, мне кажется. Переходим в recycler fragment и, во-первых, инициализируем вьюху с ошибкой. private View Error View. В onViewCreated mErrorView=view. findViewById (R.Id. error_view); Дальше нам нужно добавить как бы кейс ошибки. И мне кажется, проще это сделать следующим образом. Мы создадим новую переменную для получения случайного количества элементов. private Random = new Random. Дальше int count = Random.nextInt допустим, из четырех — так будет больше шанс, что выпадет ноль. Дальше проверяем на как раз ноль. If count = 0, то показываем ошибку. Можем даже оформить это в виде отдельного метода. Show Error, else showData. Вне зависимости от того, случилась ошибка или нет, мы выбираем индикатор прогресса. Нужно создать методы showError и showData. Alt + Enter create method showError в recycler fragment. И что он из себя представляет. На самом деле все просто. ErrorView мы делаем видимым, а ресайклер мы делаем невидимым. Gone. Соответственно, showData то же самое, только наоборот. ErrorView мы делаем невидимым, а ресайклер делаем видимым. Так. Да и помимо всего прочего нам нужно еще добавить данные на экран с ресайклером. Вместо count мы уже передаем нашу переменную, и переменную неплохо было бы еще передавать в сам метод showData. int count и сюда count. [БЕЗ_ЗВУКА] Вот как-то так. Что же, давайте протестируем нашу логику. [БЕЗ_ЗВУКА] Ждем, пока загрузится. Куча конфликтов. Все-таки красный цвет в иконке — плохая идея. [БЕЗ_ЗВУКА] Один, ошибка. Данные, ошибка. Да, все работает, как надо, за исключением того момента, где я недосмотрел, что нельзя передавать ресурс цвет в сам вектор. Из-за того, что у нас min API неправильный, min API — слишком низкий. Ладно. В общем и целом логика работает, логика рабочая, так что в принципе можете пользоваться. Показ стейта ошибки именно таким образом — это один из способов, можно также менять фрагмент на другой фрагмент, можно стейт ошибки добавлять в качестве нового view holder в адаптер либо просто показывать диалог с ошибкой. Это уже на ваше усмотрение. Так, а что у нас по плану? Добавить обновление данных из состояния ошибки. Сделано. Хорошо. В следующем видео мы уже добавим загрузку данных с телефона книги. Это будет происходить с помощью контент-провайдера системного и с помощью лоадеров. В общем, встретимся в следующем видео. [БЕЗ_ЗВУКА]