如何在配置更改期间保留RecyclerView项目的状态?

时间:2018-07-09 08:54:16

标签: android android-recyclerview viewmodel android-mvvm

我正在使用RecyclerView创建抽认卡应用程序。

每个项目都是扩展ConstraintsLayout的自定义视图,并且是可以翻转的卡片。当卡片从屏幕上刷下时,下一张卡片将从RecyclerView中移入。

我已经为自定义视图实现了onSavedInstanceState和onRestoreInstanceState:

class FlashcardView : ConstraintLayout {

override fun onSaveInstanceState(): Parcelable {
    Log.d("FlashcardView", "saving instance state")
    val savedState = SavedState(super.onSaveInstanceState())
    savedState.isFlipped = isFlipped
    savedState.side = side
    return savedState
}

override fun onRestoreInstanceState(state: Parcelable?) {
    Log.d("FlashcardView", "restoring instance state")
    if (state is SavedState) {
        Log.d("FlashcardView", "is flipped: ${state.isFlipped}, " +
                "side: ${state.side}")
        super.onRestoreInstanceState(state)
        isFlipped = state.isFlipped
        if(state.side != side)
            flipCard()
    }else{
        super.onRestoreInstanceState(state)
    }
}


internal class SavedState : View.BaseSavedState {
    var isFlipped: Boolean = false
    var side: Int = 0

    constructor(source: Parcel): super(source){
        isFlipped = source.readByte().toInt() != 0
        side = source.readByte().toInt()
    }
    constructor(superState: Parcelable) : super(superState)

    override fun writeToParcel(destination: Parcel, flags: Int) {
        super.writeToParcel(destination, flags)
        destination.writeByte((if (isFlipped) 1 else 0).toByte())
        destination.writeByte(side.toByte())
    }

    companion object {

        @JvmField
        val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
            override fun createFromParcel(source: Parcel): SavedState {
                return SavedState(source)
            }

            override fun newArray(size: Int): Array<SavedState?> {
                return arrayOfNulls(size)
            }
        }
    }
}
}

我希望自定义视图处理其自身的状态,但是在配置上进行更改时,RecyclerView.Adapter在onCreateViewHolder中创建该自定义视图的新实例(而不是将先前的实例与onBindViewHolder一起使用)。我假设发生这种情况是因为适配器被销毁或具有已创建的Viewholder的池被销毁了。如何保持recyclerview适配器的状态?

CardFlipFragment.kt

class CardFlipFragment: Fragment() {

@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: CardViewModel

@Inject
lateinit var disposable: CompositeDisposable

private val cards = ArrayList<Card>()
private val cardsAdapter = CardsAdapter(cards)
private val DEBUGTAG = "CardFlipFragment"
companion object {
    const val deckNameKey = "deckName"
}

override fun onAttach(context: Context?) {
    super.onAttach(context)
    AndroidSupportInjection.inject(this)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    viewModel = ViewModelProviders.of(activity!!, viewModelFactory).get(CardViewModel::class.java)
    setupRecyclerView()
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.activity_card_flip, container, false)
}

override fun onStart() {
    super.onStart()
    val deckName = arguments!!.getString(deckNameKey)
    fetchDeckCards(deckName)
}

override fun onStop() {
    super.onStop()
    disposable.clear()
}

private fun setupRecyclerView() {
    cardRecyclerView.adapter = cardsAdapter

    cardRecyclerView.layoutManager = object : LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) {

        override fun smoothScrollToPosition(recyclerView: RecyclerView?, state: RecyclerView.State?, position: Int) {

            val smoothScroller = object: LinearSmoothScroller(context){
                //return a vector pointing to the next position -> to the card to the right
                override fun computeScrollVectorForPosition(targetPosition: Int): PointF? {
                    return PointF(1F, 0F)
                }

            }

            smoothScroller.targetPosition = position
            startSmoothScroll(smoothScroller)
        }
    }

    cardRecyclerView.addOnItemTouchListener(CardRecyclerTouchListener(context!!, cardRecyclerView, object : CardRecyclerTouchListener.ClickListener {
        override fun onClick(view: FlashcardView) {
            Log.d(DEBUGTAG, "click")
            view.flipCard()
        }

        override fun onLongClick(view: FlashcardView?) {
            Log.d(DEBUGTAG, "longclick")
            val pos = view!!.tag as Int
            val card = cards[pos]
            val side = view.side
            Log.d(DEBUGTAG, "view pos :" + pos + "card uid: " + card.uid)
            //start edit card fragment
            val bundle = Bundle()
            bundle.putInt("cardUid", card.uid)
            bundle.putInt("cardSide", side)
            Navigation.findNavController(view).navigate(R.id.action_cardFlipFragment_to_editCardFragment, bundle)
        }

        override fun onSwipe(view: FlashcardView, swipe_type: Int) {
            Log.d(DEBUGTAG, "fling cards: ${cards.size}")
            if (view.isFlipped) {
                if (swipe_type == CardRecyclerTouchListener.SWIPE_IN) {
                    animateIn(view)
                } else {
                    animateOut(view) {
                        val nextCardPos = view.tag as Int + 1
                        cardRecyclerView.smoothScrollToPosition(nextCardPos)

                        if (nextCardPos == cards.size)
                            Navigation.findNavController(view).popBackStack()
                    }
                }
            }
        }
    }))
}

private fun fetchDeckCards(deckName: String) {
    disposable.add(
        viewModel.getCards(deckName)
            .subscribe({ deck ->
                if (deck.isEmpty())
                    startEditCardFragment(deckName)

                cards.clear()
                cards.addAll(deck)
                cardsAdapter.notifyDataSetChanged()
            }, { throwable -> Log.e(DEBUGTAG, "Unable to fetch cards", throwable) }))
}

}

CardsAdapter.kt

class CardsAdapter (private val cards: List<Card>) : RecyclerView.Adapter<CardsAdapter.ViewHolder>(){

inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    internal val textFront: TextView = itemView.cardFrontTextView
    internal val textBack: TextView = itemView.cardBackTextView
    internal val imageFront: ImageView = itemView.imageViewFront
    internal val imageBack: ImageView = itemView.imageViewBack
    internal val cardLayout: FlashcardView = itemView as FlashcardView

}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardsAdapter.ViewHolder {
    Log.d("CardsAdapter", "flashcard viewholder created")
    val flashcardView = FlashcardView(parent.context)
    return ViewHolder(flashcardView)
}

override fun getItemCount(): Int {
    return cards.size
}

override fun onBindViewHolder(holder: CardsAdapter.ViewHolder, position: Int) {
    val card = cards[position]
    holder.cardLayout.tag = position

    var tv = holder.textFront
    tv.text = card.textFront
    tv = holder.textBack
    tv.text = card.textBack
}

//reset card so it is ready for reuse
override fun onViewRecycled(holder: ViewHolder) {
    super.onViewRecycled(holder)
    holder.cardLayout.reset()
}

}

0 个答案:

没有答案