Skip to content

Commit

Permalink
fixed erratic behavior when performing diffing against new list. Rele…
Browse files Browse the repository at this point in the history
…ased 2.1.5. Improved demo app with shuffle addition.
  • Loading branch information
gotev committed Feb 16, 2019
1 parent b532d8c commit c728834
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 35 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ In this way every item of the recycler view has its own set of files, resulting
## <a name="setup"></a>Setup
In your gradle dependencies add:
```groovy
implementation 'net.gotev:recycleradapter:2.1.1'
implementation 'net.gotev:recycleradapter:2.1.5'
```

## <a name="basicTutorial"></a>Basic usage tutorial
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_sync.*
import net.gotev.recycleradapter.AdapterItem
import net.gotev.recycleradapter.RecyclerAdapter
import net.gotev.recycleradapterdemo.adapteritems.LabelItem
import net.gotev.recycleradapterdemo.adapteritems.SyncItem
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit
import kotlin.random.Random


class SyncActivity : AppCompatActivity() {
Expand All @@ -22,26 +27,26 @@ class SyncActivity : AppCompatActivity() {
}

private lateinit var recyclerAdapter: RecyclerAdapter
private var executor = ScheduledThreadPoolExecutor(1)
private var scheduledOperation: ScheduledFuture<*>? = null

private var listB = arrayListOf(
SyncItem(this, 1, "listB"),
SyncItem(this, 3, "listB"),
SyncItem(this, 4, "listB"),
SyncItem(this, 5, "listB")
)

private fun listB(): ArrayList<SyncItem> {
listB.add(SyncItem(this, listB.last().id + 1, "listB${listB.last().id + 1}"))
listB.add(SyncItem(this, listB.last().id + 1, "listB${listB.last().id + 1}"))
return listB
}

private fun listA() =
arrayListOf(
SyncItem(this, 2, "listA"),
SyncItem(this, 3, "listA"),
SyncItem(this, 6, "listA")
)

private fun listB() =
arrayListOf(
SyncItem(this, 1, "listB"),
SyncItem(this, 2, "listB"),
SyncItem(this, 3, "listB"),
SyncItem(this, 4, "listB"),
SyncItem(this, 5, "listB")
)

private fun listC() =
arrayListOf(
SyncItem(this, 3, "listC")
SyncItem(this, 1, "listA"),
SyncItem(this, 2, "listA")
)

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -55,11 +60,23 @@ class SyncActivity : AppCompatActivity() {
setDisplayHomeAsUpEnabled(true)
}

val linearLayoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)

recyclerAdapter = RecyclerAdapter()
recyclerAdapter.setEmptyItem(LabelItem(getString(R.string.empty_list)))

// prevent recyclerview from scrolling when adding many items
// https://github.com/airbnb/epoxy/issues/224#issuecomment-305991898
recyclerAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0) {
linearLayoutManager.scrollToPosition(0)
}
}
})

recycler_view.apply {
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
layoutManager = linearLayoutManager
adapter = recyclerAdapter
}

Expand All @@ -71,10 +88,30 @@ class SyncActivity : AppCompatActivity() {
recyclerAdapter.syncWithItems(listB())
}

syncC.setOnClickListener {
recyclerAdapter.syncWithItems(listC())
shuffle.setOnClickListener {
scheduledOperation = if (scheduledOperation == null) {
executor.scheduleAtFixedRate({
runOnUiThread {
recyclerAdapter.syncWithItems(ArrayList(createItems()))
}
}, 1, 100, TimeUnit.MILLISECONDS)
} else {
scheduledOperation?.cancel(true)
null
}
}
}

override fun onPause() {
super.onPause()
scheduledOperation?.cancel(true)
scheduledOperation = null
}

fun createItems(): List<AdapterItem<*>> {
return (0..Random.nextInt(1, 10)).flatMap {
listOf(LabelItem("$it"), SyncItem(this, it, "ListC"))
}
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.content.Context

import net.gotev.recycleradapter.AdapterItem

class SyncItem(context: Context, private val id: Int, private val suffix: String) : ExampleItem(context, "item $id $suffix") {
class SyncItem(context: Context, val id: Int, private val suffix: String) : ExampleItem(context, "item $id $suffix") {

override fun equals(other: Any?): Boolean {
if (other == null || javaClass != other.javaClass) {
Expand All @@ -15,9 +15,7 @@ class SyncItem(context: Context, private val id: Int, private val suffix: String
}

override fun hashCode(): Int {
var result = id
result = 31 * result + suffix.hashCode()
return result
return id.hashCode()
}

override fun compareTo(other: AdapterItem<*>): Int {
Expand Down
6 changes: 3 additions & 3 deletions app/demo/src/main/res/layout/activity_sync.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_above="@+id/syncC" />
android:layout_above="@+id/shuffle" />

<Button
android:text="Sync C"
android:text="Shuffle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/syncC"
android:id="@+id/shuffle"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
Expand Down
2 changes: 1 addition & 1 deletion manifest.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ ext {
library_licenses = ["Apache-2.0"]
library_licenses_url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
library_project_group = 'net.gotev'
library_version = '2.1.1'
library_version = '2.1.5'
version_code = 5
min_sdk = 18
target_sdk = 28
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,22 @@ class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapterViewHolder>(), Recyc
*/
fun add(item: AdapterItem<*>, position: Int? = null): RecyclerAdapter {
val insertPosition = if (position != null) {
items.add(position, item.castAsIn())
position
when {
position >= items.size -> {
items.add(item.castAsIn())
items.lastIndex
}

position < 0 -> {
items.add(0, item.castAsIn())
0
}

else -> {
items.add(position, item.castAsIn())
position
}
}
} else {
items.add(item.castAsIn())
items.lastIndex
Expand Down Expand Up @@ -364,6 +378,8 @@ class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapterViewHolder>(), Recyc
return this
}

items.ensureCapacity(newItems.size)

newItems.forEachIndexed { newItemsIndex, newItem ->
val internalItemIndex = items.indexOf(newItem)

Expand All @@ -373,13 +389,12 @@ class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapterViewHolder>(), Recyc
val internalItem = items[internalItemIndex]

if (internalItem.hasToBeReplacedBy(newItem)) {
removeItem(internalItem)
removeItemAtPosition(internalItemIndex)
add(newItem, newItemsIndex)
} else {
if (internalItemIndex != newItemsIndex) {
items.removeAt(internalItemIndex)
items.add(newItemsIndex, internalItem)
notifyItemMoved(internalItemIndex, newItemsIndex)
removeItemAtPosition(internalItemIndex)
add(internalItem, newItemsIndex)
}
}
}
Expand Down

0 comments on commit c728834

Please sign in to comment.