本节将会总结下TV开发中的注意点,如何管理TV控制器,如何构建TV布局、如何创建TV导航。
一、处理TV硬件
为啥要处理TV硬件呢?因为TV不像其他安卓设备一样支持触摸屏,照相机、GPS之类的。这些硬件功能在TV上统统被阉割,,,,为了能够开发出更好的应用我们还是有必要了解下这些差异,处理下这些硬件。
1、检查运行环境
安卓的api在TV、Phone等安卓设备上都可以使用的。这时在进行TV开发时,或者你想做个TV手机都支持的app时,这时便需要区分运行环境啦,这样才能针对不同的环境做出不同的解决方案。如下通过UiModeManager 我们就能判断app是否运行在TV上。
UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
Log.i(TAG, "onCreate: "+(uiModeManager.getCurrentModeType()==
Configuration.UI_MODE_TYPE_TELEVISION));
// 下面是Configuration类中定义的部分常量,想了解更多可以查阅下这个类。
public static final int UI_MODE_TYPE_CAR = 0x03;// 汽车
public static final int UI_MODE_TYPE_WATCH = 0x06;// 手表
2、TV不支持的硬件功能
硬件 | 对应字符串 |
---|---|
触摸屏 | android.hardware.touchscreen |
触摸屏模拟器 | android.hardware.faketouch |
电话 | android.hardware.telephony |
相机 | android.hardware.camera |
近距离无线通信 (NFC) | android.hardware.nfc |
GPS | android.hardware.location.gps |
麦克风 | android.hardware.microphone |
传感器 | android.hardware.sensor |
纵向屏幕 | android.hardware.screen.portrait |
ps:
1、某些 TV 控制器具有麦克风,但与此处所述的麦克风硬件功能并不相同。控制器麦克风是完全受支持的。
2、如需查看功能、子功能及其描述符的完整列表,请参阅功能参考
3、上述的字符串都被封装到PackageManager这个类中的常量字段上。可以方便开发者快速调用。
(1)对不支持硬件的处理–声明为非必须支持
如果你开发一款应用,既支持TV,也支持手机。但是你在手机使用到了上述的硬件功能时,你可以吧这些功能声明为TV非必须。
<uses-feature android:name="android.hardware.touchscreen"
android:required="false"/>
<uses-feature android:name="android.hardware.faketouch"
android:required="false"/>
<uses-feature android:name="android.hardware.telephony"
android:required="false"/>
<uses-feature android:name="android.hardware.camera"
android:required="false"/>
<uses-feature android:name="android.hardware.nfc"
android:required="false"/>
<uses-feature android:name="android.hardware.location.gps"
android:required="false"/>
<uses-feature android:name="android.hardware.microphone"
android:required="false"/>
<uses-feature android:name="android.hardware.sensor"
android:required="false"/>
ps:
1、如果将硬件功能的值设为 true 来将其声明为必需功能,就会导致您的应用无法安装在 TV 设备上或出现在 Android TV 主屏幕启动器中。
2、某些功能具有子功能,比如 android.hardware.camera.front。请务必将您的应用中也使用的任何子功能标记为 required=“false”。
3、如何判断设备是否支持硬件
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
Log.d(TAG, "设备支持触屏");
// todo
}else{
Log.d(TAG, "设备不支持触屏");
}
如上栗子,使用PackageManager提供的方法即可。
4、注意某些权限隐含硬件功能
权限 | 隐含硬件功能 |
---|---|
RECORD_AUDIO | android.hardware.microphone |
CAMERA | android.hardware.camera 和 android.hardware.camera.autofocus |
ACCESS_COARSE_LOCATION | android.hardware.location和android.hardware.location.network |
ACCESS_FINE_LOCATION | android.hardware.location和android.hardware.location.gps |
1、如果您的应用请求上面列出的某项功能,请在清单中为这项隐含的硬件功能添加 uses-feature 声明,指明它并非必需功能
2、注意:如果您的应用以 Android 5.0(API 级别 21)或更高版本为目标平台且使用 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION 权限,那么即使 TV 设备没有网卡或 GPS 接收器,用户仍然可以在 TV 设备上安装您的应用。
5、TV上定位的支持
原生api已经被阉割,provider只能使用 netWork啦!但是由于TV上TV厂商没有默认集成高德地图、百度地图、google地图,的服务。所以使用原生api无法获得location对象。要想定位可以集成第三方sdk。
二、TV怎样与用户交互
手机与人最主要的交互方式就是触摸啦!然而TV却不是这样,由于TV的大屏体验,所以一般要求用户在10 英尺以外的地方尽情的欣赏。这时遥控器、游戏手柄等硬件设备就成了交互的媒介。
1、方向键基本控制按钮
1、普通的应用时,您可以使用方向键:上、下、左、右、返回、确认。等按键与TV进行交互。
2、如果是游戏app,您还应该支持更多的手柄按键。
ps:下表就是常见的KeyEvent,我们可以重写这个事件处理下相应的逻辑。例如DrawerLayout在手机上一般右滑动打开,左滑动关闭。在TV上我们应该处理逻辑,让用户点击上下左右中的右键时打开,左键关闭。
KeyEvent | 行为 |
---|---|
BUTTON_B、BACK | 返回 |
BUTTON_SELECT、BUTTON_A、ENTER、DPAD_CENTER、KEYCODE_NUMPAD_ENTER | 选择 |
DPAD_UP、DPAD_DOWN、DPAD_LEFT、DPAD_RIGHT | 导航 |
BUTTON_SELECT、BUTTON_A、ENTER、DPAD_CENTER、KEYCODE_NUMPAD_ENTER | 播放 |
BUTTON_START、BUTTON_SELECT、BUTTON_A、ENTER、DPAD_CENTER、KEYCODE_NUMPAD_ENTER | 暂停 |
BUTTON_R1 | 跳到下一个 |
BUTTON_L1 | 跳到上一个 |
DPAD_RIGHT、BUTTON_R2、AXIS_RTRIGGER、AXIS_THROTTLE | 快进 |
DPAD_LEFT、BUTTON_L2、 AXIS_LTRIGGER、AXIS_BRAKE | 快退 |
(没有与“停止”关联的 KeyEvent) | 停止 |
2、提供适当的返回按钮行为
返回按钮不应充当开关按钮。例如,不要将它用于打开和关闭菜单。它应当仅用于通过面包屑导航方式返回到玩家之前所在的屏幕,例如:游戏过程 > 游戏暂停屏幕 > 游戏主屏幕 > Android 主屏幕。
3、详情参考
三、构建TV布局
TV 屏幕的通常观看距离约为 10 英尺,这就要求您的布局界面以简洁为主啦。一下便是TV布局设计的一些建议。
1、主题背景的建议
(1)Leanback 主题背景
Leanback androidx 库包含 Theme.Leanback,它是 TV Activity 的主题背景,可提供一致的视觉风格。我们强烈建议您将此主题背景用于使用 androidx leanback 类构建的任何 TV 应用
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Leanback">
注意:leanback 主题背景不包含操作栏,因为 Android TV 应用中没有操作栏。如果您的应用使用 BrowseSupportFragment 之类的 Support Fragment,您的 Activity 必须扩展 FragmentActivity。不要使用 AppCompatActivity,它会尝试设置操作栏的主题背景并产生以下错误:
java.lang.RuntimeException: Unable to start activity ComponentInfo{
...} :
java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this
activity.
(2)NoTitleBar 主题背景
标题栏是手机和平板电脑上 Android 应用的标准界面元素,但它并不适合 TV 应用。如果您未使用 androidx leanback 类,则应将此主题背景应用于您的 TV Activity,以禁止显示标题栏。
activity
android:name="com.example.android.TvActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar">
...
</activity>
2、构建TV布局遵守准则
- 构建屏幕方向为横向的布局。TV 屏幕始终以横屏模式显示
- 将屏幕导航控件置于屏幕的左侧或右侧,将节省下的垂直空间留给内容。
- 利用 Fragment 创建按若干部分组织的界面,并利用 GridView(而非 ListView)等视图组,以更充分地利用屏幕的水平空间。
- 利用 RelativeLayout 或 LinearLayout 等视图组来布置视图。此方法让系统能够根据 TV 屏幕的尺寸、对齐、宽高比和像素密度来调整视图的位置
- 在布局控件之间添加足够的外边距,以避免造成界面杂乱无章。
3、过扫描
TV 布局有一些独特的要求,因为 TV 标准不断发展演变,并且用户希望 TV 能够始终向观众呈现全屏画面。为此,TV 设备可能会裁剪应用布局的外边缘以确保填满整个显示屏。此行为通常称为“过扫描”
(1)要求
必须始终对用户可见的屏幕元素应放置在过扫描安全区域内,并且保持可见直接控件margain上下27dp、左右48dp。
(2)栗子
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<!-- Screen elements that can render outside the overscan safe area go here -->
<!-- Nested RelativeLayout with overscan-safe margin -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="27dp"
android:layout_marginBottom="27dp"
android:layout_marginLeft="48dp"
android:layout_marginRight="48dp">
<!-- Screen elements that need to be within the overscan safe area go here -->
</RelativeLayout>
</RelativeLayout>
(3)注意:如果您使用 androidx leanback 类(如 BrowseFragment 或相关微件),请勿对布局应用过扫描外边距,因为这些布局已经纳入了过扫描安全外边距。
4、字体建议
- 使用 Android 的标准字号:
<TextView
android:id="@+id/atext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"/>
5、TV 布局建议
- TV 布局应以 1920 x 1080 像素的屏幕尺寸为目标
- 请尽可能以九宫格图片元素的形式提供图片
6、应该避免使用的
- 复用布局
- ActionBar
- ViewPager
7、处理大图
建议您使用 Glide 库来获取、解码和显示应用中的位图。
更多参考: Android 图形最佳做法
8、提供有效广告
1、建议为30s以内,可以通过控制键关闭跳过之类的。
2、Android TV 不提供网络浏览器。您的广告不得试图启动网络浏览器或者重定向到未经批准用于 Android TV 设备的 Google Play 商店内容。
3、您可以使用 WebView 类来登录社交媒体服务。
四、创建TV导航
使用遥控器进行导航!!!
使用遥控器进行导航!!!
使用遥控器进行导航!!!
1、启用方向键导航
Android 框架会自动处理布局元素之间的方向导航。如上系统会默认为第一个按钮,这时我们通过遥控器上下左右的下就可以导航到按钮2。您通常无需对应用执行任何额外的操作。不过,您应该对通过方向键控制器导航进行全面测试,以发现任何导航问题。请遵循以下准则来测试您应用的导航系统是否能够在 TV 设备上与方向键很好地搭配使用:
- 确保用户可使用方向键控制器导航到屏幕上的所有可见控件.
- 对于获得焦点的滚动列表,请确保可使用方向键的向上键和向下键滚动列表,并可使用 Enter 键选择列表中的项目。验证用户是否可选择列表中的元素,以及选择元素时是否列表仍会滚动
- 确保控件间的切换简单明了并且可以预测
2、修改方向导航
虽然Android 框架会自动处理布局元素之间的方向导航,但是如果您决定希望用户以特定方式在布局中导航,则可以为控件设置显式方向导航。
(1)Android 界面微件的所有可用导航属性
控件属性 | 功能 |
---|---|
nextFocusDown | 定义当用户向下导航时下一个获得焦点的视图。 |
nextFocusLeft | 定义当用户向左导航时下一个获得焦点的视图。 |
nextFocusRight | 定义当用户向右导航时下一个获得焦点的视图。 |
nextFocusUp | 定义当用户向上导航时下一个获得焦点的视图。 |
要使用其中一个显式导航属性,请将值设为布局中另一个微件的 ID(android:id 值)。您应将导航顺序设为循环,以便最后一个控件能够指引焦点回到第一个控件。
(2)栗子 按钮1->按钮3->按钮2->按钮1
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
android:gravity="center">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮1"
tools:ignore="HardcodedText"
android:nextFocusDown="@id/btn3"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮2"
tools:ignore="HardcodedText"
android:nextFocusDown="@id/btn1"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮3"
tools:ignore="HardcodedText"
android:nextFocusDown="@id/btn2"/>
</androidx.appcompat.widget.LinearLayoutCompat>
3、提供明确的焦点和选择
让用户导航到焦点的控件时,控件颜色尺寸凸出之类的,便于用户观看效果。体验交互。安卓给出了button栗子参考:
<!-- res/drawable/button.xml -->
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/button_pressed" /> <!-- pressed -->
<item android:state_focused="true"
android:drawable="@drawable/button_focused" /> <!-- focused -->
<item android:state_hovered="true"
android:drawable="@drawable/button_focused" /> <!-- hovered -->
<item android:drawable="@drawable/button_normal" /> <!-- default -->
</selector>
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="@drawable/button" />