mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-12-19 20:56:02 +00:00
update dependencies
This commit is contained in:
1
android/recyclerview/src/androidMain/AndroidManifest.xml
Normal file
1
android/recyclerview/src/androidMain/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest package="dev.inmo.micro_utils.android.recyclerview"/>
|
||||
@@ -0,0 +1,22 @@
|
||||
package dev.inmo.micro_utils.android.recyclerview
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
abstract class AbstractStandardViewHolder<T>(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
viewId: Int,
|
||||
onViewInflated: ((View) -> Unit)? = null
|
||||
) : AbstractViewHolder<T>(
|
||||
inflater.inflate(viewId, container, false).also {
|
||||
onViewInflated ?.invoke(it)
|
||||
}
|
||||
) {
|
||||
constructor(
|
||||
container: ViewGroup,
|
||||
viewId: Int,
|
||||
onViewInflated: ((View) -> Unit)? = null
|
||||
) : this(LayoutInflater.from(container.context), container, viewId, onViewInflated)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.inmo.micro_utils.android.recyclerview
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
abstract class AbstractViewHolder<in T>(
|
||||
view: View
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
abstract fun onBind(item: T)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.inmo.micro_utils.android.recyclerview
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
|
||||
val Context.recyclerViewItemsDecoration
|
||||
get() = DividerItemDecoration(this, LinearLayout.VERTICAL)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package dev.inmo.micro_utils.android.recyclerview
|
||||
|
||||
import androidx.recyclerview.widget.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun RecyclerView.LayoutManager.findLastVisibleItemPositionGetter(): (() -> Int)? = when (this) {
|
||||
is LinearLayoutManager -> ::findLastVisibleItemPosition
|
||||
is GridLayoutManager -> ::findLastVisibleItemPosition
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun RecyclerView.lastVisibleItemFlow(
|
||||
completingScope: CoroutineScope
|
||||
): Flow<Int> {
|
||||
val lastVisibleElementFun: () -> Int = layoutManager ?.findLastVisibleItemPositionGetter() ?: error("Currently supported only linear and grid layout manager")
|
||||
val lastVisibleFlow = MutableStateFlow(lastVisibleElementFun())
|
||||
addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
lastVisibleFlow.value = lastVisibleElementFun()
|
||||
}
|
||||
}.also { scrollListener ->
|
||||
lastVisibleFlow.onCompletion {
|
||||
removeOnScrollListener(scrollListener)
|
||||
}.launchIn(completingScope)
|
||||
}
|
||||
)
|
||||
return lastVisibleFlow.asStateFlow()
|
||||
}
|
||||
|
||||
inline fun Flow<Int>.mapLeftItems(
|
||||
crossinline countGetter: () -> Int
|
||||
): Flow<Int> = map { countGetter() - it }
|
||||
|
||||
inline fun Flow<Int>.mapRequireFilling(
|
||||
minimalLeftItems: Int,
|
||||
crossinline countGetter: () -> Int
|
||||
): Flow<Int> = mapLeftItems(countGetter).mapNotNull {
|
||||
if (it < minimalLeftItems) {
|
||||
it
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
inline fun RecyclerView.mapRequireFilling(
|
||||
minimalLeftItems: Int,
|
||||
completingScope: CoroutineScope,
|
||||
crossinline countGetter: () -> Int
|
||||
): Flow<Int> = lastVisibleItemFlow(completingScope).mapRequireFilling(minimalLeftItems, countGetter)
|
||||
@@ -0,0 +1,91 @@
|
||||
package dev.inmo.micro_utils.android.recyclerview
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
|
||||
abstract class RecyclerViewAdapter<T>: RecyclerView.Adapter<AbstractViewHolder<T>>() {
|
||||
protected abstract val data: List<T>
|
||||
|
||||
private val _dataCountState by lazy {
|
||||
MutableStateFlow<Int>(data.size)
|
||||
}
|
||||
val dataCountState: StateFlow<Int> by lazy {
|
||||
_dataCountState.asStateFlow()
|
||||
}
|
||||
|
||||
var emptyView: View? = null
|
||||
set(value) {
|
||||
field = value
|
||||
checkEmpty()
|
||||
}
|
||||
|
||||
init {
|
||||
registerAdapterDataObserver(
|
||||
object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
|
||||
super.onItemRangeChanged(positionStart, itemCount)
|
||||
_dataCountState.value = data.size
|
||||
checkEmpty()
|
||||
}
|
||||
|
||||
override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
|
||||
super.onItemRangeChanged(positionStart, itemCount, payload)
|
||||
_dataCountState.value = data.size
|
||||
checkEmpty()
|
||||
}
|
||||
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
_dataCountState.value = data.size
|
||||
checkEmpty()
|
||||
}
|
||||
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
super.onItemRangeRemoved(positionStart, itemCount)
|
||||
_dataCountState.value = data.size
|
||||
checkEmpty()
|
||||
}
|
||||
|
||||
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
|
||||
super.onItemRangeMoved(fromPosition, toPosition, itemCount)
|
||||
_dataCountState.value = data.size
|
||||
checkEmpty()
|
||||
}
|
||||
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
super.onItemRangeInserted(positionStart, itemCount)
|
||||
_dataCountState.value = data.size
|
||||
checkEmpty()
|
||||
}
|
||||
}
|
||||
)
|
||||
checkEmpty()
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
override fun onBindViewHolder(holder: AbstractViewHolder<T>, position: Int) {
|
||||
holder.onBind(data[position])
|
||||
}
|
||||
|
||||
private fun checkEmpty() {
|
||||
emptyView ?. let {
|
||||
if (dataCountState.value == 0) {
|
||||
it.visibility = View.VISIBLE
|
||||
} else {
|
||||
it.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> RecyclerViewAdapter(
|
||||
data: List<T>,
|
||||
onCreateViewHolder: (parent: ViewGroup, viewType: Int) -> AbstractViewHolder<T>
|
||||
) = object : RecyclerViewAdapter<T>() {
|
||||
override val data: List<T> = data
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractViewHolder<T> = onCreateViewHolder(parent, viewType)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package dev.inmo.micro_utils.android.recyclerview
|
||||
|
||||
import dev.inmo.micro_utils.common.Diff
|
||||
import dev.inmo.micro_utils.common.PreviewFeature
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@PreviewFeature("This feature in preview state and may contains different bugs. " +
|
||||
"Besides, this feature can be changed in future in non-compatible way")
|
||||
abstract class StateFlowBasedRecyclerViewAdapter<T>(
|
||||
listeningScope: CoroutineScope,
|
||||
dataState: StateFlow<List<T>>
|
||||
) : RecyclerViewAdapter<T>() {
|
||||
override var data: List<T> = emptyList()
|
||||
|
||||
init {
|
||||
dataState.onEach {
|
||||
try {
|
||||
val diffForRemoves = Diff(data, it)
|
||||
val removedIndexes = diffForRemoves.removed.map { it.index }
|
||||
val leftRemove = removedIndexes.toMutableList()
|
||||
data = data.filterIndexed { i, _ ->
|
||||
if (i in leftRemove) {
|
||||
leftRemove.remove(i)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
removedIndexes.sortedDescending().forEach {
|
||||
notifyItemRemoved(it)
|
||||
}
|
||||
}
|
||||
val diffAddsAndReplaces = Diff(data, it)
|
||||
data = it
|
||||
withContext(Dispatchers.Main) {
|
||||
diffAddsAndReplaces.replaced.forEach { (from, to) ->
|
||||
notifyItemMoved(from.index, to.index)
|
||||
}
|
||||
diffAddsAndReplaces.added.forEach {
|
||||
notifyItemInserted(it.index)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// currently do nothing
|
||||
}
|
||||
}.launchIn(listeningScope)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user