目录创建 android,创建目录浏览器 | Android 开发者 | Android Developers
在 TV 上运行的媒体应用需要允许用户浏览其提供的内容、选择要播放的内容,以及开始播放内容。此类应用的内容浏览体验应简单直观,并且视觉上要赏心悦目。
本节课介绍如何利用 Leanback androidx 库提供的类来实现一个用户界面,该界面可让用户浏览应用的媒体目录中包含的音乐或视频。
注意:此处显示的实现示例使用的是 BrowseSupportFragment 扩展了 AndroidX
图 1. Leanback 示例应用浏览 Fragment 中显示视频目录数据。
创建媒体浏览布局
通过 Leanback 库中的
android:id="@+id/main_frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:name="com.example.android.tvleanback.ui.MainFragment"
android:id="@+id/main_browse_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
应用的主 Activity 用于设置此视图,如下例所示:
Kotlin
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main)
}
...Java
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
...
如需详细了解如何设置界面元素,请参阅设置界面元素。
如需详细了解如何隐藏标题,请参阅隐藏或停用标题。
实现
Kotlin
class MainFragment : BrowseSupportFragment(),
LoaderManager.LoaderCallbacks>> {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadVideoData()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
prepareBackgroundManager()
setupUIElements()
setupEventListeners()
}
...
private fun prepareBackgroundManager() {
backgroundManager = BackgroundManager.getInstance(activity).apply {
attach(activity?.window)
}
defaultBackground = resources.getDrawable(R.drawable.default_background)
metrics = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getMetrics(metrics)
}
private fun setupUIElements() {
badgeDrawable = resources.getDrawable(R.drawable.videos_by_google_banner)
// Badge, when set, takes precedent over title
title = getString(R.string.browse_title)
headersState = BrowseSupportFragment.HEADERS_ENABLED
isHeadersTransitionOnBackEnabled = true
// set headers background color
brandColor = ContextCompat.getColor(requireContext(), R.color.fastlane_background)
// set search icon color
searchAffordanceColor = ContextCompat.getColor(requireContext(), R.color.search_opaque)
}
private fun loadVideoData() {
VideoProvider.setContext(activity)
videosUrl = getString(R.string.catalog_url)
loaderManager.initLoader(0, null, this)
}
private fun setupEventListeners() {
setOnSearchClickedListener {
Intent(activity, SearchActivity::class.java).also { intent ->
startActivity(intent)
}
}
onItemViewClickedListener = ItemViewClickedListener()
onItemViewSelectedListener = ItemViewSelectedListener()
}
...Java
public class MainFragment extends BrowseSupportFragment implements
LoaderManager.LoaderCallbacks>> {
}
...
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadVideoData();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
prepareBackgroundManager();
setupUIElements();
setupEventListeners();
}
...
private void prepareBackgroundManager() {
backgroundManager = BackgroundManager.getInstance(getActivity());
backgroundManager.attach(getActivity().getWindow());
defaultBackground = getResources()
.getDrawable(R.drawable.default_background);
metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
}
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(ContextCompat.getColor(requireContext(), R.color.fastlane_background));
// set search icon color
setSearchAffordanceColor(ContextCompat.getColor(requireContext(), R.color.search_opaque));
}
private void loadVideoData() {
VideoProvider.setContext(getActivity());
videosUrl = 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() 调用了几个
setTitle(),此方法会将标题字符串替换为可绘制资源。可绘制资源的高度应为 52dp。
如果未调用 setBadgeDrawable(),
自定义标题视图
图 1 中所示的浏览 Fragment 在左侧窗格中列出了视频类别名称(行标题)。文本视图显示的这些类别名称来自视频数据库。您可以自定义标题,以便在更复杂的布局中添加其他视图。下面几部分展示了如何添加图片视图,以在类别名称旁边显示一个图标,如图 2 中所示。
图 2. 浏览 Fragment 中的行标题,同时具有图标和文本标签。
行标题的布局定义如下:
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/header_icon"
android:layout_width="32dp"
android:layout_height="32dp" />
android:id="@+id/header_label"
android:layout_marginTop="6dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
可以使用
Kotlin
class IconHeaderItemPresenter : Presenter() {
override fun onCreateViewHolder(viewGroup: ViewGroup): Presenter.ViewHolder {
val view = LayoutInflater.from(viewGroup.context).run {
inflate(R.layout.icon_header_item, null)
}
return Presenter.ViewHolder(view)
}
override fun onBindViewHolder(viewHolder: Presenter.ViewHolder, o: Any) {
val headerItem = (o as ListRow).headerItem
val rootView = viewHolder.view
rootView.findViewById(R.id.header_icon).apply {
rootView.resources.getDrawable(R.drawable.ic_action_video, null).also { icon ->
setImageDrawable(icon)
}
}
rootView.findViewById(R.id.header_label).apply {
text = headerItem.name
}
}
override fun onUnbindViewHolder(viewHolder: Presenter.ViewHolder) {
// no op
}
}Java
public class IconHeaderItemPresenter extends Presenter {
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
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
}
}
您的标题必须可聚焦,以便可以使用方向键来滚动浏览。有两个备选方式:
在
Kotlin
override fun onBindViewHolder(viewHolder: Presenter.ViewHolder, o: Any) {
val headerItem = (o as ListRow).headerItem
val rootView = viewHolder.view
rootView.focusable = View.FOCUSABLE
//...
}Java
@Override
public void onBindViewHolder(ViewHolder viewHolder, Object o) {
HeaderItem headerItem = ((ListRow) o).getHeaderItem();
View rootView = viewHolder.view;
rootView.setFocusable(View.FOCUSABLE) // Allows the D-Pad to navigate to this header item
//...
}
将您的布局设为可聚焦:
...
android:focusable="true">
Kotlin
setHeaderPresenterSelector(object : PresenterSelector() {
override fun getPresenter(o: Any): Presenter {
return IconHeaderItemPresenter()
}
})Java
setHeaderPresenterSelector(new PresenterSelector() {
@Override
public Presenter getPresenter(Object o) {
return new IconHeaderItemPresenter();
}
});
如需查看完整示例,请参阅 Android TV GitHub 代码库中的 Android Leanback 示例应用。
隐藏或停用标题
有时,您可能不希望显示行标题。例如:当类别数量不是很多,无需使用可滚动列表时。在 Fragment 的
提供卡片视图的图 1 中所示。用户可选择收起的标题部分,以将其展开。
显示媒体列表
通过
以下示例代码展示了一个用于显示字符串数据的
Kotlin
private const val TAG = "StringPresenter"
class StringPresenter : Presenter() {
override fun onCreateViewHolder(parent: ViewGroup): Presenter.ViewHolder {
val textView = TextView(parent.context).apply {
isFocusable = true
isFocusableInTouchMode = true
background = parent.resources.getDrawable(R.drawable.text_bg)
}
return Presenter.ViewHolder(textView)
}
override fun onBindViewHolder(viewHolder: Presenter.ViewHolder, item: Any) {
(viewHolder.view as TextView).text = item.toString()
}
override fun onUnbindViewHolder(viewHolder: Presenter.ViewHolder) {
// no op
}
}Java
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.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
}
}
为媒体内容构建 Presenter 类后,您可以构建 Adapter 并将其附加到 StringPresenter 类来显示类别以及这些类别的内容:
Kotlin
private const val NUM_ROWS = 4
...
private lateinit var rowsAdapter: ArrayObjectAdapter
override fun onCreate(savedInstanceState: Bundle?) {
...
buildRowsAdapter()
}
private fun buildRowsAdapter() {
rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
for (i in 0 until NUM_ROWS) {
val listRowAdapter = ArrayObjectAdapter(StringPresenter()).apply {
add("Media Item 1")
add("Media Item 2")
add("Media Item 3")
}
HeaderItem(i.toLong(), "Category $i").also { header ->
rowsAdapter.add(ListRow(header, listRowAdapter))
}
}
browseSupportFragment.adapter = rowsAdapter
}Java
private ArrayObjectAdapter rowsAdapter;
private static final int NUM_ROWS = 4;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
buildRowsAdapter();
}
private void buildRowsAdapter() {
rowsAdapter = 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);
rowsAdapter.add(new ListRow(header, listRowAdapter));
}
browseSupportFragment.setAdapter(rowsAdapter);
}
此示例展示了 Adapter 的一个静态实现。典型的媒体浏览应用会使用来自在线数据库或网络服务的数据。如需查看使用从网络检索的数据的浏览应用示例,请参阅 Android TV GitHub 代码库中的 Android Leanback 示例应用。
更新背景
为了增强 TV 上媒体浏览应用的视觉吸引力,您可以在用户浏览内容时更新背景图片。此方法可令用户与应用的交互更加赏心悦目。
Leanback 支持库提供了一个
Kotlin
protected fun updateBackground(drawable: Drawable) {
BackgroundManager.getInstance(this).drawable = drawable
}Java
protected void updateBackground(Drawable drawable) {
BackgroundManager.getInstance(this).setDrawable(drawable);
}
许多现有的媒体浏览应用都会在用户浏览媒体列表时自动更新背景。为了实现此项更新,您可以设置一个选择监听器,以根据用户的当前选择自动更新背景。以下示例展示了如何设置一个
Kotlin
protected fun clearBackground() {
BackgroundManager.getInstance(this).drawable = defaultBackground
}
protected fun getDefaultItemViewSelectedListener(): OnItemViewSelectedListener =
OnItemViewSelectedListener { _, item, _, _ ->
if (item is Movie) {
item.getBackdropDrawable().also { background ->
updateBackground(background)
}
} else {
clearBackground()
}
}Java
protected void clearBackground() {
BackgroundManager.getInstance(this).setDrawable(defaultBackground);
}
protected OnItemViewSelectedListener getDefaultItemViewSelectedListener() {
return new OnItemViewSelectedListener() {
@Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof Movie ) {
Drawable background = ((Movie)item).getBackdropDrawable();
updateBackground(background);
} else {
clearBackground();
}
}
};
}
注意:以上实现是出于说明目的而展示的一个简单示例。在您自己的应用中创建此功能时,您应考虑在一个单独的线程内运行背景更新操作,以便获得更好的性能。此外,如果您计划在用户滚动浏览内容时更新背景,不妨考虑添加一个延迟时间,使背景图片更新延迟到用户选择某个内容后再执行。此方法可以避免背景图片更新过于频繁。
目录创建 android,创建目录浏览器 | Android 开发者 | Android Developers相关推荐
- android 文件浏览器源码,android 文件管理器源码
[实例简介] [实例截图] [核心代码] package com.android.FileBrowser; import java.io.File; import java.util.ArrayLis ...
- android 平板安装程序开发者,android – 限制平板电脑中的应用安装
我有一个 Android应用程序,我想在Tablet中停止安装此应用程序.我搜索了很多网站.我有一些想法并遵循了.但这不是限制. 以下是我访问过的一些网站. 我已尝试使用支持屏幕仍然应用程序正在平板电 ...
- 最新android studio创建项目,创建项目 | Android 开发者 | Android Developers
利用 Android Studio,您可以轻松地为各种类型的设备(例如手机.平板电脑.电视和 Wear 设备)创建 Android 应用.本页介绍了如何启动新的 Android 应用项目或导入现有项目 ...
- 另辟蹊径创建移动应用:iOS和Android代码共享
2019独角兽企业重金招聘Python工程师标准>>> 过去几年,移动应用席卷了整个世界,在工作和生活的方方面面改变着我们使用互联网的方式.创建移动应用的各种技术也随之兴起,各种开发 ...
- Android 实现浏览器打开app
我们经常看到当点击一个链接的时候,跳转到app,比如当我们在网页端浏览新闻的时候,要想查看更多评论等就会提示你跳转到app内打开查看,那是如何实现网页中打开app的呢? 怎么实现? 要想实现浏览器内打 ...
- Android 12 首个开发者预览版到来
作者 / Dave Burke,工程副总裁 从手机和笔记本电脑,再到平板电脑.电视甚至汽车,Android 应用每天都在各种设备上支持着数十亿人的工作.娱乐.交流和创造.当越来越多的人开始依赖您所构建 ...
- android安装自动打开网页,Android调用系统自带浏览器打开网页的实现方法
Android调用系统自带浏览器打开网页的实现方法 在Android中可以调用自带的浏览器,或者指定一个浏览器来打开一个链接.只需要传入一个uri,可以是链接地址. 启动android默认浏览器 在A ...
- android finish()传参数,48. (android开发) activity间传递数据(打开浏览器、拨号盘、传参)...
在 android 中多个 activity 中互相访问是常见的一种操作,比如:打开系统浏览器.显示系统拨号盘等等. 做这些操作的时候,自然是需要传递一些数据过去,比如:要打开的网址.要拨打的电话号码 ...
- Chrome模拟手机浏览器(iOS/Android)的三种方法,亲测无误!
各大网站都有推出自己的手机访问版本页面,不管是新闻类还是视频网站,我们在电脑是无法直接访问到手机网站的,比如我经常访问一个3g.qq.com这个手机站点,如果在电脑上直接打开它,则会跳转到其它页面,一 ...
最新文章
- matlab var std,Matlab var std cov 函数解析
- linux 串口命令
- php模板怎么导入服务器_php项目怎么放到服务器
- 操作系统实验_Chcore -- 上交IPADS操作系统银杏书配套Lab实验笔记 - Lab2内存管理(一)...
- SonarLint插件的安装与使用
- Atitit.软件硕士 博士课程 一览表 attilax 总结
- Mybatis中mysql blob类型乱码解决
- Go爬虫colly官方示例三【cryptocoinmarketcap】- 爬取加密数字货币最新市值
- wifi ap6212驱动移植及调试分析技术笔记
- 做好目标管理和任务管理,提高办公协同效率
- Proteus做C51最小系统的仿真
- ppt转图片LibreOffice解决方案--java
- android系统自带system/app下载,Android 把应用APK安装包放到system/app下的方法
- 容联完成1.25亿美元F轮融资
- 蓝桥杯国赛C++A组B组题解整理(第八、七、六、五、四届)
- 车载以太网 - SomeIP - 总纲
- 遥感学报(湖泊遥感专刊)投稿历程
- mariadb 卸载 Kali_流氓软件卸载不掉?赶紧试试这款免费专业的卸载神器!专治各类顽固派软件!...
- hp服务器pe系统安装win7系统安装系统安装失败,使用硬盘安装系统出现pGptRestore部署失败怎么解决?...
- 设计计算机系统,紫外可见分光光度计的计算机系统设计
热门文章
- 强化学习笔记: MDP - Policy iteration
- 模型独立学习:多任务学习与迁移学习
- Myeclipse10下搭建SSH框架(图解)Struts2.1+Spring3.0+Hibernate3.3
- python线程同步锁_Python实现的多线程同步与互斥锁功能示例
- matlab绘制横向柱状图
- 多种负载均算法及其 Java 代码实现 --转
- spring mvc DispatcherServlet详解之拾忆工具类utils
- Linux内核--网络栈实现分析(一)--网络栈初始化--转
- 【特征工程】(未完成)编码
- 【风控场景】互利网上数字金融典型场景: 网购运费险