Pull to refresh

RecyclerView Adapter in Android, сделай быстрее и проще

Reading time8 min
Views8.2K
Original author: Suleiman

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

  • classRecyclerView.Adapter

  • inflate item

  • fun getItemCount()

А теперь рассмотрим некоторые популярные функции RecyclerView реализованные с помощью FastAdapter:

  1. Click Listener

weatherFastAdapter .onClickListener = { view, adapter, item, position -> 
// обработка клика 
false 
}
  1. Фильтрация данных с помощью поиска

    Если наше приложение использует SearchView, и мы хотим отфильтровать данные нашего адаптера, то FastAdapter может это сделать.

weatherItemAdapter.filter("yourSearchTerm") 
weatherItemAdapter.itemFilter.filterPredicate = 
{ item: WeatherItem, constraint: CharSequence? -> 
item.weather.temperature.toString().
contains(constraint.toString(), ignoreCase = true) }
  1. 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-а в вашем арсенале абсолютно необходимо!

Tags:
Hubs:
Total votes 6: ↑6 and ↓0+6
Comments0

Articles