AndroidX TV libraries

Most AndroidX libraries can be used with Android TV. You can use Architecture Components such as ViewModel for managing lifecycle-aware UI data, and Room to easily work with local SQLite databases just the same as you would for mobile; however, there are some TV-specific libraries that support functionality that is exclusive to Android TV. These libraries include the following:

  • The Leanback library provides UI templates that simplify creating Android TV apps.
  • The Leanback Preferences library provides preferences and settings screens that are consistent with the platform but can be themed to match your app.
  • The Leanback Paging library supports the AndroidX paging model for ObjectAdapters, which are commonly used with the Leanback templates.
  • The Leanback Tabs library supports tabbed navigation on Android TV.

Leanback Paging library

Paging for Leanback works the same as the AndroidX Paging 3 library, which simplifies adding paging to a RecyclerView.Adapter. With Leanback, the adapter that is exposed is typically an ObjectAdapter instead, so the Leanback Paging library adds paging support to ObjectAdapter.

First, add the library to your project:

implementation "androidx.leanback:leanback-paging:$version"

Then you can follow the Paging 3 documentation using androidx.leanback.paging.PagingDataAdapter instead of androidx.paging.PagingDataAdapter. The only difference is that you’re now able to pass in a Presenter or PresenterSelector. This works anywhere you would ordinarily use an ObjectAdapter, such as in a ListRow:

Kotlin

val adapter: PagingDataAdapter<MyItem> = PagingDataAdapter(myPresenter,
   object : DiffUtil.ItemCallback<MyItem>() {
       override fun areItemsTheSame(
           oldItem: MyItem,
           newItem: MyItem
       ): Boolean {
           return oldItem.id === newItem.id
       }

       override fun areContentsTheSame(
           oldItem: MyItem,
           newItem: MyItem
       ): Boolean {
           return oldItem == newItem
       }
   })

val header = HeaderItem(headerTitle)
val row = ListRow(header, adapter)

Java

PagingDataAdapter<MyItem> adapter = new PagingDataAdapter(myPresenter, new DiffUtil.ItemCallback<MyItem>() {
    @Override
    public boolean areItemsTheSame(@NonNull MyItem oldItem, @NonNull MyItem newItem) {
        return oldItem.getId().equals(newItem.getId());
    }

    @Override
    public boolean areContentsTheSame(@NonNull MyItem oldItem, @NonNull MyItem newItem) {
        return oldItem.equals(newItem);
    }
});

HeaderItem header = new HeaderItem(headerTitle);
Row row = new ListRow(header, adapter);

Leanback Tabs library

The Leanback templates provide side navigation in the browse experience, which works well for many apps. If you need tab navigation (typically displayed horizontally across the top of the app), you can instead use Leanback Tabs.

Add the library to your project:

implementation "androidx.leanback:leanback-tab:$version"

Then implement tabs using LeanbackTabLayout and LeanbackViewPager by following the existing ViewPager guide. Note that LeanbackViewPager is based on ViewPager, not ViewPager2.

A simple example looks like the following:

Kotlin

val leanbackTabLayout = findViewById<LeanbackTabLayout>(R.id.tab_layout)
val leanbackViewPager = findViewById<LeanbackViewPager>(R.id.view_pager)

leanbackViewPager.setAdapter(adapter)
leanbackTabLayout.setupWithViewPager(leanbackViewPager)

Java

LeanbackTabLayout leanbackTabLayout = findViewById(R.id.tab_layout);
LeanbackViewPager leanbackViewPager = findViewById(R.id.view_pager);

leanbackViewPager.setAdapter(adapter);
leanbackTabLayout.setupWithViewPager(leanbackViewPager);

Limitations

The Leanback Tabs library has the following limitations.

Supported themes

Only themes that are derived from Theme.AppCompat are supported. TabLayout contains a theme enforcement constraint, which prevents any non-descendant theme of Theme.AppCompat from being used. You can also use the bridge theme for Leanback.

Focus movement from tabs to top

When the layout height is greater than the screen height and you press the D-pad up button, control moves back to the tab instead of staying inside the fragment and navigating to an item above it (see video). To handle this issue, contents inside the fragment must override focus search. For example, use RowsSupportFragment to handle this issue; BrowseSupportFragment cannot be used inside a tab as it has an overridden focus search method which prevents the focus from moving back to the tab.