Здравствуйте дорогие хабрчане. Этот материал основан на статье, написанной в 2017 году.
Каждый раз, когда мы думаем о создании RecyclerView
, нас пугает объем кода в адаптере. Кроме того, если у этого адаптера много ViewHolder-ов
, то спаси нас Бог!
Конечно, все мы знакомы с шаблонным кодом RecyclerView.Adapter
. Но писать один и тот же код снова и снова — пустая трата времени.
Наверняка должен быть лучший способ?
Да здравствует FastAdapter
Поздоровайтесь с FastAdapter
!
Пуленепробиваемая, быстрая и простая в использовании библиотека адаптеров, которая сводит время разработки к минимуму… — Майк Пенз
FastAdapter
создан Майком Пензом. Разработчик популярных библиотек, таких как MaterialDrawer
и AboutLibraries
.
FastAdapter
сокращает время, затрачиваемое на код адаптера. Более того, он предлагает множество функций, поэтому никоим образом не ограничивает ваше приложение. Благодаря множеству предлагаемых функций подумайте о замене «обычных» адаптеров RecyclerView
на FastAdapter.
Приступим к работе
Для начала использования FastAdapter-а, нужно добавить в build.gradle
следующие зависимости:
Проверьте подключена ли зависимость:
implementation "androidx.appcompat:appcompat:${androidX}"
implementation "com.mikepenz:fastadapter:${latestFastAdapter}"
implementation "androidx.recyclerview:recyclerview:1.2.1"
Для использования ViewBinding-а
implementation "com.mikepenz:fastadapter-extensions-binding:5.5.1"
implementation "com.mikepenz:fastadapter-extensions-diff:5.5.1"
Не забудьте, что должен быть подключен ViewBinding
Создаем data class
Допустим, мы создаем приложение прогноза погоды. Поэтому я называю свой класс Weather.
data class Weather(
val temperature: Double,
val humidity: Int,
val windSpeed: Double,
val pressure: Double
)
Создаем xml файл weather_item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rowLayout"
android:layout_width="wrap_content"
android:layout_height="114dp"
android:layout_gravity="center"
android:layout_margin="6dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/temperatureTextView"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/humidityTextView"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/windSpeedTextView"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/pressureTextView"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
Внедряем адаптер
Cоздаем класс, который реализуетAbstractBindingItem
type()
— возвращает уникальный id (вашего родительского макета)bindView()
— метод onBindViewHolder() RecyclerView
Имплементируемidentifier
таким образом, чтобы у каждого элемента было своё уникальное значение.
class WeatherItem(val weather: Weather) : AbstractBindingItem<WeatherItemBinding>() {
override var identifier: Long
get() = weather.hashCode().toLong()
set(value) {}
override val type: Int
get() = R.id.weatherRecyclerView
override fun createBinding(inflater: LayoutInflater, parent: ViewGroup?
): WeatherItemBinding {
return WeatherItemBinding.inflate(inflater, parent, false)
}
override fun bindView(binding: WeatherItemBinding, payloads: List<Any>) {
binding.temperatureTextView.text = weather.temperature.toString()
binding.humidityTextView.text = weather.humidity.toString()
binding.pressureTextView.text = weather.pressure.toString()
binding.windSpeedTextView.text = weather.windSpeed.toString()
}
override fun unbindView(binding: WeatherItemBinding) {
binding.temperatureTextView.text = null
binding.humidityTextView.text = null
binding.pressureTextView.text = null
binding.windSpeedTextView.text = null
}
}
Прикрепляем FastAdapter к нашему RecyclerView
binding.weatherRecyclerView.setLayoutManager(LinearLayoutManager(
this,
LinearLayoutManager.VERTICAL,
false
)
)
val weatherItemAdapter = ItemAdapter<WeatherItem>()
val weatherFastAdapter =
FastAdapter.with(weatherItemAdapter)
binding.weatherRecyclerView.adapter = weatherFastAdapter
val weatherList = weatherForecastForNextDays
FastAdapterDiffUtil[weatherItemAdapter] =
weatherList.map(::WeatherItem)
map
используем для конвертации списка погоды в AbstractBindingItem
FastAdapterDiffUtil
предназначен для обновления списка. Он заменяет элементы внутри адаптера новым набором элементов. Одним предложением - меняет не весь список, а лишь те item
-ы которые поменялись.
Это все, что нам нужно сделать! Если мы запустим наше приложение сейчас, мы получим RecyclerView
, заполненный списком.
А вот тот код, написание которого нам удалось избежать благодаря FastAdapter
-у:
class
RecyclerView.Adapter
inflate
item
-аfun getItemCount()
А теперь рассмотрим некоторые популярные функции RecyclerView
реализованные с помощью FastAdapter
:
Click Listener
weatherFastAdapter .onClickListener = { view, adapter, item, position ->
// обработка клика
false
}
Фильтрация данных с помощью поиска
Если наше приложение использует
SearchView
, и мы хотим отфильтровать данные нашего адаптера, тоFastAdapter
может это сделать.
weatherItemAdapter.filter("yourSearchTerm")
weatherItemAdapter.itemFilter.filterPredicate =
{ item: WeatherItem, constraint: CharSequence? ->
item.weather.temperature.toString().
contains(constraint.toString(), ignoreCase = true) }
Drag & Drop
Начните с создания экземпляра
SimpleSwipeDrawerDragCallback
(любезно предоставленногоFastAdapter-ом
). Затем используйте его для инициализацииItemTouchHelper-а
. Наконец, прикрепитеItemTouchHelper к RecyclerView.
touchCallback = SimpleSwipeDrawerDragCallback(this, ItemTouchHelper.LEFT)
.withNotifyAllDrops(true)
.withSwipeLeft(80)
.withSensitivity(10f)
.withSurfaceThreshold(0.3f)
touchHelper = ItemTouchHelper(touchCallback)
touchHelper.attachToRecyclerView(binding.cityListRecyclerview)
Внедрите интерфейс ItemTouchCallback
. Он реализует метод itemTouchOnMove()
. Добавьте к нему следующий код:
override fun itemTouchOnMove(oldPosition: Int, newPosition: Int): Boolean {
DragDropUtil.onMove(
cityWeatherItemFastAdapter.itemAdapter, oldPosition, newPosition
)
viewModel.changingOrderList(oldPosition, newPosition)
return true
}
Итоги
FastAdapter
упрощаетRecyclerView
. Это больше, чем просто удобная библиотека. Если вы выполняете много манипуляций с адаптером, то наличие FastAdapter
-а в вашем арсенале абсолютно необходимо!