From c67776e9b121b914dfa1240ba7c39f0c7785cbcb Mon Sep 17 00:00:00 2001 From: Horis <8674809+821938089@users.noreply.github.com> Date: Tue, 8 Apr 2025 13:32:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bookshelf/style1/books/BooksFragment.kt | 2 +- .../main/bookshelf/style2/BaseBooksAdapter.kt | 131 ++++++++++++------ .../main/bookshelf/style2/BooksAdapterGrid.kt | 76 +++++----- .../main/bookshelf/style2/BooksAdapterList.kt | 84 ++++++----- .../bookshelf/style2/BookshelfFragment2.kt | 44 +++--- 5 files changed, 205 insertions(+), 132 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt index b622c4bf5..0a806d535 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt @@ -204,7 +204,7 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books), private fun startLastUpdateTimeJob() { upLastUpdateTimeJob?.cancel() - if (!AppConfig.showLastUpdateTime) { + if (!AppConfig.showLastUpdateTime || bookshelfLayout != 0) { return } upLastUpdateTimeJob = lifecycleScope.launch { diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt index 1314b9c39..179ce0a67 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt @@ -1,60 +1,96 @@ package io.legado.app.ui.main.bookshelf.style2 import android.content.Context +import android.view.LayoutInflater import androidx.core.os.bundleOf +import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import io.legado.app.data.entities.Book +import io.legado.app.data.entities.BookGroup abstract class BaseBooksAdapter( val context: Context, val callBack: CallBack ) : RecyclerView.Adapter() { - val diffItemCallback = object : DiffUtil.ItemCallback() { + protected val inflater: LayoutInflater = LayoutInflater.from(context) - override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean { - return oldItem.name == newItem.name - && oldItem.author == newItem.author - } + private val diffItemCallback = object : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean { + override fun areItemsTheSame(oldItem: Any, newItem: Any): Boolean { return when { - oldItem.durChapterTime != newItem.durChapterTime -> false - oldItem.name != newItem.name -> false - oldItem.author != newItem.author -> false - oldItem.durChapterTitle != newItem.durChapterTitle -> false - oldItem.latestChapterTitle != newItem.latestChapterTitle -> false - oldItem.lastCheckCount != newItem.lastCheckCount -> false - oldItem.getDisplayCover() != newItem.getDisplayCover() -> false - oldItem.getUnreadChapterNum() != newItem.getUnreadChapterNum() -> false - else -> true + oldItem is Book && newItem is Book -> { + oldItem.name == newItem.name + && oldItem.author == newItem.author + } + + oldItem is BookGroup && newItem is BookGroup -> { + oldItem.groupId == newItem.groupId + } + + else -> false } } - override fun getChangePayload(oldItem: Book, newItem: Book): Any? { + override fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean { + return when { + oldItem is Book && newItem is Book -> { + oldItem.durChapterTime == newItem.durChapterTime && + oldItem.name == newItem.name && + oldItem.author == newItem.author && + oldItem.durChapterTitle == newItem.durChapterTitle && + oldItem.latestChapterTitle == newItem.latestChapterTitle && + oldItem.lastCheckCount == newItem.lastCheckCount && + oldItem.getDisplayCover() == newItem.getDisplayCover() && + oldItem.getUnreadChapterNum() == newItem.getUnreadChapterNum() + } + + oldItem is BookGroup && newItem is BookGroup -> { + oldItem.groupName == newItem.groupName && + oldItem.cover == newItem.cover + } + + else -> false + } + } + + override fun getChangePayload(oldItem: Any, newItem: Any): Any? { val bundle = bundleOf() - if (oldItem.name != newItem.name) { - bundle.putString("name", newItem.name) - } - if (oldItem.author != newItem.author) { - bundle.putString("author", newItem.author) - } - if (oldItem.durChapterTitle != newItem.durChapterTitle) { - bundle.putString("dur", newItem.durChapterTitle) - } - if (oldItem.latestChapterTitle != newItem.latestChapterTitle) { - bundle.putString("last", newItem.latestChapterTitle) - } - if (oldItem.getDisplayCover() != newItem.getDisplayCover()) { - bundle.putString("cover", newItem.getDisplayCover()) - } - if (oldItem.lastCheckCount != newItem.lastCheckCount - || oldItem.durChapterTime != newItem.durChapterTime - || oldItem.getUnreadChapterNum() != newItem.getUnreadChapterNum() - || oldItem.lastCheckCount != newItem.lastCheckCount - ) { - bundle.putBoolean("refresh", true) + when { + oldItem is Book && newItem is Book -> { + if (oldItem.name != newItem.name) { + bundle.putString("name", newItem.name) + } + if (oldItem.author != newItem.author) { + bundle.putString("author", newItem.author) + } + if (oldItem.durChapterTitle != newItem.durChapterTitle) { + bundle.putString("dur", newItem.durChapterTitle) + } + if (oldItem.latestChapterTitle != newItem.latestChapterTitle) { + bundle.putString("last", newItem.latestChapterTitle) + } + if (oldItem.getDisplayCover() != newItem.getDisplayCover()) { + bundle.putString("cover", newItem.getDisplayCover()) + } + if (oldItem.lastCheckCount != newItem.lastCheckCount + || oldItem.durChapterTime != newItem.durChapterTime + || oldItem.getUnreadChapterNum() != newItem.getUnreadChapterNum() + || oldItem.lastCheckCount != newItem.lastCheckCount + ) { + bundle.putBoolean("refresh", true) + } + } + + oldItem is BookGroup && newItem is BookGroup -> { + if (oldItem.groupName != newItem.groupName) { + bundle.putString("groupName", newItem.groupName) + } + if (oldItem.cover != newItem.cover) { + bundle.putString("cover", newItem.cover) + } + } } if (bundle.isEmpty) return null return bundle @@ -62,9 +98,17 @@ abstract class BaseBooksAdapter( } + private val asyncListDiffer by lazy { + AsyncListDiffer(this, diffItemCallback) + } + + fun updateItems() { + asyncListDiffer.submitList(callBack.getItems()) + } + fun notification(bookUrl: String) { for (i in 0 until itemCount) { - callBack.getItem(i).let { + getItem(i).let { if (it is Book && it.bookUrl == bookUrl) { notifyItemChanged(i, bundleOf(Pair("refresh", null))) return @@ -73,21 +117,26 @@ abstract class BaseBooksAdapter( } } + fun getItems() = asyncListDiffer.currentList + + fun getItem(position: Int) = getItems().getOrNull(position) + override fun getItemCount(): Int { - return callBack.getItemCount() + return getItems().size } override fun getItemViewType(position: Int): Int { return callBack.getItemType(position) } + final override fun onBindViewHolder(holder: VH, position: Int) {} + interface CallBack { fun onItemClick(position: Int) fun onItemLongClick(position: Int) fun isUpdate(bookUrl: String): Boolean - fun getItemCount(): Int fun getItemType(position: Int): Int - fun getItem(position: Int): Any? + fun getItems(): List } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt index 8a81ad837..98d4a2d27 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt @@ -2,7 +2,6 @@ package io.legado.app.ui.main.bookshelf.style2 import android.content.Context import android.os.Bundle -import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import io.legado.app.data.entities.Book @@ -23,12 +22,8 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) : viewType: Int ): RecyclerView.ViewHolder { return when (viewType) { - 1 -> GroupViewHolder( - ItemBookshelfGridGroupBinding.inflate(LayoutInflater.from(context), parent, false) - ) - else -> BookViewHolder( - ItemBookshelfGridBinding.inflate(LayoutInflater.from(context), parent, false) - ) + 1 -> GroupViewHolder(ItemBookshelfGridGroupBinding.inflate(inflater, parent, false)) + else -> BookViewHolder(ItemBookshelfGridBinding.inflate(inflater, parent, false)) } } @@ -37,25 +32,13 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) : position: Int, payloads: MutableList ) { - val bundle = payloads.getOrNull(0) as? Bundle - when { - bundle == null -> super.onBindViewHolder(holder, position, payloads) - holder is BookViewHolder -> (callBack.getItem(position) as? Book)?.let { - holder.onBind(it, bundle) - } - holder is GroupViewHolder -> (callBack.getItem(position) as? BookGroup)?.let { - holder.onBind(it, bundle) - } - } - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { - is BookViewHolder -> (callBack.getItem(position) as? Book)?.let { - holder.onBind(it, position) + is BookViewHolder -> (getItem(position) as? Book)?.let { + holder.onBind(it, position, payloads) } - is GroupViewHolder -> (callBack.getItem(position) as? BookGroup)?.let { - holder.onBind(it, position) + + is GroupViewHolder -> (getItem(position) as? BookGroup)?.let { + holder.onBind(it, position, payloads) } } } @@ -63,7 +46,7 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) : inner class BookViewHolder(val binding: ItemBookshelfGridBinding) : RecyclerView.ViewHolder(binding.root) { - fun onBind(item: Book, position: Int) = binding.run{ + fun onBind(item: Book, position: Int) = binding.run { tvName.text = item.name ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin) upRefresh(this, item) @@ -75,12 +58,26 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) : } } - fun onBind(item: Book, bundle: Bundle) = binding.run { - bundle.keySet().forEach { - when (it) { - "name" -> tvName.text = item.name - "cover" -> ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin) - "refresh" -> upRefresh(this, item) + fun onBind(item: Book, position: Int, payloads: MutableList) = binding.run { + if (payloads.isEmpty()) { + onBind(item, position) + } else { + for (i in payloads.indices) { + val bundle = payloads[i] as Bundle + bundle.keySet().forEach { + when (it) { + "name" -> tvName.text = item.name + "cover" -> ivCover.load( + item.getDisplayCover(), + item.name, + item.author, + false, + item.origin + ) + + "refresh" -> upRefresh(this, item) + } + } } } } @@ -116,9 +113,20 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) : } } - fun onBind(item: BookGroup, bundle: Bundle) = binding.run { - tvName.text = item.groupName - ivCover.load(item.cover) + fun onBind(item: BookGroup, position: Int, payloads: MutableList) = binding.run { + if (payloads.isEmpty()) { + onBind(item, position) + } else { + for (i in payloads.indices) { + val bundle = payloads[i] as Bundle + bundle.keySet().forEach { + when (it) { + "groupName" -> tvName.text = item.groupName + "cover" -> ivCover.load(item.cover) + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt index 466956e47..32c79ff1e 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt @@ -2,7 +2,6 @@ package io.legado.app.ui.main.bookshelf.style2 import android.content.Context import android.os.Bundle -import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import io.legado.app.data.entities.Book @@ -22,12 +21,8 @@ class BooksAdapterList(context: Context, callBack: CallBack) : override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { - 1 -> GroupViewHolder( - ItemBookshelfListGroupBinding.inflate(LayoutInflater.from(context), parent, false) - ) - else -> BookViewHolder( - ItemBookshelfListBinding.inflate(LayoutInflater.from(context), parent, false) - ) + 1 -> GroupViewHolder(ItemBookshelfListGroupBinding.inflate(inflater, parent, false)) + else -> BookViewHolder(ItemBookshelfListBinding.inflate(inflater, parent, false)) } } @@ -36,25 +31,13 @@ class BooksAdapterList(context: Context, callBack: CallBack) : position: Int, payloads: MutableList ) { - val bundle = payloads.getOrNull(0) as? Bundle - when { - bundle == null -> super.onBindViewHolder(holder, position, payloads) - holder is BookViewHolder -> (callBack.getItem(position) as? Book)?.let { - holder.onBind(it, bundle) - } - holder is GroupViewHolder -> (callBack.getItem(position) as? BookGroup)?.let { - holder.onBind(it, bundle) - } - } - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { - is BookViewHolder -> (callBack.getItem(position) as? Book)?.let { - holder.onBind(it, position) + is BookViewHolder -> (getItem(position) as? Book)?.let { + holder.onBind(it, position, payloads) } - is GroupViewHolder -> (callBack.getItem(position) as? BookGroup)?.let { - holder.onBind(it, position) + + is GroupViewHolder -> (getItem(position) as? BookGroup)?.let { + holder.onBind(it, position, payloads) } } } @@ -62,7 +45,7 @@ class BooksAdapterList(context: Context, callBack: CallBack) : inner class BookViewHolder(val binding: ItemBookshelfListBinding) : RecyclerView.ViewHolder(binding.root) { - fun onBind(item: Book, position: Int) = binding.run{ + fun onBind(item: Book, position: Int) = binding.run { tvName.text = item.name tvAuthor.text = item.author tvRead.text = item.durChapterTitle @@ -81,15 +64,29 @@ class BooksAdapterList(context: Context, callBack: CallBack) : } } - fun onBind(item: Book, bundle: Bundle) = binding.run { - tvRead.text = item.durChapterTitle - tvLast.text = item.latestChapterTitle - bundle.keySet().forEach { - when (it) { - "name" -> tvName.text = item.name - "author" -> tvAuthor.text = item.author - "cover" -> ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin) - "refresh" -> upRefresh(this, item) + fun onBind(item: Book, position: Int, payloads: MutableList) = binding.run { + if (payloads.isEmpty()) { + onBind(item, position) + } else { + for (i in payloads.indices) { + val bundle = payloads[i] as Bundle + bundle.keySet().forEach { + when (it) { + "name" -> tvName.text = item.name + "author" -> tvAuthor.text = item.author + "dur" -> tvRead.text = item.durChapterTitle + "last" -> tvLast.text = item.latestChapterTitle + "cover" -> ivCover.load( + item.getDisplayCover(), + item.name, + item.author, + false, + item.origin + ) + + "refresh" -> upRefresh(this, item) + } + } } } } @@ -114,7 +111,7 @@ class BooksAdapterList(context: Context, callBack: CallBack) : inner class GroupViewHolder(val binding: ItemBookshelfListGroupBinding) : RecyclerView.ViewHolder(binding.root) { - fun onBind(item: BookGroup, position: Int) = binding.run{ + fun onBind(item: BookGroup, position: Int) = binding.run { tvName.text = item.groupName ivCover.load(item.cover) flHasNew.gone() @@ -132,9 +129,20 @@ class BooksAdapterList(context: Context, callBack: CallBack) : } } - fun onBind(item: BookGroup, bundle: Bundle) = binding.run { - tvName.text = item.groupName - ivCover.load(item.cover) + fun onBind(item: BookGroup, position: Int, payloads: MutableList) = binding.run { + if (payloads.isEmpty()) { + onBind(item, position) + } else { + for (i in payloads.indices) { + val bundle = payloads[i] as Bundle + bundle.keySet().forEach { + when (it) { + "groupName" -> tvName.text = item.groupName + "cover" -> ivCover.load(item.cover) + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt index c77ab5bd1..4f3081f8f 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.View import androidx.appcompat.widget.SearchView import androidx.core.view.isGone +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager @@ -12,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView import io.legado.app.R import io.legado.app.constant.AppLog import io.legado.app.constant.EventBus +import io.legado.app.data.AppDatabase import io.legado.app.data.appDb import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookGroup @@ -28,6 +30,7 @@ import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.search.SearchActivity import io.legado.app.ui.main.bookshelf.BaseBookshelfFragment import io.legado.app.utils.cnCompare +import io.legado.app.utils.flowWithLifecycleAndDatabaseChangeFirst import io.legado.app.utils.observeEvent import io.legado.app.utils.setEdgeEffectColor import io.legado.app.utils.showDialogFragment @@ -70,6 +73,7 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) private var booksFlowJob: Job? = null override var groupId = BookGroup.IdRoot override var books: List = emptyList() + private var enableRefresh = true override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { setSupportToolbar(binding.titleBar.toolbar) @@ -90,6 +94,7 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) } else { binding.rvBookshelf.layoutManager = GridLayoutManager(context, bookshelfLayout + 2) } + binding.rvBookshelf.itemAnimator = null binding.rvBookshelf.adapter = booksAdapter booksAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { @@ -110,12 +115,12 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) }) } - @SuppressLint("NotifyDataSetChanged") override fun upGroup(data: List) { if (data != bookGroups) { bookGroups = data - booksAdapter.notifyDataSetChanged() + booksAdapter.updateItems() binding.tvEmptyMsg.isGone = getItemCount() > 0 + binding.refreshLayout.isEnabled = enableRefresh && getItemCount() > 0 } } @@ -123,12 +128,12 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) initBooksData() } - @SuppressLint("NotifyDataSetChanged") private fun initBooksData() { - if (groupId == -100L) { + if (groupId == BookGroup.IdRoot) { if (isAdded) { binding.titleBar.title = getString(R.string.bookshelf) binding.refreshLayout.isEnabled = true + enableRefresh = true } } else { bookGroups.firstOrNull { @@ -136,6 +141,7 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) }?.let { binding.titleBar.title = "${getString(R.string.bookshelf)}(${it.groupName})" binding.refreshLayout.isEnabled = it.enableRefresh + enableRefresh = it.enableRefresh } } booksFlowJob?.cancel() @@ -163,20 +169,25 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) it.durChapterTime } } - }.flowOn(Dispatchers.Default).catch { + }.flowWithLifecycleAndDatabaseChangeFirst( + viewLifecycleOwner.lifecycle, + Lifecycle.State.RESUMED, + AppDatabase.BOOK_TABLE_NAME + ).catch { AppLog.put("书架更新出错", it) - }.conflate().collect { list -> + }.conflate().flowOn(Dispatchers.Default).collect { list -> books = list - booksAdapter.notifyDataSetChanged() + booksAdapter.updateItems() binding.tvEmptyMsg.isGone = getItemCount() > 0 + binding.refreshLayout.isEnabled = enableRefresh && getItemCount() > 0 delay(100) } } } fun back(): Boolean { - if (groupId != -100L) { - groupId = -100L + if (groupId != BookGroup.IdRoot) { + groupId = BookGroup.IdRoot initBooksData() return true } @@ -201,7 +212,7 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) } override fun onItemClick(position: Int) { - when (val item = getItem(position)) { + when (val item = booksAdapter.getItem(position)) { is Book -> when { item.isAudio -> startActivity { @@ -221,7 +232,7 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) } override fun onItemLongClick(position: Int) { - when (val item = getItem(position)) { + when (val item = booksAdapter.getItem(position)) { is Book -> startActivity { putExtra("name", item.name) putExtra("author", item.author) @@ -235,7 +246,7 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) return activityViewModel.isUpdate(bookUrl) } - override fun getItemCount(): Int { + fun getItemCount(): Int { return if (groupId == BookGroup.IdRoot) { bookGroups.size + books.size } else { @@ -253,14 +264,11 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2) return 0 } - override fun getItem(position: Int): Any? { + override fun getItems(): List { if (groupId != BookGroup.IdRoot) { - return books.getOrNull(position) + return books } - if (position < bookGroups.size) { - return bookGroups[position] - } - return books.getOrNull(position - bookGroups.size) + return bookGroups + books } @SuppressLint("NotifyDataSetChanged")