Fire TV为开发者提供了多种向客户展示内容的方式。基于HTML5的网页应用可能是一个很好的候选项,可以提供与您的其他服务一致的客户体验,并能缩短上市时间,从而让您更容易在多个市场和设备中部署应用。它还能简化推出更新的工作,无需构建和发布整个应用。
在本教程中,您将学习如何使用Amazon WebView (AWV) 技术为Fire TV设备创建包装器应用,该技术同时适用于方向键和触摸交互。关于用户体验最佳实践的Fire TV文档说明了应用导航和输入的基本方面。
教程将涵盖4个关键步骤:
步骤1:声明触摸屏硬件功能
要支持基本的触摸交互事件,如点击和拖动,请将android.hardware.touchscreen声明添加到AndroidManifest.xml文件中。此功能指示应用与拥有实际触摸屏或模拟触摸屏(“伪触摸”接口)的设备兼容。我们需要添加此项,因为我们将以具有触摸屏的Fire TV设备为目标。
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...>
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
...
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.myapplication"
tools:ignore="MissingLeanbackLauncher">
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-feature
android:name="android.software.leanback"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:banner="@drawable/amazon_firetv"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true">
<activity
android:name=".MainActivity"
android:configChanges="fontScale"
android:label="@string/player_activity_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<uses-library
android:name="com.amazon.webview"
android:required="false" />
</application>
</manifest>
步骤2:添加对Amazon WebView (AWV) 的支持并启用JavaScript
WebView类是Android的View类的扩展,它允许您将网页作为Activity布局的一部分来呈现。它不包括完全开发的网页浏览器的任何功能,如导航控件或地址栏。WebView在默认情况下只会显示网页。
所有运行Fire OS 5及更高版本的Fire TV设备都包含Amazon WebView (AWV),作为标准Android WebView类的透明替代品。AWV使用了针对Fire TV设备优化的Chromium自定义构建,提供了更快、更高效的渲染,更好的视频性能和改进的资源管理。
要在布局中向应用添加WebView,可以将以下代码添加至您活动的布局XML文件:
activity_main.xml
<FrameLayout 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:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
</FrameLayout>
现在,在您的Fire TV应用中,创建一个包含WebView的Activity,然后使用它在onCreate()中显示您的在线网页应用。要在WebView中加载网页,可以使用loadUrl()。在以下示例中,mBaseUri保存您的在线托管网页应用的地址。
MainActivity.java
public class MainActivity extends Activity {
// Address to your online hosted web application
private final String mBaseUri = "https://mysampleapp.com";
private Context mContext = null;
private WebView mWebView;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this.getApplicationContext();
// An activity is a single, focused thing that the customer can do.
// Activity class takes care of creating a window for you in which
// you can place your layout resource defining your UI.
// In this example layout resource is defined in activity_main.xml.
setContentView(R.layout.activity_main);
// Retrieve the WebView widget in UI that you need to interact with.
// In this example WebView widget is identified by "web_view" in activity_main.xml.
mWebView = findViewById(R.id.web_view);
mWebView.loadUrl(mBaseUrl);
}
...
}
启用JavaScript
由于您的网页应用很可能使用JavaScript来实现各种客户交互并与后端服务进行通信,因此您必须为您的WebView启用JavaScript。
默认情况下,WebView中已禁用JavaScript。您必须通过附加至您的WebView的WebSettings来启用它。要实现这一点,请使用getSettings()检索WebSettings,然后使用setJavaScriptEnabled()启用JavaScript。
MainActivity.java
public class MainActivity extends Activity {
private final String mBaseUrl = "https://mysampleapp.com";
private Context mContext = null;
private WebView mWebView;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mWebView = findViewById(R.id.web_view);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
...
mWebView.loadUrl(mBaseUrl);
}
...
}
WebSettings提供了您可能会发现有用的各种其他设置的访问权限。例如,如果您正在开发一个专门为Fire TV应用中的WebView设计的网页应用,那么您可以使用setUserAgentString()定义一个自定义用户代理字符串,然后查询网页中的自定义用户代理,以验证请求您的网页的客户端确实是您的Fire TV应用。
步骤3:处理页面导航和设备配置更改
注意:如果您的网页应用构建为仅加载单个网页文档的SPA(单页应用),然后通过JavaScript API更新该单个文档的正文内容,则可以跳过此步骤。
非SPA网页应用的导航
如果您的网页应用要求客户单击链接以导航到应用的其他部分,例如导航菜单,则当客户在您的WebView中单击链接时,Fire TV的默认行为是启动处理URL的应用。通常情况下,默认的网页浏览器会打开并加载目标URL。但是,您可以覆盖WebView的此行为,以便在WebView中打开链接。然后,您可以允许客户在WebView维护的网页历史记录中前后导航。
注意:出于安全原因,Fire OS浏览器应用不会与您的网页应用共享其应用数据。
要打开客户点击的所有链接,可以使用setWebViewClient()
为您的WebView提供WebViewClient
。创建一个覆盖shouldOverrideUrlLoading()
的自定义WebViewClient,让主机应用有机会在当前WebView中将要加载URL时进行控制。
在下面的示例中,我们将创建一个CustomWebViewClient作为Activity
的内部类和WebView的此新WebViewClient的实例。
MainActivity.java
public class MainActivity extends Activity {
private final String mBaseUrl = "https://mysampleapp.com";
private WebView mWebView;
private CustomWebViewClient webViewClient;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mWebView = findViewById(R.id.web_view);
webViewClient = new CustomWebViewClient();
mWebView.setWebViewClient(webViewClient);
...
mWebView.loadUrl(mBaseUrl);
}
...
}
class CustomWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(request.getUrl().toString());
return super.shouldOverrideUrlLoading(view, request);
}
}
响应无障碍功能自定义的设备配置更改
Fire TV提供各种无障碍功能。其中之一是文本横幅,这是一种屏幕上的文本横幅,可以帮助有视觉障碍的客户在屏幕上查看文本。当客户在运行时更改无障碍功能时,活动状态会发生变化,从而导致设备配置发生变化。这可能会干扰要销毁的WebView对象的活动和要创建的新活动,这也会创建一个加载销毁对象URL的新WebView对象。
为了在运行时处理配置更改,使您的应用在特定配置更改期间不需要更新资源并避免活动重新启动,请声明您的活动本身会处理fontScale配置更改,这样可以阻止系统重新启动您的活动。
AndroidManifest.xml
<activity
android:name=".MainActivity"
android:configChanges="fontScale"
android:label="@string/player_activity_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme">
...
</activity>
现在,当配置更改时,MainActivity不会重新启动。MainActivity会改为收到对于onConfigurationChanged()的调用。此方法传递一个指定新设备配置的Configuration对象。通过读取Configuration中的字段,您可以确定新配置,并通过更新接口中使用的资源进行适当的更改。在调用此方法时,会更新活动的Resources对象,以根据新配置返回资源,这样您就可以轻松重置用户界面元素,而无需让系统重新启动活动。
MainActivity.java
public class MainActivity extends Activity {
...
@Override
public void onConfigurationChanged(final Configuration c) {
super.onConfigurationChanged(c);
mWebView.getSettings().setTextZoom((int) (c.fontScale * 100));
}
}
这就是显示网页的基本WebView所需的全部内容。此外,您还可以通过修改以下内容来自定义WebView:
步骤4:处理WebView和网页应用之间的交互
从webview发送方向键事件
与任何Android输入设备一样,所有Amazon Fire TV遥控器都会为按键按下动作生成KeyEvent事件。您可以使用标准的Android事件侦听器接口和回调来处理控制器按钮输入。Amazon Fire TV遥控器和语音遥控器都不会引发动作事件(来自Android MotionEvent类)。
注意:Fire TV Edition的遥控器具有一些额外的按钮,例如调高/调低音量、电源,以及可用来直接打开相关应用的特定应用按钮。但是,无法将这些按钮映射到第三方应用中的事件。
通过管理方向键交互,您可以极大地增强应用中的客户体验。Android框架提供了用于检测和处理方向键(或任何其他控制器)输入的API。在Activity类中,覆盖dispatchKeyEvent()方法,并根据需要添加自定义项。以下示例显示了如何更改方向键上的BACK(后退)键以始终返回WebView,并且在没有可导航的历史记录时退出应用。如果同时覆盖dispatchKeyEvent()方法,则可以将关键事件发送到WebView,WebView可以在您的网页应用中使用JavaScript处理这些事件,以执行所需的操作。
MainActivity.java
public class MainActivity extends Activity {
private final String mBaseUrl = "https://mysampleapp.com";
private Context mContext = null;
private WebView mWebView;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
}
...
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.d(TAG, "dispatchKeyEvent : KeyCode = " + event.getKeyCode() + " (" + KeyEvent.keyCodeToString(event.getKeyCode()) + ")");
switch (event.getKeyCode()) {
// Handle back button from D-Pad
case KeyEvent.KEYCODE_BACK:
Log.d(TAG, "dispatchKeyEvent : mWebView.canGoBack = " + mWebView.canGoBack());
if (mWebView.canGoBack()) {
// Navigate back on WebView
mWebView.goBack();
} else {
// WebView has no more history, exit the app
finish();
}
}
return super.dispatchKeyEvent(event);
}
}
D/MainActivity: dispatchKeyEvent : KeyCode = 85 (KEYCODE_MEDIA_PLAY_PAUSE)
在javascript中处理方向键交互
在您的网页应用的JavaScript中将客户的方向键交互作为KeyboardEvent事件接收。要处理KeyboardEvent,您需要执行以下步骤:
1. 选择将在其上触发事件的 [Element] (https://developer.mozilla.org/en-US/docs/Web/API/Element)。由于我们使用WebView在Fire TV包装器应用中呈现网页应用的内容,因此建议在 [Window] (https://developer.mozilla.org/en-US/docs/Web/API/Window) 对象上侦听事件。
2. 使用 [element.addEventListener()] (https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) 来注册事件处理程序。对于方向键上的每次按下键的操作,JavaScript都会收到两个事件。
a.keydown–当按下方向键上的一个键时,如果长时间按下该键,则自动重复按下。
b.keyup–从方向键上的键松开而不再按压时。
在下面的例子中,我们使用一个名为Video.js的开源HTML5视频播放器来设置基本的媒体播放器体验。方向键交互一但开始,即会触发keydown事件,出于演示目的,我们将整个文档的背景设置为绿色。当从按下的键上松开时,就会触发keyup事件。在handleKeyEvent()方法中定义了自定义键处理机制。
index.html
// https://videojs.com/getting-started/
const player = videojs("video", {...});
// Custom Key Event Handler
const moveTime = 10; // Move time by number of seconds
const handleKeyEvent = (event) => {
switch(event.code) {
case "MediaPlayPause":
case "NumpadEnter":
// Check is the playback is paused
if (player.paused()) {
videojs.log('handleKeyEvent:', 'Play', player.currentTime());
// Resume the playback
player.play();
} else {
videojs.log('handleKeyEvent:', 'Pause', player.currentTime());
// Pause the playback
player.pause();
}
break;
case "MediaRewind":
case "ArrowLeft":
// Move back the playback time by 10 seconds
player.currentTime((player.currentTime() - moveTime) < 0 ? 0 : (player.currentTime() - moveTime));
videojs.log('handleKeyEvent:', 'Rewind', player.currentTime());
break;
case "MediaFastForward":
case "ArrowRight":
// Move forward the playback time by 10 seconds
player.currentTime(player.currentTime() + moveTime);
videojs.log('handleKeyEvent:', 'Fast Forward', player.currentTime());
break;
default:
videojs.log('handleKeyEvent:', 'Unhandled event.code =', event.code);
break;
}
}
// Listen for Keyboard Events dispatched by Fire TV app's WebView
window.addEventListener("keyup", (event) => {
// Reset the background colour when key press is lifted
document.body.style.background = "";
if (event.key !== undefined) {
console.log('keyup:', 'event.key =', event.key);
// Handle the event with KeyboardEvent.key
handleKeyEvent(event);
}
});
window.addEventListener("keydown", (event) => {
// Change background colour when key press is down
document.body.style.background = "green";
});
从webview发送触摸事件
尽管您的应用不应完全依赖触摸手势进行基本行为(因为这种形式的交互可能并不适用于所有Fire TV设备),但是在应用包装器应用中添加基于触摸的交互还是可以大幅提高其实用性和吸引力。
当客户将一根或多根手指放在支持触摸的设备的屏幕上时,如果您的应用将该触摸模式解释为特定手势,就会发生触摸手势。手势检测分为两个阶段
1. 收集有关触摸事件的数据 - 当客户将一根或多根手指放在屏幕上时,会在接收触摸事件的视图上触发onTouchEvent()回调。对于最终被识别为手势的每个触摸事件序列(位置、压力、大小、另外添加一根手指等),会将onTouchEvent()触发多次。
2. 解释数据,以确定其是否符合您应用支持的任何手势的条件。手势从客户第一次触摸屏幕时开始,随着系统跟踪客户手指的位置而继续,并通过捕捉客户手指离开屏幕的最终事件而结束。在整个交互过程中,传递给onTouchEvent()的MotionEvent提供每个交互的详细信息。您的应用可以使用MotionEvent提供的数据来确定是否发生了它关注的手势。
在下面的例子中,我们在应用的Activity类中拦截触摸事件,覆盖onTouchEvent()回调。该代码段使用getActionMasked()从事件参数中提取客户执行的操作。这样可为您提供原始数据,在确定是否发生了您所关注的手势时需要这些数据:
MainActivity.java
public class MainActivity extends Activity {
...
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
switch(action) {
case (MotionEvent.ACTION_DOWN):
Log.d(TAG,"Action was DOWN");
return true;
case (MotionEvent.ACTION_MOVE):
Log.d(TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP):
Log.d(TAG,"Action was UP");
return true;
case (MotionEvent.ACTION_CANCEL):
Log.d(TAG,"Action was CANCEL");
return true;
case (MotionEvent.ACTION_OUTSIDE) :
Log.d(TAG,"Movement occurred outside bounds of current screen element");
return true;
default:
return super.onTouchEvent(event);
}
}
}
在JavaScript中处理触摸交互
触摸事件与鼠标事件类似,不同之处在于它们支持在触摸表面上的不同位置同时进行触摸。TouchEvent接口包含了当前活动的所有触摸点。Touch接口表示单个触摸点,包含触摸点相对于浏览器视口的位置等信息。
触摸事件由三个接口(Touch、TouchEvent和TouchList)和以下事件类型组成:
继续前面的HTML5视频播放器的例子,我们现在可以在播放器的媒体控件中定义的按钮元素上添加一些基本触摸交互。playPauseControl元素以播放器的“播放/暂停”按钮为目标,而muteControl以音频“静音/取消静音”按钮为目标。您可以从MDN网页文档中了解更多关于JavaScript触摸事件的信息,并且Google网页基础知识上提供了将触摸事件添加到应用的指南。
index.html
const player = videojs("video", {...});
// Listen for Touch Events dispatched by Fire TV app's WebView
// Play/Pause
let playPauseControl = document.getElementsByClassName('vjs-play-control')[0];
playPauseControl.addEventListener("click touchstart", (event) => {
videojs.log('playPauseControl', 'touched');
});
// Mute/Un-mute
let muteControl = document.getElementsByClassName('vjs-mute-control')[0];
muteControl.addEventListener("click touchstart", (event) => {
videojs.log('muteControl', 'touched');
});
结论
如果您计划将现有的基于HTML5的网页应用作为客户端应用的一部分提供给Fire TV,本指南将帮助实现流畅的客户体验所需的必要交互和导航。您可以在亚马逊开发者门户上了解有关如何为Fire设备优化网页应用的更多信息。
有关更多详情,请查看我们的文档: