This commit is contained in:
Horis
2025-04-08 13:32:19 +08:00
parent fa845c3437
commit c67776e9b1
5 changed files with 205 additions and 132 deletions
@@ -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 {
@@ -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<VH : RecyclerView.ViewHolder>(
val context: Context,
val callBack: CallBack
) : RecyclerView.Adapter<VH>() {
val diffItemCallback = object : DiffUtil.ItemCallback<Book>() {
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<Any>() {
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<VH : RecyclerView.ViewHolder>(
}
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<VH : RecyclerView.ViewHolder>(
}
}
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<Any>
}
}
@@ -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<Any>
) {
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<Any>) = 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<Any>) = 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)
}
}
}
}
}
}
@@ -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<Any>
) {
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<Any>) = 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<Any>) = 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)
}
}
}
}
}
}
@@ -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<Book> = 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<BookGroup>) {
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<AudioPlayActivity> {
@@ -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<BookInfoActivity> {
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<Any> {
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")