Android Studio V3.12环境下TV开发教程(五)建立电视回放应用

Android Studio V3.12环境下TV开发教程

(转自Android官网https://developer.android.com/training/tv/start)

文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619382


建立电视回放应用

浏览和播放媒体文件通常是电视应用程序提供的用户体验的一部分。 从零开始构建这样的体验,同时确保其快速,流畅和有吸引力可能是相当具有挑战性的。 无论您的应用是否提供对小型或大型媒体目录的访问,允许用户快速浏览选项并获取他们想要的内容非常重要。

Android框架提供了用于使用v17 leanback支持库为这些类型的应用程序构建用户界面的类。 这个库提供了一个类框架,用于创建一个高效且熟悉的界面,以最少的代码浏览和播放媒体文件。 这些课程旨在进行扩展和定制,以便您可以创建独特的应用体验。

本课程向您展示如何使用Leanback电视支持库构建浏览和播放媒体内容的电视应用程序。

主题

创建一个目录浏览器
了解如何使用Leanback支持库为媒体目录构建浏览界面。
提供卡片视图
了解如何使用Leanback支持库为内容项目构建卡片视图。
建立详细信息视图
了解如何使用Leanback支持库为媒体项目构建详细信息页面。
使用Leanback的运输控制
了解如何使用Leanback支持库为您的视频播放器构建传输控件。
显示一张现在玩的牌
了解如何使用MediaSession在主屏幕上显示正在使用的即时贴。
直接在表面上渲染视频
了解您的应用如何直接在主屏幕的表面上呈现预览视频。
添加引导步骤
了解如何使用Leanback支持库来指导用户完成一系列决策。
首次将用户引入您的应用
了解如何使用Leanback支持库向初次使用者展示如何充分利用您的应用程序。
启用后台播放
了解如何在用户点击主页时继续播放。


创建一个目录浏览器

运行在电视上的媒体应用程序需要允许用户浏览其内容产品,进行选择并开始播放内容。 这种类型的应用程序的内容浏览体验应该简单直观,并且在视觉上令人愉悦且引人入胜。

本课讨论如何使用v17 leanback支持库提供的类来实现用于从应用媒体目录中浏览音乐或视频的用户界面。

应用主屏幕

图1. Leanback示例应用程序浏览片段显示视频目录数据。

创建媒体浏览布局

leanback库中的BrowseFragment类允许您使用最少的代码创建用于浏览媒体项目类别和行的主要布局。 以下示例显示如何创建包含BrowseFragment对象的布局:

<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 MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
...

BrowseFragment方法使用视频数据和UI元素填充视图,并设置布局参数(如图标,标题以及是否启用类别标题)。

实现BrowseFragment方法的应用程序的子类还为UI元素上的用户操作设置事件侦听器,并准备后台管理器,如以下示例所示:

 
   
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());
    }
...

设置UI元素

在上面的示例中,私有方法setupUIElements()调用了几个BrowseFragment方法来设置媒体目录浏览器的样式:

  • setBadgeDrawable()将指定的可绘制资源放置在浏览片段的右上角,如图1和2所示。如果还调用了setTitle() ,则此方法将用可绘制资源替换标题字符串。 可绘制的资源应该是52dps高。
  • 除非setBadgeDrawable() ,否则setBadgeDrawable()会在浏览片段的右上角设置标题字符串。
  • setHeadersState()setHeadersTransitionOnBackEnabled()隐藏或禁用标题。 有关更多信息,请参阅隐藏或禁用标题 
  • setBrandColor()使用指定的颜色值为浏览片段中的UI元素(特别是标题部分背景颜色setBrandColor()设置背景颜色。
  • setSearchAffordanceColor()用指定的颜色值设置搜索图标的颜色。 搜索图标出现在浏览片段的左上角,如图1和2所示。

自定义标题视图

图1中显示的浏览片段列出了左侧窗格中的视频类别名称(行标题)。 文本视图显示视频数据库中的这些类别名称。 您可以自定义标题以在更复杂的布局中包含其他视图。 以下部分显示如何包含图像视图,该图像视图在类别名称旁边显示图标,如图2所示。

应用主屏幕

图2.浏览片段中的行标题,带有图标和文本标签。

行标题的布局定义如下:

 
   
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/header_icon"
        android:layout_width="32dp"
        android:layout_height="32dp" />
    <TextView
        android:id="@+id/header_label"
        android:layout_marginTop="6dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

使用Presenter并实现抽象方法来创建,绑定和取消绑定视图持有者。 以下示例显示如何使用两个视图(一个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
    }
}

你的头文件必须是可以调焦的,这样D-pad可以用来滚动它们。 有两种选择:

  • 将你的视图设置为onBindViewHolder()焦点:
    @Override
    public 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
        //...
    }
  • 将您的布​​局设置为可对焦:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       ...
       android:focusable="true">

最后,在显示目录浏览器的BrowseFragment实现中,使用setHeaderPresenterSelector()方法为行标题设置演示者,如以下示例所示。

 
   
setHeaderPresenterSelector(new PresenterSelector() {
    @Override
    public Presenter getPresenter(Object o) {
        return new IconHeaderItemPresenter();
    }
});

有关完整示例,请参阅leanback示例中的IconHeaderItemPresenter。

隐藏或禁用标题

例如,有时您可能不希望行标题出现:当没有足够的类别需要可滚动列表时。 在片段的onActivityCreated()方法中调用BrowseFragment.setHeadersState()方法来隐藏或禁用行标题。 setHeadersState()方法将以下常量之一作为参数,设置浏览片段中标题的初始状态:

  • HEADERS_ENABLED - 创建浏览片段活动时,默认情况下会启用并显示标题。 标题的显示如图1和2所示。
  • HEADERS_HIDDEN - 创建浏览片段活动时,标题默认情况下处于启用和隐藏状态。 屏幕的标题部分已折叠,如提供卡片视图的 图1所示。 用户可以选择折叠页眉部分来展开它。
  • HEADERS_DISABLED - 创建浏览片段活动时,标题默认处于禁用状态,并且不会显示。

如果设置了HEADERS_ENABLEDHEADERS_HIDDEN则可以调用setHeadersTransitionOnBackEnabled()以支持从行中所选内容项移回行标题。 这是默认启用的(如果你不调用方法),但是如果你想自己处理后退运动,你应该将false值传递给setHeadersTransitionOnBackEnabled()并实现你自己的后端堆栈处理。

显示媒体列表

BrowseFragment类允许您使用适配器和演示BrowseFragment从媒体目录中定义和显示可浏览的媒体内容类别和媒体项目。 适配器使您能够连接到包含媒体目录信息的本地或联机数据源。 适配器使用演示者创建视图并将数据绑定到这些视图以在屏幕上显示项目。

以下示例代码显示了用于显示字符串数据的Presenter的实现:

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
    }
}

一旦为媒体项目构建了演示者类,就可以构建一个适配器并将其附加到BrowseFragment以在屏幕上显示这些项目以供用户浏览。 以下示例代码演示了如何使用前面的代码示例中显示的StringPresenter类构造适配器以显示这些类别中的类别和项目:

 
   
private ArrayObjectAdapter mRowsAdapter;
private static final int NUM_ROWS = 4;

@Override
protected 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);
}

此示例显示适配器的静态实现。 典型的媒体浏览应用程序使用来自在线数据库或Web服务的数据。 有关使用从Web检索的数据的浏览应用程序的示例,请参阅Android Leanback示例应用程序 

更新背景

为了在电视上为媒体浏览应用增加视觉趣味,您可以在用户浏览内容时更新背景图片。 这项技术可以使您的应用更具电影感和愉悦感。

Leanback支持库提供了一个BackgroundManager类,用于更改电视应用程序活动的背景。 以下示例显示如何创建一个简单方法来更新电视应用程序活动中的背景:

 
   
protected void updateBackground(Drawable drawable) {
    BackgroundManager.getInstance(this).setDrawable(drawable);
}

当用户浏览媒体列表时,许多现有的媒体浏览应用会自动更新背景。 为了做到这一点,您可以设置一个选择侦听器,根据用户的当前选择自动更新背景。 以下示例显示如何设置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();
            }
        }
    };
}

注意:上面的实现是一个简单的例子,用于说明。 在自己的应用程序中创建此功能时,应考虑在单独的线程中运行后台更新操作以获得更好的性能。 此外,如果您计划更新背景以响应用户滚动浏览项目,请考虑增加一段时间来延迟背景图片更新,直到用户安置一个项目。 这种技术避免了过多的背景图像更新。



猜你喜欢

转载自blog.csdn.net/jiawuhan/article/details/80619382