From 684965f3e56ceb64f8f7be0a23d6a884480eaed2 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Wed, 13 Oct 2021 20:23:38 +0700 Subject: [PATCH] MangaController optimizations (#6089) * MangaController: Fix ignored stable ids * MangaController: Replace notifyDataSetChanged * ChaptersSettingsSheet: Optimizations --- .../tachiyomi/ui/manga/MangaController.kt | 45 ++++++++++--------- .../tachiyomi/ui/manga/MangaPresenter.kt | 1 + .../ui/manga/chapter/ChaptersSettingsSheet.kt | 34 +++++++------- .../chapter/MangaChaptersHeaderAdapter.kt | 8 ++-- .../ui/manga/info/MangaInfoHeaderAdapter.kt | 10 +++-- 5 files changed, 52 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index d9469de49b..fdda1924d5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -85,7 +85,6 @@ import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.util.chapter.NoChaptersException import eu.kanade.tachiyomi.util.hasCustomCover import eu.kanade.tachiyomi.util.lang.launchIO -import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.toShareIntent @@ -249,13 +248,21 @@ class MangaController : if (manga == null || source == null) return // Init RecyclerView and adapter - mangaInfoAdapter = MangaInfoHeaderAdapter(this, fromSource, binding.infoRecycler != null) - chaptersHeaderAdapter = MangaChaptersHeaderAdapter(this) + mangaInfoAdapter = MangaInfoHeaderAdapter(this, fromSource, binding.infoRecycler != null).apply { + setHasStableIds(true) + } + chaptersHeaderAdapter = MangaChaptersHeaderAdapter(this).apply { + setHasStableIds(true) + } chaptersAdapter = ChaptersAdapter(this, view.context) // Phone layout binding.fullRecycler?.let { - it.adapter = ConcatAdapter(mangaInfoAdapter, chaptersHeaderAdapter, chaptersAdapter) + val config = ConcatAdapter.Config.Builder() + .setIsolateViewTypes(true) + .setStableIdMode(ConcatAdapter.Config.StableIdMode.SHARED_STABLE_IDS) + .build() + it.adapter = ConcatAdapter(config, mangaInfoAdapter, chaptersHeaderAdapter, chaptersAdapter) // Skips directly to chapters list if navigated to from the library it.post { @@ -318,7 +325,6 @@ class MangaController : settingsSheet = ChaptersSettingsSheet(router, presenter) { group -> if (group is ChaptersSettingsSheet.Filter.FilterGroup) { updateFilterIconState() - chaptersAdapter?.notifyDataSetChanged() } } @@ -620,7 +626,7 @@ class MangaController : } } } - mangaInfoAdapter?.notifyDataSetChanged() + mangaInfoAdapter?.update() } fun onCategoriesClick() { @@ -818,7 +824,7 @@ class MangaController : override fun deleteMangaCover(manga: Manga) { presenter.deleteCustomCover(manga) - mangaInfoAdapter?.notifyDataSetChanged() + mangaInfoAdapter?.notifyItemChanged(0, manga) destroyActionModeIfNeeded() } @@ -832,7 +838,7 @@ class MangaController : } fun onSetCoverSuccess() { - mangaInfoAdapter?.notifyDataSetChanged() + mangaInfoAdapter?.notifyItemChanged(0, this) (dialog as? MangaFullCoverDialog)?.setImage(manga) activity?.toast(R.string.cover_updated) } @@ -943,19 +949,20 @@ class MangaController : val lastClickPosition = lastClickPositionStack.peek()!! when { lastClickPosition == -1 -> setSelection(position) - lastClickPosition > position -> - for (i in position until lastClickPosition) - setSelection(i) - lastClickPosition < position -> - for (i in lastClickPosition + 1..position) - setSelection(i) + lastClickPosition > position -> { + for (i in position until lastClickPosition) setSelection(i) + chaptersAdapter?.notifyItemRangeChanged(position, lastClickPosition, position) + } + lastClickPosition < position -> { + for (i in lastClickPosition + 1..position) setSelection(i) + chaptersAdapter?.notifyItemRangeChanged(lastClickPosition + 1, position, position) + } else -> setSelection(position) } if (lastClickPosition != position) { lastClickPositionStack.remove(position) // move to top if already exists lastClickPositionStack.push(position) } - chaptersAdapter?.notifyDataSetChanged() } fun showSettingsSheet() { @@ -968,7 +975,6 @@ class MangaController : val adapter = chaptersAdapter ?: return val item = adapter.getItem(position) ?: return adapter.toggleSelection(position) - adapter.notifyDataSetChanged() if (adapter.isSelected(position)) { selectedChapters.add(item) } else { @@ -1101,11 +1107,11 @@ class MangaController : selectedChapters.clear() for (i in 0..adapter.itemCount) { adapter.toggleSelection(i) + adapter.notifyItemChanged(i, i) } selectedChapters.addAll(adapter.selectedPositions.mapNotNull { adapter.getItem(it) }) actionMode?.invalidate() - adapter.notifyDataSetChanged() } private fun markAsRead(chapters: List) { @@ -1172,10 +1178,7 @@ class MangaController : fun onChaptersDeleted(chapters: List) { // this is needed so the downloaded text gets removed from the item chapters.forEach { - chaptersAdapter?.updateItem(it) - } - launchUI { - chaptersAdapter?.notifyDataSetChanged() + chaptersAdapter?.updateItem(it, it) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index 23fc18fff5..bb051b6051 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -718,6 +718,7 @@ class MangaPresenter( fun setDisplayMode(mode: Int) { manga.displayMode = mode db.updateChapterFlags(manga).executeAsBlocking() + refreshChapters() } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt index f4cd39a661..4593eaa4a3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersSettingsSheet.kt @@ -116,7 +116,7 @@ class ChaptersSettingsSheet( } initModels() - item.group.items.forEach { adapter.notifyItemChanged(it) } + adapter.notifyItemChanged(items.indexOf(item), item) } } } @@ -158,18 +158,18 @@ class ChaptersSettingsSheet( } override fun onItemClicked(item: Item) { - item as Item.MultiStateGroup - val prevState = item.state - - item.group.items.forEach { - (it as Item.MultiStateGroup).state = + items.forEachIndexed { i, multiSort -> + multiSort.state = if (multiSort == item) { + when (item.state) { + Item.MultiSort.SORT_NONE -> Item.MultiSort.SORT_ASC + Item.MultiSort.SORT_ASC -> Item.MultiSort.SORT_DESC + Item.MultiSort.SORT_DESC -> Item.MultiSort.SORT_ASC + else -> throw Exception("Unknown state") + } + } else { Item.MultiSort.SORT_NONE - } - item.state = when (prevState) { - Item.MultiSort.SORT_NONE -> Item.MultiSort.SORT_ASC - Item.MultiSort.SORT_ASC -> Item.MultiSort.SORT_DESC - Item.MultiSort.SORT_DESC -> Item.MultiSort.SORT_ASC - else -> throw Exception("Unknown state") + } + adapter.notifyItemChanged(i, multiSort) } when (item) { @@ -180,8 +180,6 @@ class ChaptersSettingsSheet( } presenter.reverseSortOrder() - - item.group.items.forEach { adapter.notifyItemChanged(it) } } } } @@ -215,16 +213,16 @@ class ChaptersSettingsSheet( item as Item.Radio if (item.checked) return - item.group.items.forEach { (it as Item.Radio).checked = false } - item.checked = true + items.forEachIndexed { index, radio -> + radio.checked = item == radio + adapter.notifyItemChanged(index, radio) + } when (item) { displayTitle -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NAME) displayChapterNum -> presenter.setDisplayMode(Manga.CHAPTER_DISPLAY_NUMBER) else -> throw NotImplementedError("Unknown display mode") } - - item.group.items.forEach { adapter.notifyItemChanged(it) } } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/MangaChaptersHeaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/MangaChaptersHeaderAdapter.kt index 72e634c162..4ae569a63e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/MangaChaptersHeaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/MangaChaptersHeaderAdapter.kt @@ -30,20 +30,20 @@ class MangaChaptersHeaderAdapter( override fun getItemCount(): Int = 1 + override fun getItemId(position: Int): Long = hashCode().toLong() + override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) { holder.bind() } fun setNumChapters(numChapters: Int) { this.numChapters = numChapters - - notifyDataSetChanged() + notifyItemChanged(0, this) } fun setHasActiveFilters(hasActiveFilters: Boolean) { this.hasActiveFilters = hasActiveFilters - - notifyDataSetChanged() + notifyItemChanged(0, this) } inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt index ca47583d06..a6eda2c59a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt @@ -56,6 +56,8 @@ class MangaInfoHeaderAdapter( override fun getItemCount(): Int = 1 + override fun getItemId(position: Int): Long = hashCode().toLong() + override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) { holder.bind() } @@ -69,14 +71,16 @@ class MangaInfoHeaderAdapter( fun update(manga: Manga, source: Source) { this.manga = manga this.source = source + update() + } - notifyDataSetChanged() + fun update() { + notifyItemChanged(0, this) } fun setTrackingCount(trackCount: Int) { this.trackCount = trackCount - - notifyDataSetChanged() + update() } private fun updateCoverPosition() {