From 9b3ffd1f2c2e2add6eb487f46418bb5d314cf833 Mon Sep 17 00:00:00 2001 From: tarsin Date: Sun, 16 Jul 2023 21:59:48 +0800 Subject: [PATCH] Refactor EasyRecyclerView --- .../ehviewer/ui/legacy/EasyRecyclerView.kt | 470 ------------------ .../SelectableGalleryInfoRecyclerView.kt | 125 +++++ .../hippo/ehviewer/ui/scene/DownloadsScene.kt | 33 +- .../hippo/ehviewer/ui/scene/FavoritesScene.kt | 18 +- app/src/main/res/layout/scene_download.xml | 2 +- app/src/main/res/layout/scene_favorites.xml | 2 +- 6 files changed, 147 insertions(+), 503 deletions(-) delete mode 100644 app/src/main/java/com/hippo/ehviewer/ui/legacy/EasyRecyclerView.kt create mode 100644 app/src/main/java/com/hippo/ehviewer/ui/legacy/SelectableGalleryInfoRecyclerView.kt diff --git a/app/src/main/java/com/hippo/ehviewer/ui/legacy/EasyRecyclerView.kt b/app/src/main/java/com/hippo/ehviewer/ui/legacy/EasyRecyclerView.kt deleted file mode 100644 index e9a7897df..000000000 --- a/app/src/main/java/com/hippo/ehviewer/ui/legacy/EasyRecyclerView.kt +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Copyright (C) 2015 Hippo Seven - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.hippo.ehviewer.ui.legacy - -import android.content.Context -import android.os.Parcel -import android.os.Parcelable -import android.util.AttributeSet -import android.util.SparseBooleanArray -import android.view.View -import android.widget.Checkable -import androidx.collection.LongSparseArray -import androidx.core.os.ParcelCompat.readParcelable -import androidx.recyclerview.widget.RecyclerView - -class EasyRecyclerView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyle: Int = 0, -) : RecyclerView(context, attrs, defStyle) { - // Fling friction - private var pageScrollThreshold = 0f - - /** - * Controls if/how the user may choose/check items in the list - */ - private var mChoiceMode = CHOICE_MODE_NONE - - /** - * Listener for custom multiple choices - */ - private var mCustomChoiceListener: CustomChoiceListener? = null - var isInCustomChoice = false - private set - - /** - * A lock, avoid OutOfCustomChoiceMode when doing OutOfCustomChoiceMode - */ - private var mOutOfCustomChoiceModing = false - private var mTempCheckStates: SparseBooleanArray? = null - - /** - * Running count of how many items are currently checked - */ - var checkedItemCount = 0 - private set - - /** - * Running state of which positions are currently checked - */ - private var mCheckStates: SparseBooleanArray? = null - - /** - * Running state of which IDs are currently checked. - * If there is a value for a given key, the checked state for that ID is true - * and the value holds the last known position in the adapter for that id. - */ - private var mCheckedIdStates: LongSparseArray? = null - private var mAdapter: Adapter<*>? = null - - /** - * {@inheritDoc} - */ - override fun setAdapter(adapter: Adapter<*>?) { - super.setAdapter(adapter) - mAdapter = adapter - if (adapter != null) { - if (mChoiceMode != CHOICE_MODE_NONE && adapter.hasStableIds() && mCheckedIdStates == null) { - mCheckedIdStates = LongSparseArray() - } - } - if (mCheckStates != null) { - mCheckStates!!.clear() - } - if (mCheckedIdStates != null) { - mCheckedIdStates!!.clear() - } - } - - override fun onChildAttachedToWindow(child: View) { - super.onChildAttachedToWindow(child) - if (mCheckStates != null) { - val position = getChildAdapterPosition(child) - if (position >= 0) { - setViewChecked(child, mCheckStates!![position]) - } - } - } - - /** - * Returns the checked state of the specified position. The result is only - * valid if the choice mode has been set to [.CHOICE_MODE_SINGLE] - * or [.CHOICE_MODE_MULTIPLE]. - * - * @param position The item whose checked state to return - * @return The item's checked state or `false` if choice mode - * is invalid - * @see .setChoiceMode - */ - fun isItemChecked(position: Int): Boolean { - return if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) { - mCheckStates!![position] - } else { - false - } - } - - val checkedItemPositions: SparseBooleanArray? - /** - * Returns the set of checked items in the list. The result is only valid if - * the choice mode has not been set to [.CHOICE_MODE_NONE]. - * - * @return A SparseBooleanArray which will return true for each call to - * get(int position) where position is a checked position in the - * list and false otherwise, or `null` if the choice - * mode is set to [.CHOICE_MODE_NONE]. - */ - get() = if (mChoiceMode != CHOICE_MODE_NONE) { - mCheckStates - } else { - null - } - - fun intoCustomChoiceMode() { - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !isInCustomChoice) { - isInCustomChoice = true - mCustomChoiceListener!!.onIntoCustomChoice(this) - } - } - - fun outOfCustomChoiceMode() { - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && isInCustomChoice && !mOutOfCustomChoiceModing) { - mOutOfCustomChoiceModing = true - - // Copy mCheckStates - mTempCheckStates!!.clear() - run { - for (i in 0 until mCheckStates!!.size()) { - mTempCheckStates!!.put(mCheckStates!!.keyAt(i), mCheckStates!!.valueAt(i)) - } - } - // Uncheck remain checked items - for (i in 0 until mTempCheckStates!!.size()) { - if (mTempCheckStates!!.valueAt(i)) { - setItemChecked(mTempCheckStates!!.keyAt(i), false) - } - } - isInCustomChoice = false - mCustomChoiceListener!!.onOutOfCustomChoice(this) - mOutOfCustomChoiceModing = false - } - } - - /** - * Clear any choices previously set - */ - private fun clearChoices() { - if (mCheckStates != null) { - mCheckStates!!.clear() - } - if (mCheckedIdStates != null) { - mCheckedIdStates!!.clear() - } - checkedItemCount = 0 - updateOnScreenCheckedViews() - } - - /** - * Sets all items checked. - */ - fun checkAll() { - if (mChoiceMode == CHOICE_MODE_NONE) { - return - } - - // Check is intoCheckMode - check(!(mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !isInCustomChoice)) { "Call intoCheckMode first" } - - for (i in 0 until mAdapter!!.itemCount) { - val oldValue = mCheckStates!![i] - mCheckStates!!.put(i, true) - if (mCheckedIdStates != null && mAdapter!!.hasStableIds()) { - mCheckedIdStates!!.put(mAdapter!!.getItemId(i), i) - } - if (!oldValue) { - checkedItemCount++ - } - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { - val id = mAdapter!!.getItemId(i) - mCustomChoiceListener!!.onItemCheckedStateChanged(this, i, id, true) - } - } - updateOnScreenCheckedViews() - } - - fun toggleItemChecked(position: Int) { - if (mCheckStates != null) { - setItemChecked(position, !mCheckStates!![position]) - } - } - - /** - * Sets the checked state of the specified position. The is only valid if - * the choice mode has been set to [.CHOICE_MODE_SINGLE] or - * [.CHOICE_MODE_MULTIPLE]. - * - * @param position The item whose checked state is to be checked - * @param value The new checked state for the item - */ - fun setItemChecked(position: Int, value: Boolean) { - if (mChoiceMode == CHOICE_MODE_NONE) { - return - } - - // Check is intoCheckMode - check(!(mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !isInCustomChoice)) { "Call intoCheckMode first" } - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { - val oldValue = mCheckStates!![position] - mCheckStates!!.put(position, value) - if (mCheckedIdStates != null && mAdapter!!.hasStableIds()) { - if (value) { - mCheckedIdStates!!.put(mAdapter!!.getItemId(position), position) - } else { - mCheckedIdStates!!.remove(mAdapter!!.getItemId(position)) - } - } - if (oldValue != value) { - if (value) { - checkedItemCount++ - } else { - checkedItemCount-- - } - } - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { - val id = mAdapter!!.getItemId(position) - mCustomChoiceListener!!.onItemCheckedStateChanged(this, position, id, value) - } - } else { - val updateIds = mCheckedIdStates != null && mAdapter!!.hasStableIds() - // Clear all values if we're checking something, or unchecking the currently - // selected item - if (value || isItemChecked(position)) { - mCheckStates!!.clear() - if (updateIds) { - mCheckedIdStates!!.clear() - } - } - // this may end up selecting the value we just cleared but this way - // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on - if (value) { - mCheckStates!!.put(position, true) - if (updateIds) { - mCheckedIdStates!!.put(mAdapter!!.getItemId(position), position) - } - checkedItemCount = 1 - } else if (mCheckStates!!.size() == 0 || !mCheckStates!!.valueAt(0)) { - checkedItemCount = 0 - } - } - updateOnScreenCheckedViews() - } - - /** - * Defines the choice behavior for the List. By default, Lists do not have any choice behavior - * ([.CHOICE_MODE_NONE]). By setting the choiceMode to [.CHOICE_MODE_SINGLE], the - * List allows up to one item to be in a chosen state. By setting the choiceMode to - * [.CHOICE_MODE_MULTIPLE], the list allows any number of items to be chosen. - * - * @param choiceMode One of [.CHOICE_MODE_NONE], [.CHOICE_MODE_SINGLE], or - * [.CHOICE_MODE_MULTIPLE] - */ - fun setChoiceMode(choiceMode: Int) { - mChoiceMode = choiceMode - if (mChoiceMode != CHOICE_MODE_NONE) { - if (mCheckStates == null) { - mCheckStates = SparseBooleanArray(0) - } - if (mCheckedIdStates == null && mAdapter != null && mAdapter!!.hasStableIds()) { - mCheckedIdStates = LongSparseArray(0) - } - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { - if (mTempCheckStates == null) { - mTempCheckStates = SparseBooleanArray(0) - } - clearChoices() - } - } - } - - fun setCustomCheckedListener(listener: CustomChoiceListener?) { - mCustomChoiceListener = listener - } - - /** - * Perform a quick, in-place update of the checked or activated state - * on all visible item views. This should only be called when a valid - * choice mode is active. - */ - private fun updateOnScreenCheckedViews() { - val count = childCount - for (i in 0 until count) { - val child = getChildAt(i) - val position = getChildAdapterPosition(child) - setViewChecked(child, mCheckStates!![position]) - } - } - - init { - val scale = context.resources.displayMetrics.density - // g (m/s^2) - // inch/meter - // A context-specific coefficient adjusted to physical values. - // look and feel tuning - pageScrollThreshold = scale * 80 - } - - override fun onSaveInstanceState(): Parcelable { - val ss = SavedState(super.onSaveInstanceState()) - ss.choiceMode = mChoiceMode - ss.customChoice = isInCustomChoice - ss.checkedItemCount = checkedItemCount - ss.checkState = mCheckStates - ss.checkIdState = mCheckedIdStates - return ss - } - - override fun onRestoreInstanceState(state: Parcelable) { - val ss = state as SavedState - super.onRestoreInstanceState(ss.superState) - setChoiceMode(ss.choiceMode) - isInCustomChoice = ss.customChoice - checkedItemCount = ss.checkedItemCount - if (ss.checkState != null) { - mCheckStates = ss.checkState - } - if (ss.checkIdState != null) { - mCheckedIdStates = ss.checkIdState - } - updateOnScreenCheckedViews() - } - - /** - * Custom checked - */ - interface CustomChoiceListener { - fun onIntoCustomChoice(view: EasyRecyclerView) - fun onOutOfCustomChoice(view: EasyRecyclerView) - fun onItemCheckedStateChanged( - view: EasyRecyclerView, - position: Int, - id: Long, - checked: Boolean, - ) - } - - /** - * This saved state class is a Parcelable and should not extend - * [android.view.View.BaseSavedState] nor [android.view.AbsSavedState] - * because its super class AbsSavedState's constructor - * currently passes null - * as a class loader to read its superstate from Parcelable. - * This causes [android.os.BadParcelableException] when restoring saved states. - * - * - * The super class "RecyclerView" is a part of the support library, - * and restoring its saved state requires the class loader that loaded the RecyclerView. - * It seems that the class loader is not required when restoring from RecyclerView itself, - * but it is required when restoring from RecyclerView's subclasses. - */ - internal open class SavedState : Parcelable { - var choiceMode = 0 - var customChoice = false - var checkedItemCount = 0 - var checkState: SparseBooleanArray? = null - var checkIdState: LongSparseArray? = null - - // This keeps the parent(RecyclerView)'s state - var superState: Parcelable? - - constructor() { - superState = null - } - - /** - * Constructor called from [.onSaveInstanceState] - */ - constructor(superState: Parcelable?) { - this.superState = if (superState !== EMPTY_STATE) superState else null - } - - /** - * Constructor called from [.CREATOR] - */ - private constructor(`in`: Parcel) { - // Parcel 'in' has its parent(RecyclerView)'s saved state. - // To restore it, class loader that loaded RecyclerView is required. - val superState = - readParcelable(`in`, RecyclerView::class.java.classLoader, Parcelable::class.java) - this.superState = superState ?: EMPTY_STATE - choiceMode = `in`.readInt() - customChoice = `in`.readInt() != 0 - checkedItemCount = `in`.readInt() - checkState = `in`.readSparseBooleanArray() - val n = `in`.readInt() - if (n > 0) { - checkIdState = LongSparseArray() - for (i in 0 until n) { - val key = `in`.readLong() - val value = `in`.readInt() - checkIdState!!.put(key, value) - } - } - } - - override fun describeContents(): Int { - return 0 - } - - override fun writeToParcel(out: Parcel, flags: Int) { - out.writeParcelable(superState, flags) - out.writeInt(choiceMode) - out.writeInt(customChoice.compareTo(false)) - out.writeInt(checkedItemCount) - out.writeSparseBooleanArray(checkState) - val n = if (checkIdState != null) checkIdState!!.size() else 0 - out.writeInt(n) - for (i in 0 until n) { - out.writeLong(checkIdState!!.keyAt(i)) - out.writeInt(checkIdState!!.valueAt(i)) - } - } - - companion object CREATOR : Parcelable.Creator { - private val EMPTY_STATE = SavedState() - - override fun createFromParcel(parcel: Parcel): SavedState { - return SavedState(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } - } - - companion object { - const val CHOICE_MODE_NONE = 0 - const val CHOICE_MODE_MULTIPLE_CUSTOM = 1 - fun setViewChecked(view: View, checked: Boolean) { - if (view is Checkable) { - (view as Checkable).isChecked = checked - } else { - view.isActivated = checked - } - } - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/legacy/SelectableGalleryInfoRecyclerView.kt b/app/src/main/java/com/hippo/ehviewer/ui/legacy/SelectableGalleryInfoRecyclerView.kt new file mode 100644 index 000000000..a64fa1e27 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/legacy/SelectableGalleryInfoRecyclerView.kt @@ -0,0 +1,125 @@ +package com.hippo.ehviewer.ui.legacy + +import android.content.Context +import android.os.Parcelable +import android.util.AttributeSet +import android.view.View +import android.widget.Checkable +import androidx.core.view.children +import androidx.recyclerview.widget.RecyclerView +import kotlinx.parcelize.Parcelize + +class SelectableGalleryInfoRecyclerView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0, +) : RecyclerView(context, attrs, defStyle) { + private var customChoiceListener: CustomChoiceListener? = null + private var mOutOfCustomChoiceModing = false + var isInCustomChoice = false + private set + val checkedItemCount + get() = checkedGid.size + lateinit var gidGetter: (Int) -> Long? + private var checkedGid = mutableSetOf() + + private fun checkChild(child: View) { + val position = getChildAdapterPosition(child) + val gid = gidGetter(position) ?: return + setViewChecked(child, gid in checkedGid) + } + + override fun onChildAttachedToWindow(child: View) { + super.onChildAttachedToWindow(child) + checkChild(child) + } + + val checkedItemGid: Set + get() = checkedGid + + fun intoCustomChoiceMode() { + if (!isInCustomChoice) { + isInCustomChoice = true + customChoiceListener?.onIntoCustomChoice(this) + } + } + + fun outOfCustomChoiceMode() { + if (isInCustomChoice && !mOutOfCustomChoiceModing) { + mOutOfCustomChoiceModing = true + clearChoices() + isInCustomChoice = false + customChoiceListener?.onOutOfCustomChoice(this) + mOutOfCustomChoiceModing = false + } + } + + private fun clearChoices() { + checkedGid.clear() + updateOnScreenCheckedViews() + } + + fun checkAll() { + check(isInCustomChoice) { "Call intoCheckMode first" } + // Foreach checked + // Notify customChoiceListener!!.onItemCheckedStateChanged(this) + updateOnScreenCheckedViews() + } + + fun toggleItemChecked(position: Int) { + check(isInCustomChoice) { "Call intoCheckMode first" } + val gid = gidGetter(position) ?: return + if (gid in checkedGid) { + checkedGid.remove(gid) + } else { + checkedGid.add(gid) + } + customChoiceListener?.onItemCheckedStateChanged(this) + updateOnScreenCheckedViews() + } + + fun setCustomCheckedListener(listener: CustomChoiceListener?) { + customChoiceListener = listener + } + + private fun updateOnScreenCheckedViews() { + children.forEach { + checkChild(it) + } + } + + override fun onSaveInstanceState() = SavedState( + isInCustomChoice, + checkedGid, + super.onSaveInstanceState(), + ) + + override fun onRestoreInstanceState(state: Parcelable) { + val ss = state as SavedState + super.onRestoreInstanceState(ss.superState) + isInCustomChoice = ss.customChoice + checkedGid = ss.checkedGid + updateOnScreenCheckedViews() + } + + interface CustomChoiceListener { + fun onIntoCustomChoice(view: SelectableGalleryInfoRecyclerView) + fun onOutOfCustomChoice(view: SelectableGalleryInfoRecyclerView) + fun onItemCheckedStateChanged(view: SelectableGalleryInfoRecyclerView) + } + + @Parcelize + class SavedState( + val customChoice: Boolean, + val checkedGid: MutableSet, + val superState: Parcelable?, + ) : Parcelable +} + +private fun setViewChecked(view: View, checked: Boolean) { + if (view is Checkable) { + (view as Checkable).isChecked = checked + } else { + view.isActivated = checked + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt index 3ad91c0b1..ba4c7f54e 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt @@ -79,8 +79,6 @@ import com.hippo.ehviewer.spider.putToDownloadDir import com.hippo.ehviewer.ui.legacy.AutoStaggeredGridLayoutManager import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder import com.hippo.ehviewer.ui.legacy.CheckBoxDialogBuilder -import com.hippo.ehviewer.ui.legacy.EasyRecyclerView -import com.hippo.ehviewer.ui.legacy.EasyRecyclerView.CustomChoiceListener import com.hippo.ehviewer.ui.legacy.EditTextDialogBuilder import com.hippo.ehviewer.ui.legacy.FabLayout import com.hippo.ehviewer.ui.legacy.FabLayout.OnClickFabListener @@ -88,6 +86,8 @@ import com.hippo.ehviewer.ui.legacy.FabLayout.OnExpandListener import com.hippo.ehviewer.ui.legacy.FastScroller.OnDragHandlerListener import com.hippo.ehviewer.ui.legacy.HandlerDrawable import com.hippo.ehviewer.ui.legacy.STRATEGY_MIN_SIZE +import com.hippo.ehviewer.ui.legacy.SelectableGalleryInfoRecyclerView +import com.hippo.ehviewer.ui.legacy.SelectableGalleryInfoRecyclerView.CustomChoiceListener import com.hippo.ehviewer.ui.legacy.ViewTransition import com.hippo.ehviewer.ui.main.requestOf import com.hippo.ehviewer.ui.navToReader @@ -277,7 +277,7 @@ class DownloadsScene : recyclerView.layoutManager = layoutManager recyclerView.clipToPadding = false recyclerView.clipChildren = false - recyclerView.setChoiceMode(EasyRecyclerView.CHOICE_MODE_MULTIPLE_CUSTOM) + recyclerView.gidGetter = { mList?.get(it)?.gid } recyclerView.setCustomCheckedListener(DownloadChoiceListener()) // Cancel change animation val itemAnimator = recyclerView.itemAnimator @@ -566,16 +566,12 @@ class DownloadsScene : if (collectDownloadInfo) { downloadInfoList = LinkedList() } - val stateArray = binding.recyclerView.checkedItemPositions!! - for (i in 0 until stateArray.size()) { - if (stateArray.valueAt(i)) { - val info = list[stateArray.keyAt(i)] - if (collectDownloadInfo) { - downloadInfoList!!.add(info) - } - if (collectGid) { - gidList!!.add(info.gid) - } + binding.recyclerView.checkedItemGid.mapNotNull { gid -> list.find { it.gid == gid } }.forEach { + if (collectDownloadInfo) { + downloadInfoList!!.add(it) + } + if (collectGid) { + gidList!!.add(it.gid) } } when (position) { @@ -1050,26 +1046,21 @@ class DownloadsScene : } private inner class DownloadChoiceListener : CustomChoiceListener { - override fun onIntoCustomChoice(view: EasyRecyclerView) { + override fun onIntoCustomChoice(view: SelectableGalleryInfoRecyclerView) { binding.fabLayout.isExpanded = true // Lock drawer setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.START) setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END) } - override fun onOutOfCustomChoice(view: EasyRecyclerView) { + override fun onOutOfCustomChoice(view: SelectableGalleryInfoRecyclerView) { binding.fabLayout.isExpanded = false // Unlock drawer setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.START) setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END) } - override fun onItemCheckedStateChanged( - view: EasyRecyclerView, - position: Int, - id: Long, - checked: Boolean, - ) { + override fun onItemCheckedStateChanged(view: SelectableGalleryInfoRecyclerView) { if (view.checkedItemCount == 0) { view.outOfCustomChoiceMode() } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt index 238a1ed8f..8ee039db7 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt @@ -37,7 +37,6 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.res.stringResource import androidx.core.content.ContextCompat import androidx.core.os.bundleOf -import androidx.core.util.valueIterator import androidx.core.view.GravityCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsAnimationCompat @@ -72,12 +71,12 @@ import com.hippo.ehviewer.databinding.SceneFavoritesBinding import com.hippo.ehviewer.ui.CommonOperations import com.hippo.ehviewer.ui.legacy.AddDeleteDrawable import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder -import com.hippo.ehviewer.ui.legacy.EasyRecyclerView -import com.hippo.ehviewer.ui.legacy.EasyRecyclerView.CustomChoiceListener import com.hippo.ehviewer.ui.legacy.FabLayout import com.hippo.ehviewer.ui.legacy.FabLayout.OnClickFabListener import com.hippo.ehviewer.ui.legacy.FastScroller.OnDragHandlerListener import com.hippo.ehviewer.ui.legacy.HandlerDrawable +import com.hippo.ehviewer.ui.legacy.SelectableGalleryInfoRecyclerView +import com.hippo.ehviewer.ui.legacy.SelectableGalleryInfoRecyclerView.CustomChoiceListener import com.hippo.ehviewer.ui.legacy.ViewTransition import com.hippo.ehviewer.ui.legacy.WindowInsetsAnimationHelper import com.hippo.ehviewer.ui.setMD3Content @@ -354,11 +353,11 @@ class FavoritesScene : SearchBarScene() { } } } + gidGetter = { adapter.peek(it)?.gid } } switchFav(Settings.recentFavCat) - setChoiceMode(EasyRecyclerView.CHOICE_MODE_MULTIPLE_CUSTOM) setCustomCheckedListener(object : CustomChoiceListener { - override fun onIntoCustomChoice(view: EasyRecyclerView) { + override fun onIntoCustomChoice(view: SelectableGalleryInfoRecyclerView) { showSelectionFab() binding.fabLayout.setAutoCancel(false) // Delay expanding action to make layout work fine @@ -369,7 +368,7 @@ class FavoritesScene : SearchBarScene() { setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END) } - override fun onOutOfCustomChoice(view: EasyRecyclerView) { + override fun onOutOfCustomChoice(view: SelectableGalleryInfoRecyclerView) { showNormalFab() binding.fabLayout.setAutoCancel(true) binding.fabLayout.isExpanded = false @@ -379,7 +378,7 @@ class FavoritesScene : SearchBarScene() { setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END) } - override fun onItemCheckedStateChanged(view: EasyRecyclerView, position: Int, id: Long, checked: Boolean) { + override fun onItemCheckedStateChanged(view: SelectableGalleryInfoRecyclerView) { if (view.checkedItemCount == 0) { view.outOfCustomChoiceMode() } @@ -470,9 +469,8 @@ class FavoritesScene : SearchBarScene() { } private fun takeCheckedInfo() = run { - val stateArray = binding.recyclerView.checkedItemPositions!! - stateArray.valueIterator().asSequence().withIndex().filter { it.value } - .mapNotNull { mAdapter?.peek(stateArray.keyAt(it.index)) }.toList() + val snapshot = mAdapter?.snapshot()?.items + binding.recyclerView.checkedItemGid.mapNotNull { gid -> snapshot?.find { it.gid == gid } } }.also { binding.recyclerView.outOfCustomChoiceMode() } private fun checkedSize() = binding.recyclerView.checkedItemCount diff --git a/app/src/main/res/layout/scene_download.xml b/app/src/main/res/layout/scene_download.xml index 482502ba2..cef8655c1 100644 --- a/app/src/main/res/layout/scene_download.xml +++ b/app/src/main/res/layout/scene_download.xml @@ -23,7 +23,7 @@ android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> - -