Евгений Колесников 3 年之前
父節點
當前提交
768d848e36
共有 13 個文件被更改,包括 525 次插入47 次删除
  1. 252 34
      articles/android_bottom_navigation.md
  2. 46 0
      articles/android_tv.md
  3. 17 1
      articles/fragments.md
  4. 205 0
      articles/map_yandex.md
  5. 二進制
      img/bna_01.png
  6. 二進制
      img/bna_02.png
  7. 二進制
      img/bna_03.png
  8. 二進制
      img/bna_04.png
  9. 二進制
      img/mad_19.png
  10. 二進制
      img/mad_20.png
  11. 二進制
      img/mad_21.png
  12. 二進制
      img/tv_14.png
  13. 5 12
      readme.md

+ 252 - 34
articles/android_bottom_navigation.md

@@ -1,3 +1,11 @@
+<table style="width: 100%;"><tr><td style="width: 40%;">
+<a href="./fragments.md">Фрагменты
+</a></td><td style="width: 20%;">
+<a href="../readme.md">Содержание
+</a></td><td style="width: 40%;">
+<a href="./android_auth.md">Проект "база". Авторизация на сервере (Basic auth, token).
+</a></td><tr></table>
+
 # Навигация в приложении. Начало работы с Navigation Architecture Component. Знакомство с BottomNavigationView.
 
 >Содрано [отсюда](https://www.fandroid.info/17-android-bottom-navigation/)
@@ -10,18 +18,19 @@
 * [Создаем проект](#создаем-проект)
 * [Неоходимые библиотеки](#неоходимые-библиотеки)
 * [Граф навигации](#граф-навигации)
-* Добавление экранов
-* Хост навигации
-* BottomNavigationView
-* Добавление пунктов меню
-* Добаление идентификаторов в контроллер навигации
-* Рефакторинг
+* [Добавление экранов](#добавление-экранов)
+* [Хост навигации](#хост-навигации)
+* [BottomNavigationView](#bottomnavigationview)
+* [Добавление пунктов меню](#добавление-пунктов-меню)
+* [Добавление идентификаторов в контроллер навигации](#добавление-идентификаторов-в-контроллер-навигации)
+* [Рефакторинг](#рефакторинг)
+* [Использование активности в качестве destination](#использование-активности-в-качестве-destination)
 
 ## Navigation Architecture Component
 
 **Navigation Architecture Component** это библиотека, которая позволяет пользователям перемещаться между различными частями контента в вашем приложении. Компонент навигации входит в набор компонентов Android Jetpack и помогает реализовать навигацию, от простых нажатий кнопок до более сложных шаблонов, таких как панели приложений (appbars) и панель навигации (navigation drawer). Компонент навигации также обеспечивает согласованное и предсказуемое взаимодействие с пользователем.
 
-Navigation Architecture Component упрощает осуществление навигации, а также помогает визуализировать **navigation flow** вашего приложения. Библиотека предоставляет ряд преимуществ, в том числе:
+**Navigation Architecture Component** упрощает осуществление навигации, а также помогает визуализировать *navigation flow* вашего приложения. Библиотека предоставляет ряд преимуществ, в том числе:
 
 * Автоматическая обработка транзакций фрагментов
 * Корректная обработка кнопок «Вверх» и «Назад» по умолчанию
@@ -33,54 +42,56 @@ Navigation Architecture Component упрощает осуществление н
  
 ## Создаем проект
 
-Cоздать простое приложение с панелью навигации можно, не написав ни одной строчки кода. Достаточно воспользоваться готовым макетом Bottom Navigation Activity на этапе создания проекта в Android Studio. При этом создается проект приложения с нижней панелью навигации BottomNavigationView на главном экране, в которой отображается три пункта. При нажатии каждого из них меняются экраны приложения. Это все хорошо, скажете вы, но если нужно добавить или убрать экраны и пункты для них? Или добавить нижнюю навигацию в существующее приложение?
+Cоздать простое приложение с панелью навигации можно, не написав ни одной строчки кода. Достаточно воспользоваться готовым макетом **Bottom Navigation Activity** на этапе создания проекта в **Android Studio**. При этом создается проект приложения с нижней панелью навигации **BottomNavigationView** на главном экране, в которой отображается три пункта. При нажатии каждого из них меняются экраны приложения. Это все хорошо, скажете вы, но если нужно добавить или убрать экраны и пункты для них? Или добавить нижнюю навигацию в существующее приложение?
 
-Ок, давайте изучим структуру проекта, созданного по шаблону, и по ходу попробуем добавить сюда пару новых экранов и пунктов для них~~, а также рассмотрим шаги для добавления нижней панели навигации в уже существующее приложение~~.
+Ок, давайте изучим структуру проекта, созданного по шаблону, и по ходу попробуем добавить сюда пару новых экранов и пунктов для них.
 
-## Неоходимые библиотеки
+## Необходимые библиотеки
 
-Если открыть файл сборки build.gradle, в секции dependencies можно увидеть подключенные библиотеки из пакета androidx.navigation (с ktx  для проектов на kotlin)  которые нам уже знакомы по предыдущим урокам на тему навигации в приложении. Если вы добавляете нижнюю навигацию в существующее приложение, то начинать нужно с добавления этих библиотек в ваш проект.
+Если открыть файл сборки `build.gradle`, в секции **dependencies** можно увидеть подключенные библиотеки из пакета `androidx.navigation` (с ktx  для проектов на kotlin)  которые нам уже знакомы по предыдущим урокам на тему навигации в приложении. Если вы добавляете нижнюю навигацию в существующее приложение, то начинать нужно с добавления этих библиотек в ваш проект.
 
 ```
-implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
-implementation 'androidx.navigation:navigation-ui-ktx:2.4.1'
+implementation 'androidx.navigation:navigation-fragment-ktx:2.5.2'
+implementation 'androidx.navigation:navigation-ui-ktx:2.5.2'
 ```
 
 ## Граф навигации
 
-Архитектурный компонент **Navigation** позволяет упростить реализацию навигации между экранами в вашем приложении. По умолчанию, **Navigation** поддерживает фрагменты (Fragments) и активности (Activities) в качестве экранов назначения, но вы также можете добавить поддержку новых типов экранов назначения. Набор экранов назначения называется **навигационным графом (navigation graph) приложения**.
+Архитектурный компонент **Navigation** позволяет упростить реализацию навигации между экранами в вашем приложении. По умолчанию, **Navigation** поддерживает фрагменты (**Fragments**) и активности (**Activities**) в качестве экранов назначения. Набор экранов назначения называется **навигационным графом (navigation graph) приложения**.
 
-Помимо экранов назначения на навигационном графе есть соединения между ними, называемые действиями (actions). Следующий рисунок демонстрирует визуальное представление навигационного графа для простого приложения из шести экранов назначения, соединённых пятью действиями.
+Помимо экранов назначения на *навигационном графе* есть соединения между ними, называемые *действиями (actions)*. Следующий рисунок демонстрирует визуальное представление *навигационного графа* для простого приложения из шести экранов назначения, соединённых пятью действиями.
 
 ![](../img/android001.png)
 
 ### Обзор компонента навигации
+
 Компонент навигации состоит из трех ключевых частей:
 
-* **Navigation graph**: ресурс XML, который содержит всю связанную с навигацией информацию в одном централизованном месте. Он включает в себя все отдельные области содержимого в вашем приложении, называемые destinations (пункты назначения), а также возможные пути, которые пользователь может пройти через ваше приложение.
-* **NavHost**: Пустой контейнер, который отображает пункты назначения из вашего графика навигации. Компонент Navigation содержит реализацию NavHost по умолчанию — NavHostFragment, которая отображает фрагменты — места назначения.
-* **NavController**: Объект, который управляет навигацией приложения в NavHost. NavController управляет перемещениями контента мест назначения в NavHost, в процессе перемещения пользователей по приложению.
+* **Navigation graph**: ресурс XML, который содержит всю связанную с навигацией информацию в одном централизованном месте. Он включает в себя все отдельные области содержимого в вашем приложении, называемые *destinations (пункты назначения)*, а также возможные пути, которые пользователь может пройти через ваше приложение.
+* **NavHost**: Пустой контейнер, который отображает пункты назначения из вашего графика навигации. Компонент **Navigation** содержит реализацию **NavHost** по умолчанию — **NavHostFragment**, которая отображает фрагменты — места назначения.
+* **NavController**: Объект, который управляет навигацией приложения в **NavHost**. **NavController** управляет перемещениями контента мест назначения в **NavHost**, в процессе перемещения пользователей по приложению.
  
-Мы используем объект **NavController**, сообщая ему путь в ресурсе **Navigation Graph**. Затем объекту NavController будет показан соответствующий пункт назначения в NavHostFragment.
+Мы используем объект **NavController**, сообщая ему путь в ресурсе **Navigation Graph**. Затем объекту **NavController** будет показан соответствующий пункт назначения в **NavHostFragment**.
 
 Давайте посмотрим, как это выглядит на практике, начиная с нового ресурса **Navigation Graph**.
 
 ### Destinations
 
-Компонент навигации представляет концепцию **Destinations** —  пункта назначения. Пункт назначения — это любое место, в котором вы можете перемещаться в приложении, обычно это фрагмент или активити. Они поддерживаются «из коробки», но вы также можете создавать свои собственные типы назначения, если это необходимо.
+Компонент навигации представляет концепцию **Destinations** —  пункта назначения. Пункт назначения — это любое место, в которе вы можете перемещаться в приложении, обычно это *фрагмент* или *активность*. Они поддерживаются «из коробки».
 
 ### Navigation Graph
 
-Navigation Graph представляет собой новый тип ресурса, который определяет все возможные пути, доступные пользователю в приложении. Он показывает визуально все пункты назначения, которые могут быть достигнуты из данного пункта назначения. Редактор навигации Android Studio отображает Navigation Graph наглядно.
+**Navigation Graph** представляет собой новый тип ресурса, который определяет все возможные пути, доступные пользователю в приложении. Он показывает визуально все пункты назначения, которые могут быть достигнуты из данного пункта назначения. **Редактор навигации** Android Studio отображает **Navigation Graph** наглядно.
 
 ### Редактор навигации
 
 1. Откройте файл `res/navigation/mobile_navigation.xml` 
+
 1. Перейдите в режим «Дизайн»:
 
     ![](../img/android002.png)
 
-    Navigation Graph показывает доступные пункты назначения. Стрелки между пунктами назначения называются actions (действия). О них мы поговорим позже.
+    **Navigation Graph** показывает доступные пункты назначения. Стрелки между пунктами назначения называются *actions (действия)*. О них мы поговорим позже.
 
 1. В панели атрибутов (справа) можно посмотреть и отредактировать атрибуты пунктов назначения и действий.
 
@@ -93,11 +104,13 @@ Navigation Graph представляет собой новый тип ресу
 Вы увидите такой XML-код:
 
 ```xml
-<navigation xmlns:android="http://schemas.android.com/apk/res/android"
-            xmlns:app="http://schemas.android.com/apk/res-auto"
-            xmlns:tools="http://schemas.android.com/tools"
+<navigation 
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
     
-    app:startDestination="@+id/navigation_home">
+    app:startDestination="@+id/navigation_home"
+>
 
     <!-- здесь находятся теги с фрагментами и активностями -->
 
@@ -108,16 +121,17 @@ Navigation Graph представляет собой новый тип ресу
 
 * `<navigation>` является корневым узлом каждого навигационного графа.
 * `<navigation>` содержит один или несколько пунктов назначения, представленных элементами `<activity>` и/или `<fragment>`.
-* `app:startDestination`` является атрибутом, который указывает место назначения, которое запускается по умолчанию, когда пользователь впервые открывает приложение.
+* `app:startDestination` является атрибутом, который указывает место назначения, которое запускается по умолчанию, когда пользователь впервые открывает приложение.
 
-Давайте посмотрим на место назначения фрагмента:
+Давайте посмотрим на один из *destination*:
 
 ```xml
 <fragment
-        android:id="@+id/navigation_home"
-        android:name="ru.yotc.ui.home.HomeFragment"
-        android:label="@string/title_home"
-        tools:layout="@layout/fragment_home">
+    android:id="@+id/navigation_home"
+    android:name="ru.yotc.ui.home.HomeFragment"
+    android:label="@string/title_home"
+    tools:layout="@layout/fragment_home"
+>
     <argument
         .../>
 
@@ -140,8 +154,10 @@ Navigation Graph представляет собой новый тип ресу
 
 ### Добавление нового назначения в граф навигации
 
-1. Создайте новый фрагмент с именем **SettingsFragment** (в контекстном меню **res -> layout** выбрать **new fragment**)
+1. Создайте новый фрагмент с именем **SettingsFragment** (в контекстном меню `res -> layout` выбрать *new fragment*)
+
 1. Откройте файл `res/navigation/mobile_navigation.xml`
+
 1. Нажмите значок «Новый пункт назначения» и выберите «fragment_settings»
 
     ![](../img/android003.png)
@@ -158,4 +174,206 @@ Navigation Graph представляет собой новый тип ресу
     tools:layout="@layout/fragment_settings" />
 ```
 
-https://habr.com/ru/post/416025/
+## Добавление экранов
+
+Нажмите кнопку `New destination` вверху и выберите `Create new destination`. На экране добавления фрагментов выберите шаблон `Fragment (Blank)` (в оригинальной статье предлагают выбрать `Fragment with ViewModel`, но мы **ViewModel** не разбирали).  
+
+![](../img/bna_01.png)
+
+![](../img/bna_02.png)
+
+Добавьте один за другим два фрагмента – четвертый и пятый экран. Я их так и назову: **FourthFragment**  и **FifthFragment**.
+
+В граф навигации добавилось два фрагмента. Переименуйте их идентификаторы в соответствии с именами других пунктов назначения: `navigation_fourth` и `navigation_fifth`.
+
+![](../img/bna_03.png)
+
+Либо в режиме "кода":
+
+```
+<fragment
+    android:id="@+id/navigation_fourth"
+    android:name="info.fandroid.bottomnavapplication17.ui.fourth.FourthFragment"
+    android:label="@string/title_fourth"
+    tools:layout="@layout/fourth_fragment" />
+<fragment
+    android:id="@+id/navigation_fifth"
+    android:name="info.fandroid.bottomnavapplication17.ui.fifth.FifthFragment"
+    android:label="@string/title_fifth"
+    tools:layout="@layout/fifth_fragment" />
+```
+
+Можно было оставить идентификаторы как было, но более правильно с точки зрения *Code Convention*, чтобы названия однотипных элементов были логически похожими. Да и находить их будет легче потом.
+
+Также измените значение параметра `android:label` — оно используется для заголовка экрана. Впишите сюда текст, который вы хотите видеть в заголовке соответствующего экрана, желательно сохранить его затем в строковых ресурсах. Для сохранения нажмите желтую лампочку и выберите пункт «Extract string resourse», а затем придумайте название строки, которая сохранится в файле res/values/strings.xml.
+
+На этом добавление экранов закончено. Но, если запустить приложение сейчас, они не будут отображаться, и нижняя панель навигации никак не изменится. Что ж, идем дальше.
+
+## Хост навигации
+
+Если мы откроем папку `layout`, то увидим макеты всех фрагментов – пунктов назначения, и макет главной активности.
+
+Макет `activity_main.xml` содержит компонент *fragment* – это хост навигации, в котором отображаются фрагменты – пункты назначения. Он связан с графом навигации через параметр `app:navGraph`.
+
+```xml
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingTop="?attr/actionBarSize">
+
+    <com.google.android.material.bottomnavigation.BottomNavigationView
+        android:id="@+id/nav_view"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="0dp"
+        android:layout_marginEnd="0dp"
+        android:background="?android:attr/windowBackground"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:menu="@menu/bottom_nav_menu" />
+
+    <fragment
+        android:id="@+id/nav_host_fragment_activity_main"
+        android:name="androidx.navigation.fragment.NavHostFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:defaultNavHost="true"
+        app:layout_constraintBottom_toTopOf="@id/nav_view"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:navGraph="@navigation/mobile_navigation" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
+```
+
+## BottomNavigationView
+
+Также здесь присутствует и компонент **BottomNavigationView**. Это и есть нижняя панель навигации. Компонент **BottomNavigationView** является частью библиотеки компонентов материального дизайна. 
+
+>Эта библиотека должна быть импортирована в файле сборки модуля (она уже есть в проекте по умолчанию):
+>
+>```txt
+>dependencies {    
+>    implementation 'com.google.android.material:material:<version>'    
+>}
+>```
+
+Рассмотрим компонент **BottomNavigationView** боле подробно.
+
+Параметр `android:background` содержит ссылку на атрибут `windowBackground` и делает фон панели такой же, как фон экрана.
+
+>На сайте `material.io` можно найти много полезных рекомендаций как по дизайну, так и по реализации компонентов визуального интерфейса. На странице *Bottom Navigation*, например, указано, что рекомендованное число элементов панели нижней навигации должно быть от трех до пяти. Подписи должны быть максимально короткими и состоять, по возможности, из одного слова.
+>
+>На вкладе implementation дается пример реализации нижней панели без графа навигации, а также примеры кастомизации и оформления фона и цвета пунктов нижней панели, добавления пунктам бейджей и т.д.
+
+## Добавление пунктов меню
+
+Но вернемся к нашему проекту и компоненту **BottomNavigationView** в макете `activity_main.xml`.
+
+Нас интересует последний параметр:
+
+```xml
+app:menu="@menu/bottom_nav_menu"
+```
+
+Он ссылается на файл `bottom_nav_menu.xml` в папке `res/menu`. Этот файл содержит описание пунктов меню нижней панели навигации. Следовательно, добавлять новые пункты следует здесь.
+
+```xml
+<?xml version="1.0" encoding="utf-8"?>
+<menu 
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:id="@+id/navigation_home"
+        android:icon="@drawable/ic_home_black_24dp"
+        android:title="@string/title_home" />
+
+    <item
+        android:id="@+id/navigation_dashboard"
+        android:icon="@drawable/ic_dashboard_black_24dp"
+        android:title="@string/title_dashboard" />
+
+    <item
+        android:id="@+id/navigation_notifications"
+        android:icon="@drawable/ic_notifications_black_24dp"
+        android:title="@string/title_notifications" />
+
+    <item
+        android:id="@+id/navigation_fourth"
+        android:icon="@drawable/ic_notifications_black_24dp"
+        android:title="Четвёртый" />
+
+</menu>
+```
+
+Добавим пару элементов `<item>` для четвертого и пятого фрагментов. Их идентификаторы должны совпадать с идентификаторами пунктов назначения в графе навигации. Также укажите соответствующие строковые ресурсы в качестве названия пунктов в `android:title`.
+
+## Добавление идентификаторов в контроллер навигации
+
+Осталось добавить идентификаторы пунктов навигации в конфигурацию контроллера навигации в классе **MainActivity**.
+
+```kt
+override fun onCreate(savedInstanceState: Bundle?) {
+
+    ...
+
+    val appBarConfiguration = AppBarConfiguration(
+        setOf(
+            R.id.navigation_home, 
+            R.id.navigation_dashboard, 
+            R.id.navigation_notifications,
+            R.id.navigation_fourth
+        )
+    )
+
+    ...
+}
+```
+    
+Переменная класса **АppBarConfiguration** содержит набор идентификаторов пунктов навигации и передается вместе с контроллером – хостом навигации – в функцию setupActionBarWithNavController. Это нужно для того, чтобы система считала каждый из пунктов назначения пунктом верхнего уровня. В противном случае каждый фрагмент будет отображать стрелку возврата назад в тулбаре слева, как это отображается на дочерних активити и фрагментах.
+
+Попробуйте убрать какой-либо из идентификаторов пунктов назначения из набора, переданного в переменную *appBarConfiguration*, запустите приложение и посмотрите на экран пропущенного пункта назначения, и вы поймете, о чем речь.
+
+Теперь, когда добавление закончено, запустите приложение. Все добавленные пункты меню должны отображаться в нижней панели навигации, и при нажатии вести на соответствующие экраны.
+
+## Рефакторинг
+
+Теперь можно навести порядок в проекте. Дело в том, что изначально в проекте фрагменты каждого экрана со своими **ViewModel** расположены в отдельных папках по имени каждого пункта меню нижней панели навигации, и все это лежит в папке `ui`. Новые же добавленные фрагменты попали просто в главный пакет. Нужно создать в папке `ui` новые пакеты по имени добавленных экранов. Затем нужно перенести туда добавленные фрагменты вместе с их привязанными `ViewModel`. Делается это простым перетаскиванием в дереве проекта, с открытием окна рефакторинга, в котором нужно подтвердить операцию.
+
+Теперь вы знаете, как добавить **Bottom Navigation** в приложение.
+
+## Использование активности в качестве destination
+
+В качестве пункта назначения навигации можно использовать и обычную активность. 
+
+Создайте как обычно активность.
+
+Затем в `графе навигации` добавьте активность:
+
+![](../img/bna_04.png)
+
+ <activity
+        android:id="@+id/navigation_five"
+        android:name="ru.yotc.bottom_navigation.FiveActivity"
+        android:label="activity_five"
+        tools:layout="@layout/activity_five" />
+
+---
+
+**Дополнительная литература:**
+
+[Реализация навигации в Android приложениях с помощью Navigation Architecture Component](https://habr.com/ru/post/416025/)
+
+<table style="width: 100%;"><tr><td style="width: 40%;">
+<a href="./fragments.md">Фрагменты
+</a></td><td style="width: 20%;">
+<a href="../readme.md">Содержание
+</a></td><td style="width: 40%;">
+<a href="./android_auth.md">Проект "база". Авторизация на сервере (Basic auth, token).
+</a></td><tr></table>

+ 46 - 0
articles/android_tv.md

@@ -951,6 +951,52 @@ val bundle = ActivityOptionsCompat
     mBackgroundManager.drawable = drawable
     ```
 
+### Реализация поиска
+
+В примере не написано, как реализовывать поиск. Попробуем разобраться сами:
+
+1. Нам нужно поле ввода, которое будет отображаться при клике на иконку поиска. 
+
+    В разметке главного окна `activity_main.xml` в тег **FrameLayout** добавьте **EditText**:
+
+    ```xml
+    <EditText
+        android:id="@+id/searchEditText"
+        android:layout_width="500dp"
+        android:layout_height="wrap_content"
+        android:elevation="100dp"
+        android:padding="20dp"
+        android:hint="Поиск по названию или описанию фильма"
+        android:textColor="#000000"
+        android:textColorHint="#000000"
+        android:background="@drawable/rect_rounded_filled"
+        android:layout_gravity="center"/>
+    ```
+
+    * атрибут *elevation* аналог *z-index*
+    * фон задан залитым прямоугольником со скруглёнными углами
+
+    Остальное на своё усмотрение (либо по ТЗ)
+
+    Должно получиться что-то подобное:
+
+    ![](../img/tv_14.png)
+
+    После настройки строки поиска скройте её: ` android:visibility="invisible"`. Показывать этот элемент мы будем при клике по значку поиска.
+
+1. В **MainFragment** 
+
+    * на уровне класса создайте переменную для этого элемента
+    * в методе *onActivityCreated* ДО вызова других элементов получите указатель на этот элемент `activity!!.findViewById(...)` (фрагмент работает в контексте активности)
+    * реализуйте обработчик клика для поиска
+
+        ```kt
+        setOnSearchClickedListener {
+            searchEditText.visibility = View.VISIBLE
+            searchEditText.requestFocus()
+        }
+        ```
+
 <!-- https://medium.com/@Marcus_fNk/building-an-android-tv-app-part-2-824766c1ddbe -->
 
 

+ 17 - 1
articles/fragments.md

@@ -1,3 +1,11 @@
+<table style="width: 100%;"><tr><td style="width: 40%;">
+<a href="./weather2.md">Проект погода
+</a></td><td style="width: 20%;">
+<a href="../readme.md">Содержание
+</a></td><td style="width: 40%;">
+<a href="./android_bottom_navigation.md">Android Navigation. Знакомство с BottomNavigationView.
+</a></td><tr></table>
+
 # Фрагменты
 
 >Содрано [отсюда](https://metanit.com/java/android/8.1.php)
@@ -372,4 +380,12 @@ button.setOnClickListener {
             frag1?.view?.findViewById<TextView>(R.id.textView)?.text = s
         }
         ...
-    ```
+    ```
+
+<table style="width: 100%;"><tr><td style="width: 40%;">
+<a href="./weather2.md">Проект погода
+</a></td><td style="width: 20%;">
+<a href="../readme.md">Содержание
+</a></td><td style="width: 40%;">
+<a href="./android_bottom_navigation.md">Android Navigation. Знакомство с BottomNavigationView.
+</a></td><tr></table>

+ 205 - 0
articles/map_yandex.md

@@ -0,0 +1,205 @@
+<table style="width: 100%;"><tr><td style="width: 40%;">
+<a href="../articles/android_auth.md">Проект "база". Авторизация на сервере (Basic auth, token)
+</a></td><td style="width: 20%;">
+<a href="../readme.md">Содержание
+</a></td><td style="width: 40%;">
+<a href="../articles/wear_os.md">Wear OS
+</a></td><tr></table>
+
+# Работа с картами
+
+>Этот вариант написан для [Яндекс.карт](https://yandex.ru/dev/maps/mapkit/doc/intro/concepts/about.html?from=mapkit)
+
+>Описание протокола API доступно по ссылке:
+>http://swagger.kolei.ru/?url=http://carsharing.kolei.ru/swagger/api.yml
+
+### Шаг 1. Получите ключ для работы с MapKit
+
+Переходим по [ссылке](https://developer.tech.yandex.ru/services/) (если у вас нет аккаунта yandex, то нужно сделать)
+
+Нажмите "Подключить API" и выберите пакет "MapKit - мобильный SDK".
+
+![](../img/mad_19.png)
+
+Заполните форму
+
+![](../img/mad_20.png)
+
+В итоге у вас будет ключ
+
+![](../img/mad_21.png)
+
+### Шаг 2. Установите библиотеку MapKit
+
+Создайте проект, откройте `build.gradle` **проекта**, В секции `buildscript/repositories` добавьте репозитории **Maven Central** и **Google Maven** (этот блок должен быть ДО секции `plugins`):
+
+```txt
+buildscript {
+    repositories {
+        mavenCentral()
+        maven {
+            url "https://maven.google.com/"
+        }
+    }
+}
+```
+
+Откройте файл `build.gradle` приложения (модуля). В секции *dependencies* добавьте зависимость:
+
+```txt
+implementation 'com.yandex.android:maps.mobile:4.2.2-lite'
+```
+
+### Шаг 3. Настройте библиотеку
+
+1. Добавьте карту на Activity (создайте активность для карты, если её ещё нет):
+
+    ```xml
+    <com.yandex.mapkit.mapview.MapView
+        android:id="@+id/mapview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+    ```
+
+1. Установите ваш API-ключ в методе *onCreate()* вашего наследника **Application** (судя по названию класса это должен быть наш класс MyApp, но я устанавливаю ключ в активности с картой):
+
+    ```kt
+    private lateinit var mapView: MapView  
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        MapKitFactory.setApiKey( !!!ТУТ ВАШ КЛЮЧ!!! )
+    ```
+
+1. Инициализируйте карту в методе *onCreate()* нужного **Activity**:
+
+    **Внимание!!! Инициализацию нужно делать до вызова *setContentView***
+
+    ```kt
+    MapKitFactory.setApiKey(apiKey!!)
+
+    MapKitFactory.initialize(this)
+
+    setContentView(R.layout.activity_main)
+
+    // получение указателя на визуальный элемент "карта"
+    mapView = findViewById(R.id.mapview)
+
+    // перемещение камеры в нужную точку
+    mapView.map.move(
+        CameraPosition(
+            Point(55.751574, 37.573856),
+            18.0f,  // зум
+            0.0f, 
+            0.0f
+        ),
+        Animation(Animation.Type.SMOOTH, 0f),
+        null
+    )
+    ```
+
+1. Передайте события *onStart* и *onStop* в **MapKitFactory** и **MapView**. Иначе **MapKit** не сможет отобразить карту и остановить обработку карты, когда **Activity** с картой становится невидимым для пользователя:
+
+    ```kt
+    override fun onStart() {
+        super.onStart()
+        MapKitFactory.getInstance().onStart()
+        mapView.onStart()
+    }
+
+    override fun onStop() {
+        mapView.onStop()
+        MapKitFactory.getInstance().onStop()
+        super.onStop()
+    }
+    ```
+
+На этом этапе приложение уже должно нормально запускаться и показывать карту где-то в реке Москве
+
+### Добавление маркеров на карту
+
+У яндекс карт, насколько я понял, нет стандарного значка для маркера текущей локации, все маркеры делаются одинаково из картинок (есть варианты и из фигур, но их рассматривать не будем). Добавьте в ресурсы приложения картинку для маркера текущей локации (в каталог `res/drawable`) и создайте маркер (в конструкторе, после камеры):
+
+```kt
+val imageProvider: ImageProvider = ImageProvider
+    .fromResource(
+        this, 
+        R.drawable.location_marker)
+
+mapView
+    .map
+        .mapObjects
+            .addPlacemark(
+                Point(
+                    55.751574, 
+                    37.573856),
+                imageProvider)
+```
+
+### Отображение детальной информации об объекте
+
+В яндекс картах я не нашёл аналога **InformationWindow** - окна, появляющегося при клике на маркер у гугла. Реализуем показ обычными визуальными элементами:
+
+1. Добавьте в разметку окна **LinearLayout** с необходимым содержимым. Задайте ему атрибут `visibility="gone"` (не показывать и не занимать место)
+
+1. В свойствах класса окна добавьте массив для созданных маркеров и указатель на обработчик клика (тапа) по маркеру:
+
+    ```kt
+    private var markerList = ArrayList<PlacemarkMapObject>()
+
+    private var markerTapListener: MapObjectTapListener? = null
+    ```
+
+1. В класс добавьте методы для задания обработчика и для добавления маркера:
+
+    ```kt
+    private fun setTapListener(listener: MapObjectTapListener) {
+        markerTapListener = listener
+    }
+
+    private fun addMarker(
+        latitude: Double,
+        longitude: Double,
+        @DrawableRes imageRes: Int,
+        userData: Any? = null
+    ): PlacemarkMapObject 
+    {
+        val marker = mapView.map.mapObjects
+            .addPlacemark(
+                Point(latitude, longitude),
+                ImageProvider
+                    .fromResource(
+                        this, 
+                        imageRes)
+        )
+        marker.userData = userData
+        markerTapListener?.let{ 
+            marker
+                .addTapListener(it) }
+        return marker
+    }
+    ```
+
+1. Задайте обработчик клика. Добавьте маркер на карту (в примере добавляется один в конструкторе, но вы получаете их из интернета, запросив список автомобилей)
+
+    ```kt
+    setTapListener { mapObject, point ->
+        detailInfo.visibility = View.VISIBLE
+        // тут задайте информацию о маркере (фото, модель...)
+        // mapObject.userData содержит экземпляр Car, который вы задали при создании маркера
+        true
+    }
+
+    // создайте экземпляр информации об автомобиле
+    val car = Car(...)
+
+    markerList.add(
+        addMarker(
+            lat, 
+            lon,
+            R.drawable.car, 
+            car
+        )
+    )
+    ```

二進制
img/bna_01.png


二進制
img/bna_02.png


二進制
img/bna_03.png


二進制
img/bna_04.png


二進制
img/mad_19.png


二進制
img/mad_20.png


二進制
img/mad_21.png


二進制
img/tv_14.png


+ 5 - 12
readme.md

@@ -297,38 +297,31 @@ https://office-menu.ru/uroki-sql Уроки SQL
 
 1. [Фрагменты](./articles/fragments.md)
 
-1. [Принципы навигации внутри и между андроид-приложениями](./articles/navigation.md)
-
-1. [ViewPager, Fragments, Tabs](./articles/pager.md)
-
 1. [Android Navigation. Знакомство с BottomNavigationView.](./articles/android_bottom_navigation.md)
 
 1. TODO по итогам курсов сюда сделать каршеринг [Проект "база". Авторизация на сервере (Basic auth, token).](./articles/android_auth.md)
 
-1. [Wear OS](./articles/wear_os.md)
-
-1. [Разбор заданий прошлых лет](./articles/f6_demo_1.md)
+1. [Работа с картами](./articles/map_yandex.md)
 
-1. [Фрагменты](./articles/fragments.md)
+1. [Wear OS](./articles/wear_os.md)
 
 1. [Android TV](./articles/android_tv.md)
 
-<!-- https://corochann.com/introduction-android-tv-application-hands-on-tutorial-1-32/#Android_TV_application_development_introduction -->
+1. [Разбор заданий прошлых лет](./articles/f6_demo_1.md)
 
 <!-- 
 
-1. [Использование регулярных выражений для разбора данных в любом формате](./articles/regex.md)
+1. [Принципы навигации внутри и между андроид-приложениями](./articles/navigation.md)
 
+1. [ViewPager, Fragments, Tabs](./articles/pager.md)
 
 TODO
 
-BottomNavigation + frames
 темы DayNight
 ViewPager
 swype вниз (обновление списка)
 tablayout
 
-кнопки с картинками (нижнее или боковое меню)(фрагменты)
 поиск (фильтрация) - строка поиска и выпадающий список
 операции с датой