Merge pull request #42 from TouchInstinct/feature/documentation
First part of documentation
This commit is contained in:
commit
9249db7b0b
83
README.md
83
README.md
|
|
@ -1 +1,82 @@
|
|||
# RoboSwag
|
||||
# RoboSwag
|
||||
Roboswag - библиотека решений, ускоряющих разработку Android приложений. Она включает в себя архитектурные решения для построения приложения, утилитарные классы и общие инструменты, которые используются в компании Touch Instinct.
|
||||
Библиотека состоит из gradle модулей. Каждый модуль отвечает за свой функционал. В проектах используются только те модули, которые нужны. Такая модульность позволяет сохранять размер приложения небольшим и ускорять сборку проекта.
|
||||
|
||||
## Минимальные требования
|
||||
|
||||
* Andoroid Api: 19
|
||||
* Kotlin: 1.3.11
|
||||
* Gradle: 3.2.1
|
||||
* Gradle CPD Plugin: 1.1
|
||||
* Detekt Plugin: 1.0.0-RC12
|
||||
|
||||
## Основная архитектура
|
||||
За основу архитектуры взят подход от Google - MVVM на основе [Android Architecture Components](https://developer.android.com/jetpack/docs/guide). Данный подход популярен в сообществе Android разработки, позволяет разбивать код на мелкие и независимые части, что ускоряет разработку и последующую поддержку приложения.
|
||||
Для организации многопоточности используется фреймворк [RxJava2](https://github.com/ReactiveX/RxJava). RxJava - обширный инструмент, реализующий концепции реактивного программирования. Сочетание этой концепции с возможностью выносить задачи на другой поток позволяет легко писать многопоточное асинхронное приложение.
|
||||
В качестве Di-фреймворка выбран [Dagger 2](https://github.com/google/dagger). Он позволяет сделать код приложения менее связным, более гибким и позволяет легко настроить автотестирование.
|
||||
Roboswag позволяет сочетать эти три решения в одну гибкую и удобную архитектуру. Разработка становится быстрее, проще и надежнее. За архитектуру отвечают модули [lifecycle](/lifecycle) и [lifecycle-rx](/lifecycle-rx).
|
||||
|
||||
## Основные инструменты библиотеки
|
||||
### Работа с RecyclerView
|
||||
RecyclerView - один из самых часто используемых инструментов Android разработчика. Модуль [recyclerview-adapters](/recyclerview-adapters) позволяет сделать работу с RecyclerView более гибкой и делает работу самого элемента быстрее.
|
||||
### BuildScripts
|
||||
[BuildScrpts](https://github.com/TouchInstinct/BuildScripts) - набор скриптов, автоматизирующих разработку. Один из главных скриптов - staticAnalysis - инструмент для автоматической проверки кода на соответствие правилам компании.
|
||||
### Api Generator
|
||||
Внутренний инструмент компании Touch Instinct для генерации общего кода на разные платформы - Android, iOS и Server. Описанные в одном месте общие классы и Http методы используются на разных платформах. Данный инструмент позволяет сократить время разработки в два раза.
|
||||
### Работа с SharedPreferences
|
||||
Чтобы сохранять простые данные в память смартфона, используются SharedPreferences. Модуль [storable](/storable) разработан для облегчения работы с SharedPreferences.
|
||||
### Утилиты и extension функции
|
||||
В Roboswag также есть много [утилитарных](/utils) классов и [extension](/kotlin-extensions) функций, которые позволяют писать часто используемый код в одну строку.
|
||||
|
||||
### Подключение
|
||||
|
||||
#### .gitmodules
|
||||
|
||||
```
|
||||
[submodule "RoboSwag"]
|
||||
path = RoboSwag
|
||||
url = git@github.com:TouchInstinct/RoboSwag.git
|
||||
```
|
||||
|
||||
#### build.gradle (Module: app)
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation project(':utils')
|
||||
implementation project(':views')
|
||||
implementation project(':storable')
|
||||
implementation project(':logging')
|
||||
implementation project(':api-logansquare')
|
||||
implementation project(':lifecycle')
|
||||
implementation project(':lifecycle-rx')
|
||||
implementation project(':navigation')
|
||||
implementation project(':templates')
|
||||
implementation project(':recyclerview-adapters')
|
||||
implementation project(':recyclerview-calendar')
|
||||
implementation project(':kotlin-extensions')
|
||||
implementation project(':livedata-location')
|
||||
}
|
||||
```
|
||||
Можно подключать только те модули, которые вам необходимы.
|
||||
|
||||
### R8/Proguard
|
||||
|
||||
```
|
||||
-keep class ** extends ru.touchin.roboswag.components.navigation.viewcontrollers.ViewController { *; }
|
||||
```
|
||||
|
||||
### Лицензия
|
||||
|
||||
```
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
kotlin-extensions
|
||||
=====
|
||||
|
||||
Модуль содержит extension-функции для `Activity`, `Context`, `Delegates`, `TextView`, `View` и `ViewHolder`.
|
||||
|
||||
### Основные интерфейсы и классы
|
||||
|
||||
##### Расширения для `Activity`:
|
||||
|
||||
* *safeStartActivityForResult* - функция для запуска нового активити с `requestCode`, который будет передан в *onActivityResult* при завершении работы данного активити. Находит наиболее подходящий активити для выполнения действия. Если не будет найден ни один активити для выполнения действия, то функция ничего не сделает и вернет `false`.
|
||||
|
||||
##### Расширения для `Context`:
|
||||
|
||||
* *safeStartActivity* - функция запуска активити, аналогична *safeStartActivityForResult*, но не позволяет передать `requestCode`.
|
||||
|
||||
* *openBrowser* - функция для открытия ссылки в браузере через *safeStartActivity*.
|
||||
|
||||
* *callToPhoneNumber* - функция для открытия программы "Телефон" с переданным номером телефона через *safeStartActivity*.
|
||||
|
||||
##### Расширения для `TextView`:
|
||||
|
||||
* *drawableStart*, *drawableTop*, *drawableEnd*, *drawableBottom* - функции для установки и получения `Drawable` на соответсвующих позициях.
|
||||
|
||||
##### Расширения для `View`:
|
||||
|
||||
* *setOnRippleClickListener* - функция для добавления Ripple-эффекта и действия, которое будет выполняться при нажатии на `View`.
|
||||
|
||||
##### Расширения для `ViewHolder`:
|
||||
|
||||
* *ViewHolder.findViewById* - функция для поиска `View`, расположеного внутри *itemView*.
|
||||
|
||||
* *ViewHolder.getText* - функция для получения текста из ресурсов.
|
||||
|
||||
* *ViewHolder.getString* - функция для получения строк из ресурсов. Может также принимать вторым аргументом и далее - строки, которые будут подставлены вместо специальных символов в строку из ресурсов.
|
||||
|
||||
* *ViewHolder.getColor* - получить цвет в виде `Int` из ресурсов.
|
||||
|
||||
* *ViewHolder.getColorStateList* - получить `ColorStateList`, который ассоциируется с переданным цветом.
|
||||
|
||||
* *ViewHolder.getDrawable* - получить `Drawable` из ресурсов.
|
||||
|
||||
##### Расширения для `Delegates`:
|
||||
|
||||
* *observable* - подписка на изменения свойства, принимает `initialValue` - начальное значение и `onChange` - действие, которое будет выполняться после каждой установки свойства.
|
||||
|
||||
* *distinctUntilChanged* - тоже самое, что и предыдущее расширение, только `onChange` будет выполняться лишь в том случае, когда свойство принимает новое значение не равное `null` и отличное от предыдущего.
|
||||
|
||||
### Примеры
|
||||
|
||||
```kotlin
|
||||
class LinkViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private val linkView: TextView = findViewById(R.id.item_link)
|
||||
private val linkColor = getColor(R.color.global_action)
|
||||
|
||||
fun bind(link: String) {
|
||||
linkView.text = link
|
||||
linkView.setOnRippleClickListener { context.openBrowser(link) }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Подключение
|
||||
|
||||
```gradle
|
||||
implementation project(':kotlin-extensions')
|
||||
```
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
lifecycle-rx
|
||||
=====
|
||||
|
||||
Модуль для преобразования событий из `Observable` в `LiveData`. Нужен для передачи событий из `ViewModel` во `ViewController` с автоматическим управлением подписками во `ViewController`.
|
||||
|
||||
### Основный интерфейсы и классы
|
||||
`Destroyable` - интерфейс, который содержит extansion-функцию *untilDestroy* для `Flowable`, `Observable`, `Single`, `Completable`, `Maybe`. Данная функция гарантирует, что подписка на события "умрет" после *onDestroy*.
|
||||
|
||||
`LifeDataDispatcher` - интерфейс, описывающий функцию *dispatchTo* для преобразования `Observable` в `MutableLiveData`.
|
||||
|
||||
`BaseDestroyable` и `BaseLifeDataDispatcher` - базовые реализации `Destroyable` и `LifeDataDispatcher` соответсвенно.
|
||||
|
||||
`RxViewModel` - базовый класс, от которого должны наследоваться все `ViewModel`. Обеспечивает отписку всех подписчиков при возникновении *onCleared*. Реализует `BaseDestroyable` и `LiveDataDispatcher`. По умолчанию использует базовые реализации данных интерфейсов, при желании можно передать свои `Destroyable` и `LiveDataDispatcher` через конструктор.
|
||||
|
||||
### Примеры
|
||||
|
||||
Простой пример `ViewModel`, через который можно получить список элементов и добавить один элемент.
|
||||
|
||||
```kotlin
|
||||
class SomeViewModel (
|
||||
private val someRepository: SomeRepository
|
||||
) : RxViewModel() {
|
||||
|
||||
val itemsList = MutableLiveData<ContentEvent<List<Item>>>()
|
||||
|
||||
fun getItemsList() {
|
||||
someRepository
|
||||
.getItems()
|
||||
.dispatchTo(itemsList)
|
||||
}
|
||||
|
||||
fun addItem(item: Item) {
|
||||
someRepository
|
||||
.addItem(item)
|
||||
.untilDestroy()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Подписка на события во `ViewController`. `ContentEvent` описан в модуле [lifecycle](https://github.com/TouchInstinct/RoboSwag/tree/master/lifecycle).
|
||||
|
||||
```kotlin
|
||||
someViewModel.itemsList.observe(this, Observer { event ->
|
||||
when (event) {
|
||||
is ContentEvent.Loading -> // do something
|
||||
is ContentEvent.Success -> // do something
|
||||
is ContentEvent.Error -> // do something
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Подключение
|
||||
|
||||
``` gradle
|
||||
implementation project(':lifecycle-rx')
|
||||
```
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
lifecycle
|
||||
=====
|
||||
|
||||
Модуль содержит обертку над `ViewModelProviders` для работы с `ViewController` и обертки для передачи событий из `ViewModel` во `ViewController`.
|
||||
|
||||
### Основные интерфейсы и классы
|
||||
|
||||
`LifecycleViewModelProviders` - объект для получения `ViewModelProvider`. Содержит функцию *of*, которая принимает `LifecycleOwner` и возвращает специфичный для него `ViewModelProvider`.
|
||||
|
||||
`SingleLiveEvent` - событие - одиночка. Посылает события только один раз. Наследуется от `MutableLiveData` и переопределяет методы `observe` и `setValue`.
|
||||
|
||||
`ContentEvent` - событие, обертка над данными.
|
||||
Дочерние классы:
|
||||
* `Loading` - символизирует состояние загрузки,
|
||||
* `Success` - символизирует успешное событие,
|
||||
* `Error` - символизирует ошибку,
|
||||
* `Complete` - символизирует завершение события.
|
||||
|
||||
`Event` - аналогичен `ContentEvent`, только не содержит никакой информации о данных. Нужен для оповещения о наступлении одного из следующих событий: `Loading`, `Complete` или `Error`.
|
||||
|
||||
### Примеры
|
||||
|
||||
Получение `ViewModel` во `ViewController`.
|
||||
|
||||
```kotlin
|
||||
private val viewModel = LifecycleViewModelProviders.of(this).get(SomeViewModel::class.java)
|
||||
```
|
||||
|
||||
Подписка на `SingleLiveEvent`.
|
||||
|
||||
```kotlin
|
||||
// во ViewModel
|
||||
val event = SingleLiveEvent<Event>()
|
||||
|
||||
// во ViewController
|
||||
event.observe(this, Observer { event ->
|
||||
when (event) {
|
||||
is Event.Loading -> ::onEventLoading
|
||||
is Event.Complete -> ::onEventComplete
|
||||
is Event.Error -> ::onEventError
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Подключение
|
||||
|
||||
```gradle
|
||||
implementation project(':lifecycle')
|
||||
```
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
livedata-location
|
||||
=====
|
||||
|
||||
Модуль позволяющий получать местоположение пользователя в виде потока данных `LiveData`.
|
||||
|
||||
### Основный интерфейсы и классы
|
||||
|
||||
Класс `LocationLiveData`. В конструкторе принимает `Context` и `LocationRequest`. Посылает `Location` подписчикам через указанные в `LocationRequest` интервалы времени. Метод *observe* позволяет подписаться на эти обновления. Данный метод принимает `LifecycleOwner` и `Observer`. Стоит учесть, что для использования данного класса нужно одно из следующих разрешений `ACCESS_COARSE_LOCATION` или `ACCESS_FINE_LOCATION`.
|
||||
### Примеры
|
||||
|
||||
Во `ViewModel`.
|
||||
|
||||
```kotlin
|
||||
val locationWithInterval = LocationLiveData(
|
||||
context,
|
||||
LocationRequest
|
||||
.create()
|
||||
.setInterval(5000)
|
||||
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
|
||||
)
|
||||
```
|
||||
|
||||
Во `ViewController`.
|
||||
|
||||
```kotlin
|
||||
viewModel.locationWithInterval.observe(this, Observer(::onLocationChanged))
|
||||
```
|
||||
|
||||
### Подключение
|
||||
|
||||
``` gradle
|
||||
implementation project(':livedata-location')
|
||||
```
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
navigation
|
||||
====
|
||||
|
||||
Модуль содержит классы для организации навигации в приложении.
|
||||
|
||||
### Основные интерфейсы и классы
|
||||
|
||||
#### Пакет `activities`
|
||||
|
||||
`BaseActivity` - абстрактный класс, в котором выполняется логгирование с помощью модуля [logging](https://github.com/TouchInstinct/RoboSwag/tree/master/logging) при выполнении некоторых методов. Класс позволяет добавлять новые `OnBackPressedListener` и удалять их с помощью методов *addOnBackPressedListener* и *removeOnBackPressedListener* (*removeAllOnBackPressedListeners*) соответственно.
|
||||
|
||||
Интерфейс `OnBackPressedListener` - интерфейс с одним методом *onBackPressed*. Используется в `BaseActivity`.
|
||||
|
||||
#### Пакет `fragments`
|
||||
|
||||
Класс `ViewControllerFragment` наследуется от `Fragment`. Через статический метод *args* получается `Bundle` с классом `ViewController`(а) и состоянием, которое наследуется от `Parcelable`. В методе *onCreate* инициализируются поля *state* и *viewControllerClass* используя данные из `Bundle`. В методе *onCreateView* создается `ViewController`.
|
||||
|
||||
#### Пакет `viewcontrollers`
|
||||
|
||||
`ViewController` - обертка над Fragment. Один ViewController - один экран. К моменту инициализации вашего класса уже будут доступны следующие поля из `ViewController`: *state*, *activity*, *fragment*, *view*. Это означает, что можно выполнять всю настройку экрана в `init { }`.
|
||||
|
||||
У класса есть два параметра `TActivity: FragmentActivity` и `TState: Parcelable`, которые нужно указывать при инициализации класса `ViewController`. В конструкторе данный класс принимает `CreationContext` и идентификатор layout-ресурса.
|
||||
|
||||
`ViewControllerNavigation` отвечает за навигацию по `ViewController`(ам). В конструкторе принимает `Context`, `FragmentManager` и идентификатор ресурса, который является контейнером для других фрагментов. Имеет параметр `TActivity : FragmentActivity`.
|
||||
|
||||
`EmptyState` - пустое состояние. Использутся, когда при переходе к новому `ViewController` не нужно передавать никаких инициализирующих данных.
|
||||
|
||||
`LifecycleLoggingObserver` подписывается на вызовы методов жизненного цикла и логгирует номер строки, из которой был вызваны эти методы.
|
||||
|
||||
Методы для навигации:
|
||||
|
||||
* *pushViewController* добавляет `ViewController` в стек. Имеет два обязательных параметра *viewControllerClass* - класс, унаследованный от `ViewController` и *state* - объект описывающий состояние.
|
||||
|
||||
* *pushViewControllerForResult* аналогичен предыдущему методу, используется, когда необходимо запустить какой-то фрагмент и при его завершении получить код. Для этого передаются еще два параметра: *requestCode* - код, который нужно получить при закрытии фрагмента и *targetFragment* - фрагмент, который должен получить этот код.
|
||||
|
||||
* *setViewControllerAsTop* работает так же как и *pushViewController* но еще добавляет в качестве *backStackName* тег `TOP_FRAGMENT_TAG_MARK`. При выполнении возврата с помощью метода `up` будет выполнен возврат данному фрагменту.
|
||||
|
||||
* *setInitialViewController* очищает стек и добавляет туда переданный `ViewController`.
|
||||
|
||||
`ViewControllerNavigation` является наследником класса `FragmentNavigation` и для возвратов необходимо использовать методы из родительского класса:
|
||||
|
||||
* *back* - вернуться к фрагменту, который лежит ниже в стеке.
|
||||
* *up* - вернуться к самому низу стека, если в стеке нет фрагментов, помеченных тегом `TOP_FRAGMENT_TAG_MARK`. Если есть, то выполнить возврат к нему. Имеет два необязательных параметра: *name* - имя класса до которого нужно сделать возврат, если он не будет найден, то будет произведен возврат к самому низу стека; *inclusive* - если установить этот флаг, то будет произведен возврат к самому низу стека несмотря на фрагменты с тегом `TOP_FRAGMENT_TAG_MARK`. Если будет установлен и *name* и *inclusive*, то будет произведен возврат к фрагменту, который стоит ниже фрагмента с переданным *name*.
|
||||
|
||||
### Примеры
|
||||
|
||||
Файл `MainActivity.kt`
|
||||
```Kotlin
|
||||
class MainActivity : BaseActivity() {
|
||||
|
||||
private val screenNavigation by lazy {
|
||||
ViewControllerNavigation<MainActivity>(
|
||||
this,
|
||||
supportFragmentManager,
|
||||
R.id.fragment_container
|
||||
)
|
||||
}
|
||||
|
||||
fun getNavigation() = screenNavigation
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
screenNavigation.setInitialViewController(
|
||||
MainViewController::class.java,
|
||||
MainScreenState(true)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Файл `MainViewController.kt`
|
||||
```Kotlin
|
||||
class MainViewController(
|
||||
creationContext: CreationContext
|
||||
) : ViewController<MainActivity, MainScreenState>(
|
||||
creationContext,
|
||||
R.layout.view_controller_main
|
||||
) {
|
||||
|
||||
private val button: View = findViewById(R.id.view_controller_main_button)
|
||||
|
||||
init {
|
||||
button.setOnClickListener {
|
||||
activity.getNavigation().pushViewController(
|
||||
TutorialViewController::class.java,
|
||||
EmptyState
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Файл `activity_main.xml`
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</FrameLayout>
|
||||
```
|
||||
|
||||
### Рекомендации
|
||||
|
||||
Рекомендуется делать состояния, которые передаются во `ViewController` неизменяемыми, чтобы при навигации обратно `ViewController` корректно восстанавливались с изначально заданным состоянием.
|
||||
|
||||
### Зависимости
|
||||
|
||||
Для работы с данным модулем необходимо так же подключить модуль [logging](https://github.com/TouchInstinct/RoboSwag/tree/master/logging).
|
||||
|
||||
```gradle
|
||||
implementation project(':logging')
|
||||
```
|
||||
|
||||
### Подключение
|
||||
|
||||
```gradle
|
||||
implementation project(':navigation')
|
||||
```
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
# recyclerview-adapters
|
||||
|
||||
Модуль, расширяющий возможности работы со стандартным `RecyclerView.Adapter`.
|
||||
|
||||
### Основные интерфейсы и классы
|
||||
|
||||
`DelegationListAdapter` - базовый класс, наследник от `RecyclerView.Adapter`.
|
||||
|
||||
Конструктор принимает `DiffUtil.ItemCallback` - интерфейс, описывающий как различать элементы в адаптере. Он содержит два абстрактных метода: *areItemsTheSame* - метод, сравнивающий элементы, и *areContentsTheSame* - метод, сравнивающий визуальную составляющую элементов.
|
||||
|
||||
Например, возьмем список товаров, у которых есть уникальный *id* и *title*, который может повторяться. В `RecyclerView` отображается только название товара, т.е. *title*. В такой ситуации в методе *areItemsTheSame* нужно будет написать `oldItem.id == newItem.id`, а в методе *areContentsTheSame* - `oldItem.title == newItem.title`. Оба эти метода вычисляются в бэкграунд потоке.
|
||||
|
||||
Методы `getHeadersCount` и `getFootersCount` нужны, когда в списке есть элементы, которые всегда должны быть наверху или внизу. Например, если нужно добавить кнопку после всех элементов.
|
||||
|
||||
Управлением элементами списка занимаются делегаты. Они добавляются с помощью метода *addDelegate*.
|
||||
|
||||
`ItemAdapterDelegate` - делегат, который управляет созданием и прикреплением элементов в зависимости от типа элемента.
|
||||
|
||||
`PositionAdapterDelegate` - делегат, который управляет созданием и прикреплением элементов, основываясь на позиции элемента в `DelegationListAdapter`.
|
||||
|
||||
При реализации делегатов, необходимо описать два метода:
|
||||
* *isForViewType* - метод, который говорит делегату, должен ли он управлять соответсвующим элементом;
|
||||
* *onBindViewHolder* - метод, который описывает действия при прикреплении элемента к `ViewHolder`.
|
||||
|
||||
### Примеры
|
||||
|
||||
Создание адаптера.
|
||||
|
||||
```Kotlin
|
||||
class SomeAdapter : DelegationListAdapter<Item>(CALLBACK) {
|
||||
|
||||
companion object {
|
||||
private val CALLBACK = object : DiffUtil.ItemCallback<Item>() {
|
||||
override fun areItemsTheSame(oldItem: Item, newItem: Item) = newItem.id == oldItem.id
|
||||
override fun areContentsTheSame(oldItem: Item, newItem: Item) = newItem == oldItem
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
addDelegate(HeaderDelegate())
|
||||
addDelegate(ItemDelegate())
|
||||
addDelegate(BottomDelegate())
|
||||
}
|
||||
|
||||
// Some logic in your adapter
|
||||
}
|
||||
```
|
||||
|
||||
Создание делегата.
|
||||
```Kotlin
|
||||
class HeaderDelegate(
|
||||
private val addAction: () -> Unit
|
||||
) : PositionAdapterDelegate<RecyclerView.ViewHolder>() {
|
||||
|
||||
override fun isForViewType(adapterPosition: Int): Boolean = adapterPosition == 0
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) =
|
||||
object : RecyclerView.ViewHolder(HeaderItemView()) {}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
adapterPosition: Int,
|
||||
payloads: MutableList<Any>
|
||||
) = holder.itemView.setOnClickListener { addAction.invoke() }
|
||||
}
|
||||
```
|
||||
|
||||
### Подключение
|
||||
```gralde
|
||||
implementation project(':recyclerview-adapters')
|
||||
```
|
||||
Loading…
Reference in New Issue