> Building TV Playback Apps
The Android framework provides classes for building user interfaces for these types of apps with the v17 leanback support library. This library provides a framework of classes for creating an efficient and familiar interface for browsing and playing media files with minimal coding. The classes are designed to be extended and customized so you can create an experience that is unique to your app.
>Creating a Catalog Browser
A media app that runs on a TV needs to allow users to browse its content offerings, make a selection, and start playing content. the v17 leanback support library to implement a user interface for browsing music or videos from your app's media catalog.
how to create a layout that contains a BrowseFragment
object:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_frame" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:name="com.example.android.tvleanback.ui.MainFragment" android:id="@+id/main_browse_fragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
public class MainFragment extends BrowseFragment implements LoaderManager.LoaderCallbacks<HashMap<String, List<Movie>>> { ... @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); loadVideoData(); prepareBackgroundManager(); setupUIElements(); setupEventListeners(); } ... private void prepareBackgroundManager() { mBackgroundManager = BackgroundManager.getInstance(getActivity()); mBackgroundManager.attach(getActivity().getWindow()); mDefaultBackground = getResources() .getDrawable(R.drawable.default_background); mMetrics = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics); } private void setupUIElements() { setBadgeDrawable(getActivity().getResources() .getDrawable(R.drawable.videos_by_google_banner)); // Badge, when set, takes precedent over title setTitle(getString(R.string.browse_title)); setHeadersState(HEADERS_ENABLED); setHeadersTransitionOnBackEnabled(true); // set headers background color setBrandColor(getResources().getColor(R.color.fastlane_background)); // set search icon color setSearchAffordanceColor(getResources().getColor(R.color.search_opaque)); } private void loadVideoData() { VideoProvider.setContext(getActivity()); mVideosUrl = getActivity().getResources().getString(R.string.catalog_url); getLoaderManager().initLoader(0, null, this); } private void setupEventListeners() { setOnSearchClickedListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(getActivity(), SearchActivity.class); startActivity(intent); } }); setOnItemViewClickedListener(new ItemViewClickedListener()); setOnItemViewSelectedListener(new ItemViewSelectedListener()); } ...
In the sample above, the private method setupUIElements()
calls several of the BrowseFragment
methods to style the media catalog browser:
setBadgeDrawable()
places the specified drawable resource in the upper-right corner of the browse fragment, as shown in figures 1 and 2. This method replaces the title string with the drawable resource, ifsetTitle()
is also called. The drawable resource should be 52dps tall.setTitle()
sets the title string in the upper-right corner of the browse fragment, unlesssetBadgeDrawable()
is called.setHeadersState()
andsetHeadersTransitionOnBackEnabled()
hide or disable the headers. See Hide or Disable Headers for more information.setBrandColor()
sets the background color for UI elements in the browse fragment, specifically the header section background color, with the specified color value.setSearchAffordanceColor()
sets the color of the search icon with the specified color value. The search icon appears in the upper-left corner of the browse fragment, as shown in figures 1 and 2.
> Use a Presenter
and implement the abstract methods to create, bind, and unbind the view holder. The following example shows how to bind the viewholder with two views, an ImageView
and a TextView
.
public class IconHeaderItemPresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup) { LayoutInflater inflater = (LayoutInflater) viewGroup.getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.icon_header_item, null); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder viewHolder, Object o) { HeaderItem headerItem = ((ListRow) o).getHeaderItem(); View rootView = viewHolder.view; ImageView iconView = (ImageView) rootView.findViewById(R.id.header_icon); Drawable icon = rootView.getResources().getDrawable(R.drawable.ic_action_video, null); iconView.setImageDrawable(icon); TextView label = (TextView) rootView.findViewById(R.id.header_label); label.setText(headerItem.getName()); } @Override public void onUnbindViewHolder(ViewHolder viewHolder) { // no op } }However, an easier way to combine a
TextView
with a drawable resource is to use the
TextView.drawableLeft
attribute. Doing it this way, you don't need the
ImageView
shown here.
> The Leanback support library provides a BackgroundManager
class for changing the background of your TV app activity.
protected void updateBackground(Drawable drawable) { BackgroundManager.getInstance(this).setDrawable(drawable); }>
protected void clearBackground() { BackgroundManager.getInstance(this).setDrawable(mDefaultBackground); } protected OnItemViewSelectedListener getDefaultItemViewSelectedListener() { return new OnItemViewSelectedListener() { @Override public void onItemSelected(Object item, Row row) { if (item instanceof Movie ) { Drawable background = ((Movie)item).getBackdropDrawable(); updateBackground(background); } else { clearBackground(); } } }; }
Note: The implementation above is a simple example shown for purposes of illustration. When creating this function in your own app, you should consider running the background update action in a separate thread for better performance. In addition, if you are planning on updating the background in response to users scrolling through items, consider adding a time to delay a background image update until the user settles on an item. This technique avoids excessive background image updates.
> Providing a Card View
you create the card views for your media items and present them in the browse fragment.
The BaseCardView
class and subclasses display the meta data associated with a media item. The ImageCardView
class used in this lesson displays an image for the content along with the media item's title.
>In the Presenter
, implement an onCreateViewHolder()
callback that creates a view holder that can be used to display a content item.
@Override public class CardPresenter extends Presenter { private Context mContext; private static int CARD_WIDTH = 313; private static int CARD_HEIGHT = 176; private Drawable mDefaultCardImage; @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { mContext = parent.getContext(); mDefaultCardImage = mContext.getResources().getDrawable(R.drawable.movie); ...
In the onCreateViewHolder()
method, create a card view for content items. The sample below uses anImageCardView
.
When a card is selected, the default behavior expands it to a larger size. If you want to designate a different color for the selected card, call setSelected()
as shown here.
... ImageCardView cardView = new ImageCardView(mContext) { @Override public void setSelected(boolean selected) { int selected_background = mContext.getResources().getColor(R.color.detail_background); int default_background = mContext.getResources().getColor(R.color.default_background); int color = selected ? selected_background : default_background; findViewById(R.id.info_field).setBackgroundColor(color); super.setSelected(selected); } };