Skip to content

Instantly share code, notes, and snippets.

@dzolnai
Last active April 22, 2020 08:06
Show Gist options
  • Save dzolnai/d9256db0055ba98f862772afbc1a9a66 to your computer and use it in GitHub Desktop.
Save dzolnai/d9256db0055ba98f862772afbc1a9a66 to your computer and use it in GitHub Desktop.
RowsSupportFragment which also remembers position in last selected row.
@file:Suppress("PackageDirectoryMismatch")
package android.support.v17.leanback.widget
import android.os.Bundle
import android.support.v17.leanback.app.RowsSupportFragment
import android.support.v7.widget.RecyclerView
import android.view.View
/**
* RowsSupportFragment which also remembers the subposition in the rows.
* Warning: very nasty code, because of lifecycle things.
* Created by Daniel Zolnai on 2018-03-08.
*/
class SubRowsSupportFragment : RowsSupportFragment() {
companion object {
private const val KEY_SELECTED_SUB_POSITION = "selected_sub_position"
}
// The saved instance state is used when the fragment is restored from a saved state.
// However if coming from the back stack, there is no saved state, but the internal variables
// of the fragment still remain. In this case we use this property to set the fragment up and running again.
var subPositionOnBackStackSave: Int? = null
private set
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
(getRowViewHolder(selectedPosition)
as? ListRowPresenter.ViewHolder)?.selectedPosition?.let {
outState.putInt(KEY_SELECTED_SUB_POSITION, it)
}
}
override fun onStop() {
super.onStop()
subPositionOnBackStackSave = (getRowViewHolder(selectedPosition)
as? ListRowPresenter.ViewHolder)?.selectedPosition
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// If it is 0, we should not wait.
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_SELECTED_SUB_POSITION) && savedInstanceState.getInt(KEY_SELECTED_SUB_POSITION) > 0 ||
subPositionOnBackStackSave != null) {
findRowViewHolderByPosition(selectedPosition)?.let {
(presenterSelector.getPresenter(it.row) as? RowPresenter)?.setSelectLevel(it, 1f)
}
val subPosition = savedInstanceState?.getInt(KEY_SELECTED_SUB_POSITION)
?: subPositionOnBackStackSave!!
(verticalGridView.layoutManager as GridLayoutManager).addOnChildViewHolderSelectedListener(object : OnChildViewHolderSelectedListener() {
override fun onChildViewHolderSelected(parent: RecyclerView?, child: RecyclerView.ViewHolder?, position: Int, subposition: Int) {
if (position == selectedPosition) {
// I've tried setting the position with the setSelectedPosition and the attached task.
// The problem with this is, that the task is only ran after the position has been set on the row.
// So you will see the position 0 appear for a frame, and immediately the correct position.
// So instead I intercept the layout manager to give me the view before it has been made visible.
// Here I set the subposition I want.
(child as? ItemBridgeAdapter.ViewHolder)?.mHolder?.let {
if (it is RowPresenter.ContainerViewHolder) {
(it as? RowPresenter.ContainerViewHolder)?.mRowViewHolder?.let {
// Set the position
(it.view as? ListRowView)?.gridView?.selectedPosition = subPosition
// Set as selected
it.mSelectLevel = 1f
(it as? ListRowPresenter.ViewHolder)?.mListRowPresenter?.onSelectLevelChanged(it)
}
} else if (it is ListRowPresenter.ViewHolder) {
(it as? ListRowPresenter.ViewHolder)?.let {
// Set the position
(it.view as? ListRowView)?.gridView?.selectedPosition = subPosition
// Set as selected
it.mSelectLevel = 1f
(it as? ListRowPresenter.ViewHolder)?.mListRowPresenter?.onSelectLevelChanged(it)
}
}
(verticalGridView.layoutManager as GridLayoutManager).removeOnChildViewHolderSelectedListener(this)
}
}
}
})
}
}
override fun onDestroyView() {
// Fixes a memory leak where the grid view whould be registered as an observer at the adapter.
// Each new gridview would subscribe but not unsubscribe, so the old views would be kept in memory and not be collected.
// We could null out the adapter immediately, but then it would hide the items while the fragment is animating
verticalGridView?.let {
val tempRef = it
tempRef.postDelayed({
tempRef.adapter = null
}, 800) // Enough time to finish animations
}
super.onDestroyView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment