Skip to content

Instantly share code, notes, and snippets.

@dcampogiani
Created February 25, 2022 10:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dcampogiani/45043dc7938b2fa4f4bcc065fb7edffc to your computer and use it in GitHub Desktop.
Save dcampogiani/45043dc7938b2fa4f4bcc065fb7edffc to your computer and use it in GitHub Desktop.
Flow and RecyclerView extensions
/**
* This method requires a LinearLayoutManager
*/
val RecyclerView.visibleFlow: Flow<List<Int>>
get() = callbackFlow<List<Int>> {
val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dx == 0 && dy == 0) {
return
}
val layoutManager = layoutManager
require(layoutManager is LinearLayoutManager)
val first = layoutManager.findFirstVisibleItemPosition()
val last = layoutManager.findLastVisibleItemPosition()
if (first != RecyclerView.NO_POSITION && last != RecyclerView.NO_POSITION) {
if (first == last) {
offer(listOf(first))
} else {
val element = (first..last).toList()
offer(element)
}
}
}
}
addOnScrollListener(scrollListener)
awaitClose {
removeOnScrollListener(scrollListener)
}
}.distinctUntilChanged()
/**
* This operator is used to emit the items present in a list but not in the previous one.
*
* For example:
* ```
* flowOf(
* listOf(0, 1, 2),
* listOf(1, 2),
* listOf(1, 2, 3)
* ).justNewValues()
* ```
* will produce:
* `0`
* `1`
* `2`
* `3`.
*
* The first diff is [0, 1, 2] - [] = [0, 1, 2]
* So thanks to flatMap the flow is emitting single values `0`, `1`, `2`
*
* When the new list [1, 2] is received the diff is [1, 2] - [0, 1, 2] = []
* so the flow is not emitting any value
*
* When the new list [1, 2, 3] is received the diff is [1, 2, 3] - [1, 2] = [3]
* so the flow will emit the single value `3`
*/
fun <T> Flow<List<T>>.justNewValues(): Flow<T> =
scan(
Pair(
emptyList(),
emptyList()
)
) { accumulator: Pair<List<T>, List<T>>, newList: List<T> ->
//Accumulator.first is used to store what was emitted as last value
//Accumulator.second is used to store what is new in the current list compared to the last one (newList - accumulator.first)
val entered = newList - accumulator.first
Pair(newList, entered)
}.flatMapConcat {
it.second.asFlow()
}.distinctUntilChanged()
@Test
fun `just new values from a flow of list of int`() = runBlockingTest {
val input = flowOf(
listOf(0, 1, 2),
listOf(1, 2),
listOf(1, 2, 3),
listOf(2, 3),
listOf(2, 3, 4),
listOf(3, 4),
listOf(4),
listOf(4, 5),
listOf(4, 5, 6),
listOf(5, 6),
listOf(5, 6, 7),
listOf(6, 7),
listOf(5, 6, 7),
listOf(5, 6),
listOf(4, 5, 6),
listOf(4, 5),
)
val sut = input.justNewValues()
sut.test {
assertEquals(0, expectItem())
assertEquals(1, expectItem())
assertEquals(2, expectItem())
assertEquals(3, expectItem())
assertEquals(4, expectItem())
assertEquals(5, expectItem())
assertEquals(6, expectItem())
assertEquals(7, expectItem())
assertEquals(5, expectItem())
assertEquals(4, expectItem())
expectComplete()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment