Merge pull request #76 from InsanusMokrassar/0.5.12

0.5.12
This commit is contained in:
InsanusMokrassar 2021-06-17 13:54:36 +06:00 committed by GitHub
commit 509583ea2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 81 additions and 6 deletions

View File

@ -1,5 +1,15 @@
# Changelog # Changelog
## 0.5.12
* `Common`:
* `Android`
* Extension `View#changeVisibility` has been fixed
* `Android`
* `RecyclerView`
* Default adapter got `dataCountFlow` property
* New subtype of adapter based on `StateFlow`: `StateFlowBasedRecyclerViewAdapter`
## 0.5.11 ## 0.5.11
* `Repos`: * `Repos`:

View File

@ -11,6 +11,7 @@ kotlin {
commonMain { commonMain {
dependencies { dependencies {
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
api project(":micro_utils.common")
} }
} }
androidMain { androidMain {

View File

@ -3,11 +3,19 @@ package dev.inmo.micro_utils.android.recyclerview
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.flow.*
abstract class RecyclerViewAdapter<T>: RecyclerView.Adapter<AbstractViewHolder<T>>() { abstract class RecyclerViewAdapter<T>: RecyclerView.Adapter<AbstractViewHolder<T>>() {
protected abstract val data: List<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 var emptyView: View? = null
set(value) { set(value) {
field = value field = value
@ -19,31 +27,37 @@ abstract class RecyclerViewAdapter<T>: RecyclerView.Adapter<AbstractViewHolder<T
object : RecyclerView.AdapterDataObserver() { object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeChanged(positionStart: Int, itemCount: Int) { override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
super.onItemRangeChanged(positionStart, itemCount) super.onItemRangeChanged(positionStart, itemCount)
_dataCountState.value = data.size
checkEmpty() checkEmpty()
} }
override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) { override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
super.onItemRangeChanged(positionStart, itemCount, payload) super.onItemRangeChanged(positionStart, itemCount, payload)
_dataCountState.value = data.size
checkEmpty() checkEmpty()
} }
override fun onChanged() { override fun onChanged() {
super.onChanged() super.onChanged()
_dataCountState.value = data.size
checkEmpty() checkEmpty()
} }
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) { override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
super.onItemRangeRemoved(positionStart, itemCount) super.onItemRangeRemoved(positionStart, itemCount)
_dataCountState.value = data.size
checkEmpty() checkEmpty()
} }
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) { override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
super.onItemRangeMoved(fromPosition, toPosition, itemCount) super.onItemRangeMoved(fromPosition, toPosition, itemCount)
_dataCountState.value = data.size
checkEmpty() checkEmpty()
} }
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
super.onItemRangeInserted(positionStart, itemCount) super.onItemRangeInserted(positionStart, itemCount)
_dataCountState.value = data.size
checkEmpty() checkEmpty()
} }
} }
@ -59,7 +73,7 @@ abstract class RecyclerViewAdapter<T>: RecyclerView.Adapter<AbstractViewHolder<T
private fun checkEmpty() { private fun checkEmpty() {
emptyView ?. let { emptyView ?. let {
if (data.isEmpty()) { if (dataCountState.value == 0) {
it.visibility = View.VISIBLE it.visibility = View.VISIBLE
} else { } else {
it.visibility = View.GONE it.visibility = View.GONE

View File

@ -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)
}
}

View File

@ -16,7 +16,7 @@ package dev.inmo.micro_utils.common
AnnotationTarget.TYPEALIAS, AnnotationTarget.TYPEALIAS,
AnnotationTarget.TYPE_PARAMETER AnnotationTarget.TYPE_PARAMETER
) )
annotation class PreviewFeature annotation class PreviewFeature(val message: String = "It is possible, that behaviour of this thing will be changed or removed in future releases")
@RequiresOptIn( @RequiresOptIn(
"This thing is marked as warned. See message of warn to get more info", "This thing is marked as warned. See message of warn to get more info",

View File

@ -36,12 +36,12 @@ fun View.toggleVisibility(goneOnHide: Boolean = true) {
fun View.changeVisibility(show: Boolean = !isShown, goneOnHide: Boolean = true) { fun View.changeVisibility(show: Boolean = !isShown, goneOnHide: Boolean = true) {
if (show) { if (show) {
show()
} else {
if (goneOnHide) { if (goneOnHide) {
gone() gone()
} else { } else {
hide() hide()
} }
} else {
show()
} }
} }

View File

@ -45,5 +45,5 @@ dokka_version=1.4.32
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.5.11 version=0.5.12
android_code_version=52 android_code_version=53