
Здравствуйте дорогие хабрчане. Этот материал основан на статье, написанной в 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.Adapterinflateitem-а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-а в вашем арсенале абсолютно необходимо!
