优化列表更新

pull/736/head
gedoor 4 years ago
parent 213a4691bb
commit 85bc488e65
  1. 4
      app/build.gradle
  2. 109
      app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt
  3. 50
      app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
  4. 2
      app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt
  5. 5
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  6. 4
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceAdapter.kt
  7. 25
      app/src/main/java/io/legado/app/ui/book/source/manage/DiffCallBack.kt
  8. 4
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BaseBooksAdapter.kt
  9. 23
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksDiffCallBack.kt
  10. 5
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt
  11. 26
      app/src/main/java/io/legado/app/ui/replace/DiffCallBack.kt
  12. 5
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt
  13. 25
      app/src/main/java/io/legado/app/ui/rss/source/manage/DiffCallBack.kt
  14. 5
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  15. 4
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt

@ -173,8 +173,8 @@ dependencies {
//
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.ljx.rxhttp:rxhttp:2.4.4-beta4'
kapt 'com.ljx.rxhttp:rxhttp-compiler:2.4.4-beta4'
implementation 'com.ljx.rxhttp:rxhttp:2.5.1'
kapt 'com.ljx.rxhttp:rxhttp-compiler:2.5.1'
//Glide
implementation 'com.github.bumptech.glide:glide:4.11.0'

@ -4,11 +4,13 @@ import android.content.Context
import android.util.SparseArray
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import java.util.*
import kotlin.collections.ArrayList
/**
* Created by Invincible on 2017/11/24.
@ -36,7 +38,10 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
private val footerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() }
private val itemDelegates: HashMap<Int, ItemViewDelegate<ITEM, VB>> = hashMapOf()
private val items: MutableList<ITEM> = mutableListOf()
private val asyncListDiffer: AsyncListDiffer<ITEM> by lazy {
AsyncListDiffer(this, diffItemCallback)
}
private val lock = Object()
@ -45,6 +50,19 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
var itemAnimation: ItemAnimation? = null
open val diffItemCallback: DiffUtil.ItemCallback<ITEM> =
object : DiffUtil.ItemCallback<ITEM>() {
override fun areItemsTheSame(oldItem: ITEM, newItem: ITEM): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: ITEM, newItem: ITEM): Boolean {
return true
}
}
fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) {
itemClickListener = listener
}
@ -115,88 +133,65 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
fun setItems(items: List<ITEM>?) {
synchronized(lock) {
if (this.items.isNotEmpty()) {
this.items.clear()
}
if (items != null) {
this.items.addAll(items)
}
notifyDataSetChanged()
}
}
fun setItems(items: List<ITEM>?, diffResult: DiffUtil.DiffResult) {
synchronized(lock) {
if (this.items.isNotEmpty()) {
this.items.clear()
}
if (items != null) {
this.items.addAll(items)
}
diffResult.dispatchUpdatesTo(this)
asyncListDiffer.submitList(items)
}
}
fun setItem(position: Int, item: ITEM) {
synchronized(lock) {
val oldSize = getActualItemCount()
if (position in 0 until oldSize) {
this.items[position] = item
notifyItemChanged(position + getHeaderCount())
}
val list = ArrayList(asyncListDiffer.currentList)
list[position] = item
asyncListDiffer.submitList(list)
}
}
fun addItem(item: ITEM) {
synchronized(lock) {
val oldSize = getActualItemCount()
if (this.items.add(item)) {
notifyItemInserted(oldSize + getHeaderCount())
}
val list = ArrayList(asyncListDiffer.currentList)
list.add(item)
asyncListDiffer.submitList(list)
}
}
fun addItems(position: Int, newItems: List<ITEM>) {
synchronized(lock) {
if (this.items.addAll(position, newItems)) {
notifyItemRangeInserted(position + getHeaderCount(), newItems.size)
}
val list = ArrayList(asyncListDiffer.currentList)
list.addAll(position, newItems)
asyncListDiffer.submitList(list)
}
}
fun addItems(newItems: List<ITEM>) {
synchronized(lock) {
val oldSize = getActualItemCount()
if (this.items.addAll(newItems)) {
if (oldSize == 0 && getHeaderCount() == 0) {
notifyDataSetChanged()
} else {
notifyItemRangeInserted(oldSize + getHeaderCount(), newItems.size)
}
}
val list = ArrayList(asyncListDiffer.currentList)
list.addAll(newItems)
asyncListDiffer.submitList(list)
}
}
fun removeItem(position: Int) {
synchronized(lock) {
if (this.items.removeAt(position) != null) {
notifyItemRemoved(position + getHeaderCount())
val list = ArrayList(asyncListDiffer.currentList)
if (list.removeAt(position) != null) {
asyncListDiffer.submitList(list)
}
}
}
fun removeItem(item: ITEM) {
synchronized(lock) {
if (this.items.remove(item)) {
notifyItemRemoved(this.items.indexOf(item) + getHeaderCount())
val list = ArrayList(asyncListDiffer.currentList)
if (list.remove(item)) {
asyncListDiffer.submitList(list)
}
}
}
fun removeItems(items: List<ITEM>) {
synchronized(lock) {
if (this.items.removeAll(items)) {
notifyDataSetChanged()
val list = ArrayList(asyncListDiffer.currentList)
if (list.removeAll(items)) {
asyncListDiffer.submitList(list)
}
}
}
@ -207,7 +202,7 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
if (oldPosition in 0 until size && newPosition in 0 until size) {
val srcPosition = oldPosition + getHeaderCount()
val targetPosition = newPosition + getHeaderCount()
Collections.swap(this.items, srcPosition, targetPosition)
Collections.swap(asyncListDiffer.currentList, srcPosition, targetPosition)
notifyItemChanged(srcPosition)
notifyItemChanged(targetPosition)
}
@ -216,9 +211,9 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
fun updateItem(item: ITEM) =
synchronized(lock) {
val index = this.items.indexOf(item)
val index = asyncListDiffer.currentList.indexOf(item)
if (index >= 0) {
this.items[index] = item
asyncListDiffer.currentList[index] = item
notifyItemChanged(index)
}
}
@ -245,18 +240,17 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
fun clearItems() =
synchronized(lock) {
this.items.clear()
notifyDataSetChanged()
asyncListDiffer.submitList(arrayListOf())
}
fun isEmpty() = items.isEmpty()
fun isEmpty() = asyncListDiffer.currentList.isEmpty()
fun isNotEmpty() = items.isNotEmpty()
fun isNotEmpty() = asyncListDiffer.currentList.isNotEmpty()
/**
* 除去header和footer
*/
fun getActualItemCount() = items.size
fun getActualItemCount() = asyncListDiffer.currentList.size
fun getHeaderCount() = headerItems.size()
@ -264,11 +258,12 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte
fun getFooterCount() = footerItems.size()
fun getItem(position: Int): ITEM? = items.getOrNull(position)
fun getItem(position: Int): ITEM? = asyncListDiffer.currentList.getOrNull(position)
fun getItemByLayoutPosition(position: Int) = items.getOrNull(position - getHeaderCount())
fun getItemByLayoutPosition(position: Int) =
asyncListDiffer.currentList.getOrNull(position - getHeaderCount())
fun getItems(): List<ITEM> = items
fun getItems(): List<ITEM> = asyncListDiffer.currentList
protected open fun getItemViewType(item: ITEM, position: Int) = 0

@ -32,7 +32,6 @@ import io.legado.app.utils.*
import io.legado.app.utils.viewbindingdelegate.viewBinding
import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.*
import kotlin.collections.ArrayList
class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
private lateinit var viewModel: GroupViewModel
@ -77,9 +76,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
private fun initData() {
App.db.bookGroupDao.liveDataAll().observe(viewLifecycleOwner, {
val diffResult =
DiffUtil.calculateDiff(GroupDiffCallBack(ArrayList(adapter.getItems()), it))
adapter.setItems(it, diffResult)
adapter.setItems(it)
})
}
@ -140,40 +137,29 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
}.show()
}
private class GroupDiffCallBack(
private val oldItems: List<BookGroup>,
private val newItems: List<BookGroup>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldItems.size
}
override fun getNewListSize(): Int {
return newItems.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return oldItem.groupId == newItem.groupId
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return oldItem.groupName == newItem.groupName
&& oldItem.show == newItem.show
}
}
private inner class GroupAdapter(context: Context) :
SimpleRecyclerAdapter<BookGroup, ItemGroupManageBinding>(context),
ItemTouchCallback.Callback {
private var isMoved = false
override val diffItemCallback: DiffUtil.ItemCallback<BookGroup>
get() = object : DiffUtil.ItemCallback<BookGroup>() {
override fun areItemsTheSame(oldItem: BookGroup, newItem: BookGroup): Boolean {
return oldItem.groupId == newItem.groupId
}
override fun areContentsTheSame(
oldItem: BookGroup,
newItem: BookGroup
): Boolean {
return oldItem.groupName == newItem.groupName
&& oldItem.show == newItem.show
}
}
override fun getViewBinding(parent: ViewGroup): ItemGroupManageBinding {
return ItemGroupManageBinding.inflate(inflater, parent, false)
}

@ -287,7 +287,7 @@ class BgTextConfigDialog : BaseDialogFragment(), FilePickerDialog.CallBack {
private fun importNetConfig(url: String) {
execute {
RxHttp.get(url).toByteArray().await()?.let {
RxHttp.get(url).toByteArray().await().let {
importConfig(it)
}
}.onError {

@ -11,7 +11,6 @@ import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.SearchView
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
@ -207,9 +206,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
Sort.Update -> data.sortedBy { it.lastUpdateTime }
else -> data.reversed()
}
val diffResult = DiffUtil
.calculateDiff(DiffCallBack(ArrayList(adapter.getItems()), sourceList))
adapter.setItems(sourceList, diffResult)
adapter.setItems(sourceList)
upCountView()
})
}

@ -8,6 +8,7 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.PopupMenu
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
@ -26,6 +27,9 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<BookSource, ItemBookSourceBinding>(context),
Callback {
override val diffItemCallback: DiffUtil.ItemCallback<BookSource>
get() = DiffCallBack()
private val selected = linkedSetOf<BookSource>()
override fun getViewBinding(parent: ViewGroup): ItemBookSourceBinding {

@ -4,28 +4,13 @@ import android.os.Bundle
import androidx.recyclerview.widget.DiffUtil
import io.legado.app.data.entities.BookSource
class DiffCallBack(
private val oldItems: List<BookSource>,
private val newItems: List<BookSource>
) : DiffUtil.Callback() {
class DiffCallBack : DiffUtil.ItemCallback<BookSource>() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun areItemsTheSame(oldItem: BookSource, newItem: BookSource): Boolean {
return oldItem.bookSourceUrl == newItem.bookSourceUrl
}
override fun getOldListSize(): Int {
return oldItems.size
}
override fun getNewListSize(): Int {
return newItems.size
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun areContentsTheSame(oldItem: BookSource, newItem: BookSource): Boolean {
if (oldItem.bookSourceName != newItem.bookSourceName)
return false
if (oldItem.bookSourceGroup != newItem.bookSourceGroup)
@ -40,9 +25,7 @@ class DiffCallBack(
return true
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun getChangePayload(oldItem: BookSource, newItem: BookSource): Any? {
val payload = Bundle()
if (oldItem.bookSourceName != newItem.bookSourceName) {
payload.putString("name", newItem.bookSourceName)

@ -2,6 +2,7 @@ package io.legado.app.ui.main.bookshelf.books
import android.content.Context
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.DiffUtil
import androidx.viewbinding.ViewBinding
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.Book
@ -9,6 +10,9 @@ import io.legado.app.data.entities.Book
abstract class BaseBooksAdapter<VB : ViewBinding>(context: Context) :
SimpleRecyclerAdapter<Book, VB>(context) {
override val diffItemCallback: DiffUtil.ItemCallback<Book>
get() = BooksDiffCallBack()
fun notification(bookUrl: String) {
for (i in 0 until itemCount) {
getItem(i)?.let {

@ -4,27 +4,14 @@ import androidx.core.os.bundleOf
import androidx.recyclerview.widget.DiffUtil
import io.legado.app.data.entities.Book
class BooksDiffCallBack(private val oldItems: List<Book>, private val newItems: List<Book>) :
DiffUtil.Callback() {
class BooksDiffCallBack() : DiffUtil.ItemCallback<Book>() {
override fun getOldListSize(): Int {
return oldItems.size
}
override fun getNewListSize(): Int {
return newItems.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean {
return oldItem.name == newItem.name
&& oldItem.author == newItem.author
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean {
return when {
oldItem.durChapterTime != newItem.durChapterTime -> false
oldItem.name != newItem.name -> false
@ -38,9 +25,7 @@ class BooksDiffCallBack(private val oldItems: List<Book>, private val newItems:
}
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun getChangePayload(oldItem: Book, newItem: Book): Any? {
val bundle = bundleOf()
if (oldItem.name != newItem.name) {
bundle.putString("name", newItem.name)

@ -4,7 +4,6 @@ import android.os.Bundle
import android.view.View
import androidx.core.view.isGone
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -117,9 +116,7 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
3 -> list.sortedBy { it.order }
else -> list.sortedByDescending { it.durChapterTime }
}
val diffResult = DiffUtil
.calculateDiff(BooksDiffCallBack(booksAdapter.getItems(), books))
booksAdapter.setItems(books, diffResult)
booksAdapter.setItems(books)
}
}
}

@ -4,27 +4,13 @@ import android.os.Bundle
import androidx.recyclerview.widget.DiffUtil
import io.legado.app.data.entities.ReplaceRule
class DiffCallBack(
private val oldItems: List<ReplaceRule>,
private val newItems: List<ReplaceRule>
) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return oldItem.id == newItem.id
}
class DiffCallBack : DiffUtil.ItemCallback<ReplaceRule>() {
override fun getOldListSize(): Int {
return oldItems.size
}
override fun getNewListSize(): Int {
return newItems.size
override fun areItemsTheSame(oldItem: ReplaceRule, newItem: ReplaceRule): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun areContentsTheSame(oldItem: ReplaceRule, newItem: ReplaceRule): Boolean {
if (oldItem.name != newItem.name) {
return false
}
@ -37,9 +23,7 @@ class DiffCallBack(
return true
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun getChangePayload(oldItem: ReplaceRule, newItem: ReplaceRule): Any? {
val payload = Bundle()
if (oldItem.name != newItem.name) {
payload.putString("name", newItem.name)

@ -11,7 +11,6 @@ import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.SearchView
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
@ -153,9 +152,7 @@ class ReplaceRuleActivity : VMBaseActivity<ActivityReplaceRuleBinding, ReplaceRu
if (dataInit) {
setResult(Activity.RESULT_OK)
}
val diffResult =
DiffUtil.calculateDiff(DiffCallBack(ArrayList(adapter.getItems()), it))
adapter.setItems(it, diffResult)
adapter.setItems(it)
dataInit = true
upCountView()
})

@ -4,36 +4,19 @@ import android.os.Bundle
import androidx.recyclerview.widget.DiffUtil
import io.legado.app.data.entities.RssSource
class DiffCallBack(
private val oldItems: List<RssSource>,
private val newItems: List<RssSource>
) : DiffUtil.Callback() {
class DiffCallBack : DiffUtil.ItemCallback<RssSource>() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun areItemsTheSame(oldItem: RssSource, newItem: RssSource): Boolean {
return oldItem.sourceUrl == newItem.sourceUrl
}
override fun getOldListSize(): Int {
return oldItems.size
}
override fun getNewListSize(): Int {
return newItems.size
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun areContentsTheSame(oldItem: RssSource, newItem: RssSource): Boolean {
return oldItem.sourceName == newItem.sourceName
&& oldItem.sourceGroup == newItem.sourceGroup
&& oldItem.enabled == newItem.enabled
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
override fun getChangePayload(oldItem: RssSource, newItem: RssSource): Any? {
val payload = Bundle()
if (oldItem.sourceName != newItem.sourceName) {
payload.putString("name", newItem.sourceName)

@ -11,7 +11,6 @@ import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.SearchView
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
@ -209,9 +208,7 @@ class RssSourceActivity : VMBaseActivity<ActivityRssSourceBinding, RssSourceView
App.db.rssSourceDao.liveSearch("%$key%")
}
sourceLiveData?.observe(this, {
val diffResult = DiffUtil
.calculateDiff(DiffCallBack(adapter.getItems(), it))
adapter.setItems(it, diffResult)
adapter.setItems(it)
upCountView()
})
}

@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
@ -22,6 +23,9 @@ class RssSourceAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<RssSource, ItemRssSourceBinding>(context),
ItemTouchCallback.Callback {
override val diffItemCallback: DiffUtil.ItemCallback<RssSource>
get() = DiffCallBack()
private val selected = linkedSetOf<RssSource>()
override fun getViewBinding(parent: ViewGroup): ItemRssSourceBinding {

Loading…
Cancel
Save