我已经开发了一个TVML应用程序。我需要的代码很少更改。请找到代码的github链接。
我正在开发iOS电视应用程序,该应用程序策划模板以显示远程服务器的视频。还需要实现搜索和中途停留功能。
https://github.com/tvmlbegnr/tvmlapp
视频阵列显示视频列表,选择一个视频,其他视频应在覆盖层中显示。搜索功能应起作用。
谢谢。
请在帖子中包含代码。不要使用链接或引用其他网站,因为这可能会改变并可能使此讨论无关紧要。
您可能想从Apple下载此示例代码。
- 创建一个搜索控制器类
请参阅样品控制器
class SearchController extends DocumentController {
setupDocument(document) {
super.setupDocument(document);
// Obtain references to some useful elements in the searchTemplate
const searchTemplateElem = document.getElementsByTagName('searchTemplate').item(0);
const searchFieldElem = document.getElementsByTagName('searchField').item(0);
const separatorElem = document.getElementsByTagName('separator').item(0);
const messageElem = document.getElementById("message");
const suggestionsElem = document.getElementById("suggestions");
const defaultResultsElem = document.getElementById("defaultResults");
const resultsModeElem = document.getElementById("resultsMode");
const resultsListElem = document.getElementById("resultsList");
const resultsShelfContainerElem = document.getElementById("resultsShelfContainer");
const resultsGridContainerElem = document.getElementById("resultsGridContainer");
const resultsSectionElem = document.getElementById("resultsSection");
// Clean up the document for initial presentation
let resultsContainerElem = resultsShelfContainerElem;
resultsListElem.removeChild(resultsGridContainerElem);
toggleDefaultResults(true);
// Define some search-related constants
const searchBaseURL = "https://itunes.apple.com/search?";
const searchResultsLimit = 25;
const searchMediaType = "movie";
const searchMediaArtworkWidth = 250;
const searchMediaArtworkAspectRatio = 1.5;
const searchMediaTitleFunc = function(item) {
return item.trackCensoredName || item.trackName ||
item.collectionCensoredName || item.collectionName;
};
const searchMediaItemIDFunc = function(item) {
return item.trackId || item.collectionId;
};
const searchMediaArtworkURLFunc = function(item) {
let result = item.artworkUrl60;
if (typeof result === "string") {
// WORKAROUND: Obtain high-resolution artwork
result = result.replace("/60x60", "/600x600");
}
return result;
};
// Retain the XHR between searches so it can be cancelled mid-flight
let searchRequest;
// Retain the query between searches to determine changes
let searchTextCache;
// Retain the result items to handle selection events
const searchResultsCache = [];
const searchKeyboard = searchFieldElem.getFeature("Keyboard");
// Register an event handler for search input changes
searchKeyboard.onTextChange = performSearchRequest;
// Register an event handler for search result mode selection
resultsModeElem.addEventListener("highlight", function(event) {
const selectedElement = event.target;
const selectedMode = selectedElement.getAttribute("value");
setResultsMode(selectedMode);
});
// Register an event handler for search suggestions selection
suggestionsElem.addEventListener("select", function(event) {
const selectedElement = event.target;
const searchValue = selectedElement.getAttribute("value");
searchKeyboard.text = searchValue;
performSearchRequest();
});
// Register an event handler for search result selection
resultsSectionElem.addEventListener("select", function(event) {
const selectedElement = event.target;
const resultIndex = selectedElement.getAttribute("resultIndex");
const resultItem = searchResultsCache[resultIndex];
handleSelectionForItem(resultItem);
});
/*
* Show or hide the message in the search body.
* Sets the content of the message if it is to be shown.
*/
function toggleSearchMessage(bool, message) {
if (bool) {
// Set the message text
if (message) {
messageElem.textContent = message;
}
// Show the message if it's hidden
if (!messageElem.parentNode) {
searchTemplateElem.appendChild(messageElem);
}
toggleModeButtons(false);
} else {
// Hide the message if it's visible
if (messageElem.parentNode) {
searchTemplateElem.removeChild(messageElem);
}
toggleModeButtons(true);
}
}
function toggleSearchSuggestions(bool) {
if (bool) {
// Show the suggestions if they're hidden
if (!suggestionsElem.parentNode) {
searchTemplateElem.appendChild(suggestionsElem);
}
toggleSearchMessage(false);
toggleModeButtons(false);
} else {
// Hide the suggestions if they're visible
if (suggestionsElem.parentNode) {
searchTemplateElem.removeChild(suggestionsElem);
}
toggleModeButtons(true);
}
}
function toggleDefaultResults(bool) {
if (bool) {
// Swap the default results in for the container
if (resultsContainerElem.parentNode) {
resultsListElem.removeChild(resultsContainerElem);
resultsListElem.appendChild(defaultResultsElem);
}
toggleSearchMessage(false);
toggleSearchSuggestions(false);
toggleModeButtons(false);
} else {
// Swap the default results out and the container in
if (!resultsContainerElem.parentNode) {
resultsListElem.removeChild(defaultResultsElem);
resultsListElem.appendChild(resultsContainerElem);
}
toggleModeButtons(true);
}
}
function toggleModeButtons(bool) {
if (bool) {
if (!separatorElem.parentNode) {
searchTemplateElem.appendChild(separatorElem);
}
} else {
if (separatorElem.parentNode) {
searchTemplateElem.removeChild(separatorElem);
}
}
}
function setResultsMode(mode) {
// Remove existing results container
while (resultsListElem.firstChild) {
resultsListElem.removeChild(resultsListElem.firstChild);
}
// Determine the new results container element
if (mode === "shelf") {
resultsContainerElem = resultsShelfContainerElem;
}
if (mode === "grid") {
resultsContainerElem = resultsGridContainerElem;
}
// Show the new results container in the collectionList
resultsListElem.appendChild(resultsContainerElem);
resultsContainerElem.appendChild(resultsSectionElem);
}
function performSearchRequest() {
// Strip leading, trailing, and multiple whitespaces from the query
const searchText = searchKeyboard.text.trim().replace(/s+/g, " ");
// Do nothing if the query hasn't meaningfully changed
if (searchTextCache && searchText === searchTextCache) {
return;
}
// Retain this query for the next time the input changes
searchTextCache = searchText;
// If there's already a search in-progress, cancel it.
if (searchRequest && searchRequest.readyState !== XMLHttpRequest.DONE) {
searchRequest.abort();
}
// Show the initial message and stop if there's no search query
if (searchText.length === 0) {
toggleDefaultResults(true);
return;
}
// Build the URL for the search query
const searchParams = [
`media=${searchMediaType}`,
`limit=${searchResultsLimit}`,
`term=${encodeURIComponent(searchText)}`
].join("&");
const searchURL = searchBaseURL + searchParams;
// Perform the search request
searchRequest = new XMLHttpRequest();
searchRequest.open("GET", searchURL);
searchRequest.responseType = "json";
searchRequest.onload = showSearchResponse;
searchRequest.onerror = showSearchError;
searchRequest.send();
searchFieldElem.setAttribute("showSpinner", true);
}
/*
* Show a generic error message in the search body
*/
function showSearchError() {
toggleSearchMessage(true, "An error occurred during your search.");
searchFieldElem.setAttribute("showSpinner", false);
}
/*
* Parse the XHR response and show the results or a message
*/
function showSearchResponse() {
// Prepare the document for new search results
toggleDefaultResults(false);
toggleSearchSuggestions(false);
clearSearchResults();
searchFieldElem.setAttribute("showSpinner", false);
// Show the results (or lack thereof)
const searchResponse = searchRequest.response;
const searchResults = searchResponse.results;
if (searchResults.length > 0) {
appendSearchResults(searchResults);
toggleSearchMessage(false);
} else {
if (searchTextCache.length > 3) {
toggleSearchMessage(true, `No results for ${searchTextCache}.`);
} else {
toggleSearchSuggestions(true);
}
}
}
/*
* Empty the results cache and remove all results lockup elements.
*/
function clearSearchResults() {
searchResultsCache.length = 0;
// Remove all existing search results
while (resultsSectionElem.firstChild) {
resultsSectionElem.removeChild(resultsSectionElem.firstChild);
}
}
/*
* Create lockup elements for the search results and cache
* the data to be referenced by the selection handler.
*/
function appendSearchResults(results) {
const startIndex = searchResultsCache.length;
// Create new lockups for the results
results.forEach(function(item, index) {
index += startIndex;
// Create item lockup element
const lockupElem = document.createElement("lockup");
// Set the result array index on the lockup
lockupElem.setAttribute("resultIndex", index);
// Populate the lockup element details
populateLockupWithItem(lockupElem, item);
// Add the lockup to the results collection
resultsSectionElem.appendChild(lockupElem);
// Add the item to the search results cache
searchResultsCache.push(item);
});
}
/*
* Inserts elements containing data from a search result into the
* empty lockup element created to display the result.
* Shows an image, title, and subtitle in the lockup.
*/
function populateLockupWithItem(lockupElem, item) {
// Determine the lockup image attributes
const titleText = searchMediaTitleFunc(item);
const itemID = searchMediaItemIDFunc(item);
const imgURL = searchMediaArtworkURLFunc(item);
const imgWidth = searchMediaArtworkWidth;
const imgHeight = imgWidth * searchMediaArtworkAspectRatio;
// Create the child nodes of the lockup element
const imgElem = document.createElement("img");
const titleElem = document.createElement("title");
// Set the lockup image attributes
imgElem.setAttribute("src", imgURL);
imgElem.setAttribute("width", imgWidth);
imgElem.setAttribute("height", imgHeight);
// Set the lockup element text from the item
titleElem.setAttribute("class", "showTextOnHighlight");
titleElem.textContent = titleText;
// Put the child nodes into the lockup
lockupElem.setAttribute("itemID", itemID);
lockupElem.appendChild(imgElem);
lockupElem.appendChild(titleElem);
}
/*
* Called when a search result is selected, passing in the
* JSON Object that was returned by the API for the result.
*/
function handleSelectionForItem(item) {
console.log("handleSelectionForItem: " + JSON.stringify(item));
// Do something more interesting than logging
}
}
}
registerAttributeName('searchDocumentURL', SearchController);