Last active
April 22, 2020 08:06
-
-
Save dzolnai/d9256db0055ba98f862772afbc1a9a66 to your computer and use it in GitHub Desktop.
RowsSupportFragment which also remembers position in last selected row.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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