Android Studio V3.12環(huán)境下TV開發(fā)教程
(轉(zhuǎn)自Android官網(wǎng)https://developer.android.com/training/tv/start)
文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619382
瀏覽和播放媒體文件通常是電視應(yīng)用程序提供的用戶體驗的一部分。?從零開始構(gòu)建這樣的體驗,同時確保其快速,流暢和有吸引力可能是相當(dāng)具有挑戰(zhàn)性的。?無論您的應(yīng)用是否提供對小型或大型媒體目錄的訪問,允許用戶快速瀏覽選項并獲取他們想要的內(nèi)容非常重要。
Android框架提供了用于使用v17 leanback支持庫為這些類型的應(yīng)用程序構(gòu)建用戶界面的類。?這個庫提供了一個類框架,用于創(chuàng)建一個高效且熟悉的界面,以最少的代碼瀏覽和播放媒體文件。?這些課程旨在進行擴展和定制,以便您可以創(chuàng)建獨特的應(yīng)用體驗。
本課程向您展示如何使用Leanback電視支持庫構(gòu)建瀏覽和播放媒體內(nèi)容的電視應(yīng)用程序。
了解如何使用Leanback支持庫為媒體目錄構(gòu)建瀏覽界面。
了解如何使用Leanback支持庫為內(nèi)容項目構(gòu)建卡片視圖。
了解如何使用Leanback支持庫為媒體項目構(gòu)建詳細信息頁面。
了解如何使用Leanback支持庫為您的視頻播放器構(gòu)建傳輸控件。
了解如何使用MediaSession在主屏幕上顯示正在使用的即時貼。
了解您的應(yīng)用如何直接在主屏幕的表面上呈現(xiàn)預(yù)覽視頻。
了解如何使用Leanback支持庫來指導(dǎo)用戶完成一系列決策。
了解如何使用Leanback支持庫向初次使用者展示如何充分利用您的應(yīng)用程序。
了解如何在用戶點擊主頁時繼續(xù)播放。
運行在電視上的媒體應(yīng)用程序需要允許用戶瀏覽其內(nèi)容產(chǎn)品,進行選擇并開始播放內(nèi)容。?這種類型的應(yīng)用程序的內(nèi)容瀏覽體驗應(yīng)該簡單直觀,并且在視覺上令人愉悅且引人入勝。
本課討論如何使用v17 leanback支持庫提供的類來實現(xiàn)用于從應(yīng)用媒體目錄中瀏覽音樂或視頻的用戶界面。
圖1.?Leanback示例應(yīng)用程序瀏覽片段顯示視頻目錄數(shù)據(jù)。
leanback庫中的BrowseFragment類允許您使用最少的代碼創(chuàng)建用于瀏覽媒體項目類別和行的主要布局。?以下示例顯示如何創(chuàng)建包含BrowseFragment對象的布局:
? ?
應(yīng)用程序的主要活動設(shè)置此視圖,如以下示例所示:
public class MainActivity extends Activity {? ? @Override? ? public void onCreate(Bundle savedInstanceState) {? ? ? ? super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.main);? ? }...
BrowseFragment方法使用視頻數(shù)據(jù)和UI元素填充視圖,并設(shè)置布局參數(shù)(如圖標(biāo),標(biāo)題以及是否啟用類別標(biāo)題)。
有關(guān)設(shè)置UI元素的更多信息,請參閱設(shè)置?UI元素。
有關(guān)隱藏標(biāo)題的更多信息,請參閱隱藏或禁用標(biāo)題。
實現(xiàn)BrowseFragment方法的應(yīng)用程序的子類還為UI元素上的用戶操作設(shè)置事件偵聽器,并準備后臺管理器,如以下示例所示:
public class MainFragment extends BrowseFragment implements? ? ? ? LoaderManager.LoaderCallbacks>> {...? ? @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());? ? }...
在上面的示例中,私有方法setupUIElements()調(diào)用了幾個BrowseFragment方法來設(shè)置媒體目錄瀏覽器的樣式:
setBadgeDrawable()將指定的可繪制資源放置在瀏覽片段的右上角,如圖1和2所示。如果還調(diào)用了setTitle()?,則此方法將用可繪制資源替換標(biāo)題字符串。?可繪制的資源應(yīng)該是52dps高。
除非setBadgeDrawable()?,否則setBadgeDrawable()會在瀏覽片段的右上角設(shè)置標(biāo)題字符串。
setHeadersState()和setHeadersTransitionOnBackEnabled()隱藏或禁用標(biāo)題。?有關(guān)更多信息,請參閱隱藏或禁用標(biāo)題?。
setBrandColor()使用指定的顏色值為瀏覽片段中的UI元素(特別是標(biāo)題部分背景顏色setBrandColor()設(shè)置背景顏色。
setSearchAffordanceColor()用指定的顏色值設(shè)置搜索圖標(biāo)的顏色。?搜索圖標(biāo)出現(xiàn)在瀏覽片段的左上角,如圖1和2所示。
圖1中顯示的瀏覽片段列出了左側(cè)窗格中的視頻類別名稱(行標(biāo)題)。?文本視圖顯示視頻數(shù)據(jù)庫中的這些類別名稱。?您可以自定義標(biāo)題以在更復(fù)雜的布局中包含其他視圖。?以下部分顯示如何包含圖像視圖,該圖像視圖在類別名稱旁邊顯示圖標(biāo),如圖2所示。
圖2.瀏覽片段中的行標(biāo)題,帶有圖標(biāo)和文本標(biāo)簽。
行標(biāo)題的布局定義如下:
? ? ? ?
使用Presenter并實現(xiàn)抽象方法來創(chuàng)建,綁定和取消綁定視圖持有者。?以下示例顯示如何使用兩個視圖(一個ImageView和一個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? ? }}
你的頭文件必須是可以調(diào)焦的,這樣D-pad可以用來滾動它們。?有兩種選擇:
將你的視圖設(shè)置為onBindViewHolder()焦點:
@Overridepublic void onBindViewHolder(ViewHolder viewHolder, Object o) {? ? HeaderItem headerItem = ((ListRow) o).getHeaderItem();? ? View rootView = viewHolder.view;? ? rootView.setFocusable(true) // Allows the D-Pad to navigate to this header item? ? //...}
將您的布??局設(shè)置為可對焦:
? ?android:focusable="true">
最后,在顯示目錄瀏覽器的BrowseFragment實現(xiàn)中,使用setHeaderPresenterSelector()方法為行標(biāo)題設(shè)置演示者,如以下示例所示。
setHeaderPresenterSelector(new PresenterSelector() {? ? @Override? ? public Presenter getPresenter(Object o) {? ? ? ? return new IconHeaderItemPresenter();? ? }});
有關(guān)完整示例,請參閱leanback示例中的IconHeaderItemPresenter。
例如,有時您可能不希望行標(biāo)題出現(xiàn):當(dāng)沒有足夠的類別需要可滾動列表時。?在片段的onActivityCreated()方法中調(diào)用BrowseFragment.setHeadersState()方法來隱藏或禁用行標(biāo)題。?setHeadersState()方法將以下常量之一作為參數(shù),設(shè)置瀏覽片段中標(biāo)題的初始狀態(tài):
HEADERS_ENABLED?- 創(chuàng)建瀏覽片段活動時,默認情況下會啟用并顯示標(biāo)題。?標(biāo)題的顯示如圖1和2所示。
HEADERS_HIDDEN?- 創(chuàng)建瀏覽片段活動時,標(biāo)題默認情況下處于啟用和隱藏狀態(tài)。?屏幕的標(biāo)題部分已折疊,如提供卡片視圖的?圖1所示。?用戶可以選擇折疊頁眉部分來展開它。
HEADERS_DISABLED?- 創(chuàng)建瀏覽片段活動時,標(biāo)題默認處于禁用狀態(tài),并且不會顯示。
如果設(shè)置了HEADERS_ENABLED或HEADERS_HIDDEN則可以調(diào)用setHeadersTransitionOnBackEnabled()以支持從行中所選內(nèi)容項移回行標(biāo)題。?這是默認啟用的(如果你不調(diào)用方法),但是如果你想自己處理后退運動,你應(yīng)該將false值傳遞給setHeadersTransitionOnBackEnabled()并實現(xiàn)你自己的后端堆棧處理。
BrowseFragment類允許您使用適配器和演示BrowseFragment從媒體目錄中定義和顯示可瀏覽的媒體內(nèi)容類別和媒體項目。?適配器使您能夠連接到包含媒體目錄信息的本地或聯(lián)機數(shù)據(jù)源。?適配器使用演示者創(chuàng)建視圖并將數(shù)據(jù)綁定到這些視圖以在屏幕上顯示項目。
以下示例代碼顯示了用于顯示字符串?dāng)?shù)據(jù)的Presenter的實現(xiàn):
public class StringPresenter extends Presenter {? ? private static final String TAG = "StringPresenter";? ? public ViewHolder onCreateViewHolder(ViewGroup parent) {? ? ? ? TextView textView = new TextView(parent.getContext());? ? ? ? textView.setFocusable(true);? ? ? ? textView.setFocusableInTouchMode(true);? ? ? ? textView.setBackground(? ? ? ? ? ? ? ? parent.getContext().getResources().getDrawable(R.drawable.text_bg));? ? ? ? return new ViewHolder(textView);? ? }? ? public void onBindViewHolder(ViewHolder viewHolder, Object item) {? ? ? ? ((TextView) viewHolder.view).setText(item.toString());? ? }? ? public void onUnbindViewHolder(ViewHolder viewHolder) {? ? ? ? // no op? ? }}
一旦為媒體項目構(gòu)建了演示者類,就可以構(gòu)建一個適配器并將其附加到BrowseFragment以在屏幕上顯示這些項目以供用戶瀏覽。?以下示例代碼演示了如何使用前面的代碼示例中顯示的StringPresenter類構(gòu)造適配器以顯示這些類別中的類別和項目:
private ArrayObjectAdapter mRowsAdapter;private static final int NUM_ROWS = 4;@Overrideprotected void onCreate(Bundle savedInstanceState) {? ? ...? ? buildRowsAdapter();}private void buildRowsAdapter() {? ? mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());? ? for (int i = 0; i < NUM_ROWS; ++i) {? ? ? ? ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(? ? ? ? ? ? ? ? new StringPresenter());? ? ? ? listRowAdapter.add("Media Item 1");? ? ? ? listRowAdapter.add("Media Item 2");? ? ? ? listRowAdapter.add("Media Item 3");? ? ? ? HeaderItem header = new HeaderItem(i, "Category " + i);? ? ? ? mRowsAdapter.add(new ListRow(header, listRowAdapter));? ? }? ? mBrowseFragment.setAdapter(mRowsAdapter);}
此示例顯示適配器的靜態(tài)實現(xiàn)。?典型的媒體瀏覽應(yīng)用程序使用來自在線數(shù)據(jù)庫或Web服務(wù)的數(shù)據(jù)。?有關(guān)使用從Web檢索的數(shù)據(jù)的瀏覽應(yīng)用程序的示例,請參閱Android Leanback示例應(yīng)用程序?。
為了在電視上為媒體瀏覽應(yīng)用增加視覺趣味,您可以在用戶瀏覽內(nèi)容時更新背景圖片。?這項技術(shù)可以使您的應(yīng)用更具電影感和愉悅感。
Leanback支持庫提供了一個BackgroundManager類,用于更改電視應(yīng)用程序活動的背景。?以下示例顯示如何創(chuàng)建一個簡單方法來更新電視應(yīng)用程序活動中的背景:
protected void updateBackground(Drawable drawable) {? ? BackgroundManager.getInstance(this).setDrawable(drawable);}
當(dāng)用戶瀏覽媒體列表時,許多現(xiàn)有的媒體瀏覽應(yīng)用會自動更新背景。?為了做到這一點,您可以設(shè)置一個選擇偵聽器,根據(jù)用戶的當(dāng)前選擇自動更新背景。?以下示例顯示如何設(shè)置OnItemViewSelectedListener類來捕獲選擇事件并更新背景:
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();? ? ? ? ? ? }? ? ? ? }? ? };}
注意:上面的實現(xiàn)是一個簡單的例子,用于說明。?在自己的應(yīng)用程序中創(chuàng)建此功能時,應(yīng)考慮在單獨的線程中運行后臺更新操作以獲得更好的性能。?此外,如果您計劃更新背景以響應(yīng)用戶滾動瀏覽項目,請考慮增加一段時間來延遲背景圖片更新,直到用戶安置一個項目。?這種技術(shù)避免了過多的背景圖像更新。