From 50cd2f56fe64c0b211612cef3abdacaf0c35ac91 Mon Sep 17 00:00:00 2001 From: HapeLee <1321903405@qq.com> Date: Wed, 11 Mar 2026 00:20:55 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BF=AE=E5=A4=8D]=20=E5=81=B6=E7=8E=B0?= =?UTF-8?q?=E7=9A=84=E6=97=A0=E6=B3=95=E9=80=80=E5=87=BA=E4=B9=A6=E6=BA=90?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/base/adapter/RecyclerAdapter.kt | 44 ++++++++++++++----- .../book/source/manage/BookSourceActivity.kt | 30 ++++++++----- .../book/source/manage/BookSourceViewModel.kt | 25 ++++++++--- 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/io/legado/app/base/adapter/RecyclerAdapter.kt b/app/src/main/java/io/legado/app/base/adapter/RecyclerAdapter.kt index 98090cc27..6a1b40e88 100644 --- a/app/src/main/java/io/legado/app/base/adapter/RecyclerAdapter.kt +++ b/app/src/main/java/io/legado/app/base/adapter/RecyclerAdapter.kt @@ -5,6 +5,7 @@ import android.content.Context import android.util.SparseArray import android.view.LayoutInflater import android.view.ViewGroup +import androidx.core.util.size import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -37,6 +38,8 @@ abstract class RecyclerAdapter(protected val context: Co private var diffJob: Coroutine<*>? = null + private var isResumed = false + var itemAnimation: ItemAnimation? = null fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) { @@ -54,8 +57,8 @@ abstract class RecyclerAdapter(protected val context: Co @Synchronized fun addHeaderView(header: ((parent: ViewGroup) -> ViewBinding)) { kotlin.runCatching { - val index = headerItems.size() - headerItems.put(TYPE_HEADER_VIEW + headerItems.size(), header) + val index = headerItems.size + headerItems.put(TYPE_HEADER_VIEW + headerItems.size, header) notifyItemInserted(index) } } @@ -63,8 +66,8 @@ abstract class RecyclerAdapter(protected val context: Co @Synchronized fun addFooterView(footer: ((parent: ViewGroup) -> ViewBinding)) { kotlin.runCatching { - val index = getActualItemCount() + footerItems.size() - footerItems.put(TYPE_FOOTER_VIEW + footerItems.size(), footer) + val index = getActualItemCount() + footerItems.size + footerItems.put(TYPE_FOOTER_VIEW + footerItems.size, footer) notifyItemInserted(index) } } @@ -113,6 +116,10 @@ abstract class RecyclerAdapter(protected val context: Co skipDiff: Boolean = false ) { kotlin.runCatching { + if (!isResumed) { //全量标记更新 + setItems(items) + return@runCatching + } val oldItems = this.items.toList() val itemsSize = items?.size ?: 0 val headerCount = getHeaderCount() @@ -153,6 +160,10 @@ abstract class RecyclerAdapter(protected val context: Co return itemCallback.getChangePayload(oldItem, newItem) } } + if (!isResumed) { + setItems(items) + return@runCatching + } diffJob?.cancel() diffJob = Coroutine.async { val diffResult = if (skipDiff) withTimeoutOrNullAsync(500L) { @@ -162,7 +173,7 @@ abstract class RecyclerAdapter(protected val context: Co } ensureActive() handler.post { - if (diffResult == null) { + if (isResumed || diffResult == null) { setItems(items) return@post } @@ -172,6 +183,10 @@ abstract class RecyclerAdapter(protected val context: Co if (items != null) { this@RecyclerAdapter.items.addAll(items) } + if (!isResumed) { + return@post + } + ensureActive() diffResult.dispatchUpdatesTo(this@RecyclerAdapter) onCurrentListChanged() } @@ -329,10 +344,10 @@ abstract class RecyclerAdapter(protected val context: Co fun getActualItemCount() = items.size - fun getHeaderCount() = headerItems.size() + fun getHeaderCount() = headerItems.size - fun getFooterCount() = footerItems.size() + fun getFooterCount() = footerItems.size fun getItem(position: Int): ITEM? = items.getOrNull(position) @@ -431,6 +446,15 @@ abstract class RecyclerAdapter(protected val context: Co } } + fun upResumed(isResumed: Boolean) { + if (!isResumed) { + diffJob?.cancel() + diffJob = null + handler.removeCallbacksAndMessages(null) + } + this.isResumed = isResumed + } + private fun isHeader(position: Int) = position < getHeaderCount() private fun isFooter(position: Int) = position >= getActualItemCount() + getHeaderCount() @@ -479,8 +503,4 @@ abstract class RecyclerAdapter(protected val context: Co private val handler by lazy { buildMainHandler() } } -} - - - - +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index b90d28af7..ea92a0d32 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.os.Bundle import android.view.Menu import android.view.MenuItem -import android.view.MotionEvent import android.view.SubMenu import android.view.WindowManager import androidx.activity.viewModels @@ -31,8 +30,7 @@ import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.help.DirectLinkUpload import io.legado.app.help.config.LocalConfig import io.legado.app.lib.dialogs.alert -//import io.legado.app.lib.theme.primaryColor -//import io.legado.app.lib.theme.primaryTextColor +import io.legado.app.lib.theme.primaryColor import io.legado.app.model.CheckSource import io.legado.app.model.Debug import io.legado.app.ui.association.ImportBookSourceDialog @@ -53,13 +51,12 @@ import io.legado.app.utils.cnCompare import io.legado.app.utils.dpToPx import io.legado.app.utils.flowWithLifecycleAndDatabaseChange import io.legado.app.utils.flowWithLifecycleAndDatabaseChangeFirst -import io.legado.app.utils.hideSoftInput import io.legado.app.utils.isAbsUrl import io.legado.app.utils.launch import io.legado.app.utils.observeEvent import io.legado.app.utils.sendToClip +import io.legado.app.utils.setEdgeEffectColor import io.legado.app.utils.share -import io.legado.app.utils.shouldHideSoftInput import io.legado.app.utils.showDialogFragment import io.legado.app.utils.showHelp import io.legado.app.utils.splitNotBlank @@ -272,7 +269,7 @@ class BookSourceActivity : VMBaseActivity + ) { file, name -> exportDir.launch { mode = HandleFileContract.EXPORT fileData = HandleFileContract.FileData( - "bookSource.json", + name, file, "application/json" ) @@ -488,8 +494,8 @@ class BookSourceActivity : VMBaseActivity + share(file) } R.id.menu_check_selected_interval -> adapter.checkSelectedInterval() @@ -594,7 +600,7 @@ class BookSourceActivity : VMBaseActivity, success: (file: File) -> Unit) { + private fun saveToFile( + sources: List, + name: String, + success: (file: File, name: String) -> Unit + ) { execute { val path = "${context.filesDir}/shareBookSource.json" FileUtils.delete(path) @@ -133,7 +140,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application) } file }.onSuccess { - success.invoke(it) + success.invoke(it, name) }.onError { context.toastOnUi(it.stackTraceStr) } @@ -144,11 +151,12 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application) searchKey: String?, sortAscending: Boolean, sort: BookSourceSort, - success: (file: File) -> Unit + success: (file: File, name: String) -> Unit ) { execute { val selection = adapter.selection - val selectedRate = selection.size.toFloat() / adapter.itemCount.toFloat() + val selectionSize = selection.size + val selectedRate = selectionSize.toFloat() / adapter.itemCount.toFloat() val sources = if (selectedRate == 1f) { getBookSources(searchKey, sortAscending, sort) } else if (selectedRate < 0.3) { @@ -160,7 +168,14 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application) keys.contains(it.bookSourceUrl) } } - saveToFile(sources, success) + val name = if (selectionSize == 1) { + "bookSource_${selection.first().bookSourceName.normalizeFileName()}.json" + } else { + val timestamp = + java.text.SimpleDateFormat("yyyyMMddHHmm", Locale.getDefault()).format(Date()) + "bookSource_$timestamp.json" + } + saveToFile(sources, name, success) } }