Multiple rows per header (category) - dextorer/Sofa GitHub Wiki
Introduction
This is certainly one of the most common requests that pop up when developers get their hands dirty with Leanback.
I believe the limitation has been posed intentionally to prevent the UI (and, in turn, the UX) from getting complicated. I agree in a general sense, but there are certain situations that just require a more structured organization for the content, otherwise the result is going to be even messier. YouTube's TV app is possibly the most resounding example, right there in plain sight.
Sofa overcomes this limitation by "relaxing" the strict hierarchical requirements of BrowseFragment
, allowing you to display several RowsFragment
instances, each one with its own set of rows.
Structure
What follows is a step-by-step tutorial on how adding multiple RowsFragment
instances works on Sofa.
1. Start with your BrowseFragment
You can either create a new instance (i.e., new BrowseFragment()
) or make your Fragment
extend from BrowseFragment
. Just make sure you're using the com.sgottard.sofa.BrowseFragment
class, and not Leanback's!
2. Create a RowsFragment
Create a new instance of RowsFragment
(double check that you're using com.sgottard.sofa.RowsFragment
).
3. Create an ArrayObjectAdapter
with a ListRowPresenter
and fill it as you normally do
Nothing special here, this is something you've done plenty of times. You can add HeaderItem
s, customize the UI, the behavior and all that stuff.
4. Set the adapter to RowsFragment
Again, nothing special here.
5. Create another ArrayObjectAdapter
(no need for a Presenter
)
This is something new. Basically, we are going to 'wrap' the RowsFragment
instance into an ArrayObjectAdapter
container. This is just a container, plain and simple, so there is no need to plug a Presenter
for it (even if you do, it is going to be ignored; but why would you do that?).
5. Add a new ListRow
that contains the RowsFragment
instance and the corresponding HeaderItem
In order to wrap the RowsFragment
instance, we need to put it inside a ListRow
object. Here you can also specify the corresponding HeaderItem
. Providing an HeaderItem
is almost mandatory, since it's the only way for the user to move across different RowsFragment
s.
6. Set the adapter to BrowseFragment
Finally, set the ArrayObjectAdapter
as the adapter for BrowseFragment
.
This is basically it. If you want to add more RowsFragment
s, just repeat all the steps and add them to the ArrayObjectAdapter
container.
Navigation and behavior
Moving between different RowsFragment
s can be done only if the headers on the left are visible: so please do not disable them.
Also, if you choose to display the title and the search orb on BrowseFragment, you will be glad to notice that the 'title bar' correctly animates in and out, depending on which row is selected (if headers are hidden) or which header is selected (if headers are visible).
Handling selections and clicks
Typically, BrowseFragment offers you the setOnItemViewSelectedListener()
and setOnItemViewClickedListener()
methods, but for obvious reasons, these are no longer suitable if you have multiple RowsFragment
instances.
There are two possibilities when it comes to handle the selection event and the click event:
- Set an appropriate listener on the
ViewHolder
- Set an appropriate listener on
RowsFragment
I would recommend the second approach, since it guarantees that those events are properly handled by Leanback. It is also a more robust choice.