Skip to content

Commit 0d87cfb

Browse files
committed
Support swapping RecyclerView adapters (#19)
1 parent 66d533f commit 0d87cfb

3 files changed

Lines changed: 79 additions & 20 deletions

File tree

indicator-fast-scroll/src/main/java/com/reddit/indicatorfastscroll/FastScrollerView.kt

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ class FastScrollerView @JvmOverloads constructor(
5858

5959
private val isSetup: Boolean get() = (recyclerView != null)
6060
private var recyclerView: RecyclerView? = null
61+
private var adapter: RecyclerView.Adapter<*>? = null
62+
set(value) {
63+
field?.unregisterAdapterDataObserver(adapterDataObserver)
64+
field = value
65+
value?.let { newAdapter ->
66+
newAdapter.registerAdapterDataObserver(adapterDataObserver)
67+
postUpdateItemIndicators()
68+
}
69+
}
70+
private val adapterDataObserver: RecyclerView.AdapterDataObserver = createAdapterDataObserver()
6171
private lateinit var getItemIndicator: (Int) -> FastScrollItemIndicator?
6272
/**
6373
* An optional predicate for deciding which indicators to show after they have been computed.
@@ -161,27 +171,17 @@ class FastScrollerView @JvmOverloads constructor(
161171
this.showIndicator = showIndicator
162172
this.useDefaultScroller = useDefaultScroller
163173

164-
updateItemIndicators()
165-
val adapter = recyclerView.adapter ?: throw IllegalArgumentException(
166-
"RecyclerView needs to have an adapter before setting up its fast scroller."
167-
)
168-
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
169-
override fun onChanged() {
170-
postUpdateItemIndicators()
174+
this.adapter = recyclerView.adapter.also {
175+
if (it != null) {
176+
updateItemIndicators()
171177
}
172-
173-
override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) =
174-
onChanged()
175-
176-
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) =
177-
onChanged()
178-
179-
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) =
180-
onChanged()
181-
182-
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) =
183-
onChanged()
184-
})
178+
}
179+
recyclerView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
180+
// RecyclerView#setAdapter calls requestLayout, so this can detect adapter changes
181+
if (recyclerView.adapter !== adapter) {
182+
this@FastScrollerView.adapter = recyclerView.adapter
183+
}
184+
}
185185
}
186186

187187
private fun postUpdateItemIndicators() {
@@ -345,6 +345,29 @@ class FastScrollerView @JvmOverloads constructor(
345345
return consumed
346346
}
347347

348+
companion object {
349+
350+
private fun FastScrollerView.createAdapterDataObserver(): RecyclerView.AdapterDataObserver {
351+
return object : RecyclerView.AdapterDataObserver() {
352+
override fun onChanged() {
353+
postUpdateItemIndicators()
354+
}
355+
356+
override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) =
357+
onChanged()
358+
359+
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) =
360+
onChanged()
361+
362+
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) =
363+
onChanged()
364+
365+
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) =
366+
onChanged()
367+
}
368+
}
369+
}
370+
348371
interface ItemIndicatorSelectedCallback {
349372
fun onItemIndicatorSelected(
350373
indicator: FastScrollItemIndicator,

indicator-fast-scroll/src/test/java/com/reddit/indicatorfastscroll/TestActivity.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ class TestActivity : AppCompatActivity() {
5050
testAdapter.notifyDataSetChanged()
5151
}
5252

53+
fun recreateAdapter() {
54+
testAdapter = TestAdapter()
55+
recyclerView.adapter = testAdapter
56+
}
57+
5358
inner class TestAdapter : RecyclerView.Adapter<TestAdapter.ViewHolder>() {
5459

5560
var data: List<ListItem> = emptyList()

indicator-fast-scroll/src/test/java/com/reddit/indicatorfastscroll/Tests.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,35 @@ class Tests {
164164
assertEquals(2, testItemIndicatorsBuilder.timesBuildCalled)
165165
}
166166

167+
@Test
168+
fun checkAdapterChange() {
169+
val items1 = listOf(
170+
TestActivity.ListItem("A"),
171+
TestActivity.ListItem("B"),
172+
TestActivity.ListItem("C")
173+
)
174+
val expectedText1 = "A\nB\nC"
175+
val items2 = listOf(
176+
TestActivity.ListItem("D"),
177+
TestActivity.ListItem("E"),
178+
TestActivity.ListItem("F")
179+
)
180+
val expectedText2 = "D\nE\nF"
181+
182+
activity.runOnUiThread {
183+
activity.presentData(items1)
184+
}
185+
onView(withId(R.id.test_fastscroller))
186+
.check(matches(withChild(withText(expectedText1))))
187+
.check(matches(hasChildCount(1)))
188+
189+
activity.runOnUiThread {
190+
activity.recreateAdapter()
191+
activity.presentData(items2)
192+
}
193+
onView(withId(R.id.test_fastscroller))
194+
.check(matches(withChild(withText(expectedText2))))
195+
.check(matches(hasChildCount(1)))
196+
}
197+
167198
}

0 commit comments

Comments
 (0)