Table of Contents
Learn the ins and outs of your user’s welcome screen to create a more engaging and personalized experience
Understanding the internals of the Leanback APIs is sometimes quite tricky, but it’s something we, as developers, must do to make sure we’re able to create apps that look and behave correctly to our users. In this post, we’ll dive deep into the HeadersSupportFragment
, which is part of the BrowseSupportFragment
, and almost always, the first piece of content users see in an Android TV app. We will learn about its creation, how it behaves, and how we can customize it.
As stated in the introduction, the HeadersSupportFragment
is part of the BrowseSupportFragment.
The BrowseSupportFragment
is often the first screen that a user sees when opening an Android TV app, and it’s where the user can browse and discover content.
HeadersSupportFragment creation and configuration
The BrowseSupportFragment
has a function called onCreateHeadersSupportFragment()
which is responsible for returning an instance of HeadersSupportFragment
to be used inside it. It is a public function, so it is possible to override it if you want to provide a custom implementation of your own HeadersSupportFragment
.
public HeadersSupportFragment onCreateHeadersSupportFragment()
return new HeadersSupportFragment();
After that, still inside the BrowseSupportFragment.onCreateView()
the HeadersSupportFragment
is added to its container by a FragmentTransaction
performed by the BrowseSupportFragment
. Then, the following steps happen:
- Update
HeadersSupportFragment
visibility based on the state passed toBrowseSupportFragment.setHeaderState(state)
. The available states are:
/** The headers fragment is enabled and shown by default. */
public static final int HEADERS_ENABLED = 1;
/** The headers fragment is enabled and hidden by default. */
public static final int HEADERS_HIDDEN = 2;
/** The headers fragment is disabled and will never be shown. */
public static final int HEADERS_DISABLED = 3;
- Calls
HeadersSupportFragment.setPresenterSelector()
passing thePresenterSelector
provided toBrowseSupportFragment.setHeaderPresenterSelector()
. ThisPresenterSelector
should support at least three types of items. Which are,DividerRow
,SectionRow
, andRow
.
Note: The HeadersSupportFragment
already have out-of-the-box implementations for these Row
types, but we can provide custom implementations. As a HeaderSupportFragment
is a subclass of BaseRowSupportFragmeent
, customizing its rows, it’s just a matter of having a custom Presenter
for its items.
- The
HeadersSupportFragment
has its adapter,OnHeaderViewSelectedListener
andOnHeaderClickedListener
set. The adapter is responsible for providing the items. The default implementation for itsOnHeaderViewSelectedLister
changes or selects the content of theBrowseSupportFrament
. As for theOnHeaderClickedListener
, its default implementation hides theHeaderSupportFragment
when an item is clicked. - The background color of the
HeadersSupportFragment
is set using the color provided to theBrowseSupportFragment.setBrandColor()
. For example:
override fun onCreateHeadersSupportFragment(): HeadersSupportFragment
// This can also be done on the onViewCreated()
brandColor = Color.RED
return super.onCreateHeadersSupportFragment()
- At this point, the
BrowseSupportFragment.onViewCreated()
finished and theBrowseSupportFragment.onStart()
will be called. Inside theonStart()
the alignment of theHeadersSupportFragment
will be set based on a Leanback theme property calledbrowseRowsMarginTop
. This property, as its name suggests, is also used by theBrowseSupportFragment
to set the top margin of its first row. - If you want to change the
HeadersSupportFragment
items alignment, you need to do so after the call tosuper.onCreate()
override fun onStart()
super.onStart()
headersSupportFragment.setAlignment(400)
Note: The idea of bothHeadersSupportFragment
and BrowseSupportFragment
using the same value from browseRowsMarginTop
to set their top margin helps them to better align the selected row on the header with the selected row on the BrowseSupportFragment
when the BrowseSupportFragment
is used to display only ListRows
. If the BrowseSupportFragment
is displaying a PageRow
this alignment doesn’t matter as the PageRow
content will fill the entire fragment.
We’ll cover PageRows
and the details of the BrowseSupportFragment
on another post. Make sure to follow me to be notified when it’s released!
- You can enable or disable the headers of a
BrowseSupportFragment
by setting callingBrowseSupportFragment.setHeaderState(state)
and passing the desired state. - Change
HeadersSupportFragment
background-color by callingHeadersSupportFragment.setBrandColor(color)
. - Change or provide new types of
Presenter
to yourHeadersSupportFragment
. - Change the
OnHeaderViewSelectedListener
andOnHeaderClickListener
implementations of youHeadersSupportFragment
.
As we saw earlier, the HeadersSupportFragment
should support three different types of items: DividerRow
, SectionRow
, and Row
. Take a look at how these items are displayed with their default Presenter
implementations.
IconSectionRow and IconSectionPresenter
To show how we can customize the items of a HeadersSupportFragment
, we will create a IconSectionPresenter
which will render a SectionRow
with an image on the left of its text.
Let’s start by creating a subclass of SectionRow
that holds the id of a drawable to be used on the row.
class IconSectionRow(
@DrawableRes val icon: Int,
headerItem: HeaderItem
) : SectionRow(headerItem)
After that, we need to create a Presenter
capable of performing the creation and binding of an IconSectionRow
. We’ll do that by extending the RowHeaderPresenter
and adding just a few lines.
And here’s the R.layout.icon_section_row
.
Note: When creating layouts for non-focusable rows, like dividers and sections, disable the focus on its root view. Otherwise, they will be able to receive focus, and sometimes this can lead to a ClassCastException
depending on the type of your item and ViewHolder
used for it as the HeadersSupportFragment
tries to cast the selected items ViewHolders
to RowHeaderPresenter.ViewHolder
.
DividerRow and SpaceDividerPresenter
Similar to how we created a custom Presenter
for theSectionRow
, we’ll create one for the DividerRow
. We’ll build a presenter that displays the divider as a space instead of a 1dp
line. In this case, we only need a custom presenter, as the item, the DividerRow
, does not need to hold any extra information (like the icon from the IconSectionRow
).
Adding Custom Presenters
Last, we must add both newly created presenters to the HeadersSupportFragment
presenter selector. Make sure you are doing this only after the call to BrowseSupportFragment.onCreateView()
finished, as the HeadersSupportFragment
is only instantiated inside it.
Inside your BrowseSupportFragment
, or in your custom implementation of HeadersSupportFragment
, do the following:
Theme Managed Attributes
A few attributes from the HeadersSupportFragment
are provided by your app theme. When creating a theme for an Android TV app, you should extend from Theme.Leanback
. This base theme has all properties the Leanback components use to set sizes, margins, paddings, colors, and more. Below is a list of the attributes that are used by the HeadersSupportFragment
.
lb_browse_headers_width
: The width of theHeadersSupportFragment
.lb_browse_headers_paddding_end
:HeadersSupportFragment
padding end.lb_browse_headers_z
: Elevation of theHeadersSupportFragment
root view.lb_browse_header_fading_length
: Fading edge width. The fading edge covers the end of a header text when the text is too long.lb_headers_right_fading
: Background drawable used by the fading edge.headersVerticalGridStyle
: Style for theVerticalGridView
of theHeadersSupportfragment
. The default theme is configured as shown below:
lb_section_header
&lb_header
: Default layout file for theSectionRow
andRow
views, respectively.
Note: On the sample app, none of these theme properties were changed.
Thanks for reading this post! Make sure to check the sample app created especially for this article. Take a look at the official documentation on HeadersSupportFragment
.
Also, check this list I created with my Android TV Leanback series articles:
📺 Android TV Leanback Guide
You can also connect with me on Twitter.