作者:Jameson Quave,原文链接,原文日期:2015/09/10
译者:ray16897188;校对:千叶知风;定稿:shanks
本文是 tvOS 教程的第二部分。如果你还没看过第一部分(译文链接),我建议你先看那篇。
增加交互事件
在第一部分中我们创建了一个简单的 TVML document,里面有几个按钮。这个document看起来是这样的:
<document>
<alertTemplate>
<title>Hello tvOS!</title>
<button>
<text>A Button</text>
</button>
<button>
<text>A Second Button</text>
</button>
</alertTemplate>
</document>
这是一个带按钮的警告(alert)界面,目前这些按钮没有任何作用。这段代码直接硬编码了具体内容,更好的方式是使用代码生成 XML,在 JS 中很容易实现。我们在main.js文件中添加一个新函数,把上面的代码封装成一个更简单的警告界面,它只包含一个 OK 按钮。
function alert(str) {
var alertXMLString = `<?xml version="1.0" encoding="UTF-8" ?>
<document>
<alertTemplate>
<title>Hey Listen!</title>
<description>${str}</description>
<button>
<text>OK</text>
</button>
</alertTemplate>
</document>`
var parser = new DOMParser();
var alertDOMElement = parser.parseFromString(alertXMLString, "application/xml");
navigationDocument.presentModal(alertDOMElement);
}
这里创建了一个alertXMLString字符串,它表示的是包含一个按钮的简单警告界面所对应的 TVML。description节点比较特殊,我们使用 TVJS 的内嵌字符串语法${variable}来插入str的值。
接下来,创建一个新的DOMParser对象,把这个字符串转换成一个实际的 XML DOM 元素。
最后,我们用navigationDocument的presentModal方法展示一个模态框,内容就是上一步的 DOM 元素。navigationDocument是一个全局变量,它永远指向 XML 文档的根节点。
现在,删除onLaunch函数中之前的代码,直接调用刚才创建的函数……
App.onLaunch = function(options) {
alert("Hello!");
}

运行应用,你会看到一个炫酷的"Hello!" tvOS 警告。但是点击 OK 没有任何反应。我们该怎么处理像触摸之类的事件呢?
通常来说,在 JavaScript 和 TVML 的世界中,你需要给 DOM 元素添加一个事件监听器(event listener)。举个例子,我们可以给alert函数添加第二个参数,把 OK 按钮触发select事件时需要调用的函数作为参数传入。下面我们就加入这个名为doneCallback的参数:
alertDOMElement.addEventListener("select", function() { doneCallback }, false);
更新后的完整函数如下:
function alert(str, doneCallback) {
var alertXMLString = `<?xml version="1.0" encoding="UTF-8" ?>
<document>
<alertTemplate>
<title>Hey Listen!</title>
<description>${str}</description>
<button>
<text>OK</text>
</button>
</alertTemplate>
</document>`
var parser = new DOMParser();
var alertDOMElement = parser.parseFromString(alertXMLString, "application/xml");
alertDOMElement.addEventListener("select", doneCallback, false);
navigationDocument.presentModal(alertDOMElement);
}
现在我们可以修改之前的onLaunch函数,添加一个回调函数来显示一个 TVML 页面。在此之前,我们需要再添加一个getDocumentContents函数,它会在页面加载完毕之后调用回调函数。这个回调函数只有一个参数,用来接收 XMLHttpRequest 对象的响应内容。这样我们就可以轻松地加载多种 TVML 文件。
function getDocumentContents(url, loadCallback) {
var templateXHR = new XMLHttpRequest();
templateXHR.responseType = "document";
templateXHR.addEventListener("load", function() { loadCallback(templateXHR) }, false);
templateXHR.open("GET", url, true);
templateXHR.send();
return templateXHR;
}
代码和之前定义的getDocument方法几乎一样,区别是这里是异步操作,而且不会在界面上显示任何内容。
有个这个函数,我们就能执行下面的调用,当 OK 按钮被点击时替换屏幕上的警告内容。
App.onLaunch = function(options) {
alert("Hello!", function() {
var helloDocument = getDocumentContents("http://localhost:8000/hello.tvml", function(xhr) {
navigationDocument.dismissModal();
navigationDocument.pushDocument(xhr.responseXML);
});
});
}
我们使用stackTemplate模板来改写hello.tvml文件,这样界面会更有趣。stackTemplate非常适合用来展示一组包含标题和图片的列表内容。下面是本例用到的内容:
<document>
<stackTemplate>
<banner>
<title>Which Artist Do You Prefer?</title>
</banner>
<collectionList>
<shelf>
<section>
<lockup>
<img src="http://localhost:8000/nina.png" width="256" height="256" />
<title>Nina Simone</title>
</lockup>
<lockup>
<img src="http://localhost:8000/coltrane.png" width="256" height="256" />
<title>John Coltrane</title>
</lockup>
</section>
</shelf>
</collectionList>
</stackTemplate>
</document>
这基本上就是stackTemplate的布局方式,banner是顶部的横幅内容,collectionList包含许多shelf对象,而shelf对象则包含许多section对象,section对象又包含许多lockup对象,最后这个才真正包含我们的图片和标题。在本例中我向目录中添加了一些图片,它们是nina.png和coltrane.png。
