By Barry Burd

The skeletal TV app that Android Studio creates contains too much code for leisurely bedside reading. Here, you will see an app that’s scaled down from Android Studio’s skeletal app. This app illustrates the backbone features in the skeletal app.

A super simple sample of an app for Android TV.

A super simple sample.

This illustrates this simple app’s behavior.

The user moves to a movie item.

The user moves to a movie item.

This example app starts with this layout.

<LinearLayout xmlns:android=
“http://schemas.android.com/apk/res/android”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:orientation=“vertical”>
<fragment android:name=
“android.support.v17.leanback.app.BrowseFragment”
android:id=“@+id/browse_fragment”
android:layout_width=“match_parent”
android:layout_height=“match_parent” />
</LinearLayout>

The layout contains only one element — an instance of Android’s pre-declared BrowseFragment class. A BrowseFragment is an elaborate layout element consisting of one or more rows. Each row has a header and several “movie” items.

The app’s main activity grabs the layout’s BrowseFragment and populates it with data. The main activity is shown below.

package com.allmycode.catalogbrowser;
import android.app.Activity;
import android.app.FragmentManager;
import android.os.Bundle;
import android.support.v17.leanback.app.BrowseFragment;
import
android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
public class BrowseMediaActivity extends Activity {
protected BrowseFragment mBrowseFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.browse_fragment);
final FragmentManager fragmentManager =
getFragmentManager();
mBrowseFragment = (BrowseFragment) fragmentManager.
findFragmentById(R.id.browse_fragment);
mBrowseFragment.
setHeadersState(BrowseFragment.HEADERS_ENABLED);
mBrowseFragment.
setTitle(getString(R.string.app_name));
mBrowseFragment.setBadgeDrawable(getResources().
getDrawable(R.drawable.ic_launcher, null));
buildRowsAdapter();
}
private ArrayObjectAdapter mRowsAdapter;
private static final int NUM_ROWS = 4;
private void buildRowsAdapter() {
mRowsAdapter =
new ArrayObjectAdapter(new ListRowPresenter());
for (int i = 0; i < NUM_ROWS; ++i) {
ArrayObjectAdapter listRowAdapter =
new ArrayObjectAdapter(new CardPresenter());
listRowAdapter.add(“Media Item “ + i + “.1”);
listRowAdapter.add(“Media Item “ + i + “.2”);
listRowAdapter.add(“Media Item “ + i + “.3”);
HeaderItem header =
new HeaderItem(i, “Category “ + i);
mRowsAdapter.add
(new ListRow(header, listRowAdapter));
}
mBrowseFragment.setAdapter(mRowsAdapter);
}
}

Each row consists of a heading and a bunch of individual items. For example, the selected row’s heading contains the text Category 1, and the row’s items (like all other items) display the slanted Movie! graphic. The code above puts these things onto the screen.

The body of the buildRowsAdapter method contains a for loop. The loop performs an iteration for each row. During one loop iteration, three calls to listRowAdapter.add create the movies in a row, and a call to the HeaderItem constructor creates a category heading (such as the Category 1 heading). At the end of a loop iteration, the call to mRowsAdapter.add puts the entire row onto the user’s screen.