Menü für die Smart TV App
In unserer Smart-TV App werden wir heute das Menü erstellen. Das Besondere daran: Es wird über die Tasten der Fernbedienung gesteuert. Zunächst einmal gibt es zwei neue JavaScript-Dateien. Eine steht für das Menü, die andere für einen einzelnen Eintrag des Menüs.
Neben diesen Dateien habe ich auch Platzhalter für das Logo und das Menü eingebaut – und auch ein wenig aufgeräumt:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Hello World</title> <script type="text/javascript" language="javascript" src="$MANAGER_WIDGET/Common/API/Widget.js"></script> <script type="text/javascript" language="javascript" src="$MANAGER_WIDGET/Common/API/TVKeyValue.js"></script> <script language="javascript" type="text/javascript" src="app/javascript/Main.js"></script> <script language="javascript" type="text/javascript" src="app/javascript/Menu.js"></script> <script language="javascript" type="text/javascript" src="app/javascript/MenuEntry.js"></script> <link rel="stylesheet" href="app/stylesheets/Main.css" type="text/css"> </head> <body onload="Main.onLoad();" onunload="Main.onUnload();"> <div id="sidebar"> <div id="logo"> </div> <ul id="menu"> </ul> </div> </body> </html>
Das Menü ist wie auf vielen Seiten üblich mit einem ul-Konstrukt aufgebaut. Jeder Menüpunkt wird dann später als li in der Liste auftauchen. Das Befüllen der Einträge wird über JavaScript erledigt, weshalb die index.html auch relativ leer ist. Die Dateipfade werde ich wie im Beispiel fortführen, also großgeschrieben.
Auch in der Main.css hat sich etwas getan. Hier werden nun Sidebar, Logo und Menü positioniert:
* { padding: 0; margin: 0; border: 0; } /* Layout */ body { background-color:#535353; color:#fff; } #sidebar{ position:absolute; top:0; bottom:0; left:0; width:25%; border-right:1px solid #fff; padding:15px; font-size:1.3em; line-height:1.5em; } #logo{ height:150px; } #menu{ list-style-type:none; } #menu li{ padding-left:10px; } #menu li.active{ background-color:#b00000; }
Die Sidebar wird komplett absolute positioniert. Da wir keine Scrollbars haben möchten, ist dies ohne Probleme möglich. Auch habe ich die active-Klasse erstellt, mit der ich später den aktuell ausgewählten Menüeintrag kennzeichne.
Kommen wir zur MenuEntry.js, die in den app/javascript Ordner kommt. Diese steht für einen einzelnen Menüpunkt:
function MenuEntry(){ this.control = false; //Control this.title = undefined; //Bezeichnung this.active = false; //Menüpunkt aktiv? this.OnActivate = false; //Callback /** * Liefert das <li>-Element zurück */ this.GetControl = function(){ if(!this.control){ //Menüpunkt noch nicht erzeugt, also nachholen this.control = document.createElement("li"); this.control.innerText = this.title; if(this.active){ //Menüpunkt bereits aktiv, also active-Klasse hinzufügen this.control.className = "active"; } } //Control zurückgeben return this.control; }; /** * Wird aufgerufen, um den Menüpunkt auszuwählen */ this.Activate = function(){ this.active = true; //Klasse active hinzufügen, wenn das Control //schon erstellt wurde if(this.control){ this.control.className = "active"; } //Callback aufrufen, wenn vorhanden. if(this.OnActivate){ this.OnActivate(); } }; /** * Wird aufgerufen, um den Menüpunkt abzuwählen */ this.Deactivate = function(){ this.active = false; //Klasse wieder zurücksetzen if(this.control){ this.control.className = ""; } }; }
In der Funktion GetControl wird das li-Element erzeugt. Dabei wird die Bezeichnung, die in der Eigenschaft title gespeichert ist, sowie die Klasse active, wenn der Menüpunkt ausgewählt ist, hinzugefügt. Die Funktionen Activate und Deactivate werden aufgerufen, wenn der Menüpunkt aktiv oder inaktiv gestellt werden soll. Wird der Menüpunkt aktiviert, wird auch der Callback OnActivate ausgeführt. Darin kann man später z.B. die Videos der App laden.
Ich habe es übrigens so gemacht, dass Eigenschaften klein und Funktionen groß geschrieben werden. So kann man das ein wenig unterscheiden.
var Menu = {}; /** * Funktion wird nach dem laden der Seite aufgerufen */ Menu.Load = function(){ this.entries = new Array(); /** * Funktion zum Hinzfügen eines neuen Menüpunkts */ this.AddEntry = function(entry){ //Damit wir ihn später wiederfinden this.entries[this.entries.length] = entry; //Menüpunkt anzeigen this.GetControl().appendChild(entry.GetControl()); }; /** * Liefert das HTML-Element (ul) zurück */ this.GetControl = function(){ return document.getElementById("menu"); }; /** * Liefert die aktuell ausgewählte Position zurück * Auf 0 basierend. */ this.GetPosition = function(){ for(var i = 0; i < this.entries.length; i++){ if(this.entries[i].active){ return i; } } }; /** * Wählt einen Menüpunkt weiter unten aus */ this.MoveDown = function(){ //Position auslesen var pos = this.GetPosition(); //Aktuellen Menüpunkt deaktivieren this.entries[pos].Deactivate(); if(pos < this.entries.length-1){ //Nächsten Punkt auswählen this.entries[pos+1].Activate(); } else{ //Liste bereits durchlaufen, //also zum ersten Punkt zurückkehren this.entries[0].Activate(); } }; /** * Wählt einen Menüpunkt weiter oben aus */ this.MoveUp = function(){ //Position auslesen var pos = this.GetPosition(); //Aktuellen Menüpunkt deaktivieren this.entries[pos].Deactivate(); if(pos > 0){ //Vorherigen Menüpunkt aktivieren this.entries[pos-1].Activate(); } else{ //Letzten Punkt in der Liste aktivieren this.entries[this.entries.length - 1].Activate(); } }; /** * Fügt einen Dummy-Link hinzu, * um die Fernbedienung-Knöpfe abzufangen */ this.AddFocus = function(){ //Link erstellen var a = document.createElement("a"); a.href = "javascript:void(0);"; //Keine Aktion beim Klick a.id = "menu_focus"; //Wird aufgerufen, wenn eine Taste gedrückt wurde a.onkeyup = function(){ var keyCode = event.keyCode; switch(keyCode) { case tvKey.KEY_DOWN: //Pfeil nach unten, //also Menüpunkt weiter unten auswählen Menu.MoveDown(); break; case tvKey.KEY_UP: //Pfeil nach oben, //also Menüpunkt weiter oben auswählen Menu.MoveUp(); break; } }; //Dummy-Link hinzufügen document.body.appendChild(a); //und fokussieren... a.focus(); }; //Neuen Menüpunkt erstellen var news = new MenuEntry(); news.title = "Neuvorstellungen"; news.OnActivate = function(){ //Wird aufgerufen, wenn der Menüpunkt //aktiv wird alert(this.title); }; this.AddEntry(news); //Weitere Menüpunkte var category1 = new MenuEntry(); category1.title = "Kategorie 1"; this.AddEntry(category1); var category2 = new MenuEntry(); category2.title = "Kategorie 2"; this.AddEntry(category2); //Als Default werden die Neuvorstellungen gewählt news.Activate(); //Versteckten Fokus hinzufügen this.AddFocus(); };
Das ist jetzt die Datei Menu.js. Sie steuert das Menü selbst. Wie im Beispiel am Anfang, nutzen wir hier auch einen Dummy-Link, der die Tastaturschläge abfängt. Mit den Funktionen MoveUp und MoveDown wird der Fokus dann entweder nach oben oder nach unten verschoben. Ist die Liste durchlaufen, wird am anderen Ende weitergemacht.
Wenn der Menüpunkt „Neuvorstellungen“ ausgewählt wird, wird bereits das Callback ausgeführt. Dabei erscheint der Titel „Neuvorstellungen“ in der Konsole. Später zeigen wir hier die ausgewählten Videos an.
Du arbeitest in einer Agentur oder als Freelancer?
Dann wirf doch mal einen Blick auf unsere Software FeatValue.
Kommentare
Jay schrieb am 14.11.2012:
Hallo, ich kenne mich noch nicht so gut mit App-Programmierung aus und habe den Code von dieser Website als Test übernommen. Jedoch zeigt der Emulator mir beim Ausführen des Codes statt dem Menü nur den Hintergrund und die vertikale Linie an. Die Menüpunkte werden also nicht angezeigt. Woran kann das liegen?
Stefan Wienströer schrieb am 15.11.2012:
Es sieht so aus, als würde das JavaScript nicht geladen werden. Überprüf doch nochmal, ob die Pfade bei dir passen.
Jay schrieb am 16.11.2012:
Hallo Stefan, meinst du die Pfade in der index.html? Die scheinen zu stimmen... Ich habe generell den Code von dieser Website benutzt und in /app/javascript die Dateien Menu.js und MenuEntry.js erzeugt, aber dennoch zeigt der Emulator keine Menüeinträge an. Gruß, Jay
Stefan Wienströer schrieb am 16.11.2012:
Kannst du mir das Ganze mal Zippen und per Mail schicken? Adresse steht hier: http://stevieswebsite.de/impressum.php
Hendrik schrieb am 08.01.2013:
Hey. cooles Tutorial. Bei mir klappt es momentan nicht, da du ja den Link in der Index.html weggelassen hast und man da ja in der Main.js drauf zugreift. Wie hast du die Main.js denn angepasst, so dass das Menu erscheint?
Stefan Wienströer schrieb am 08.01.2013:
Hallo Hendrik, ich weiß leider grade nicht genau, was du mit Link zur index.html meinst. Diese wird beim Starten des Eumlators aufgerufen. Aber ich befürchte da fehlt wirklich der Teil der Main.js. Diese sieht zu dem Zeitpunkt so aus: var widgetAPI = new Common.API.Widget(); var tvKey = new Common.API.TVKeyValue(); var Main = {}; Main.onLoad = function() { widgetAPI.sendReadyEvent(); Menu.Load(); }; Main.onUnload = function() { };
Nicole schrieb am 18.08.2013:
Hallo, erst einmal super Tutorial. Ich habe es mir angeguckt und komme so auch damit zurecht, jetzt möchte ich gern noch zusätzlich eine waagerechte Navigation, also das dann Kategorie 3 in der waagerechten weitergeführt wird. Kannst du mir erklären wie ich dieses machen kann? MfG
Stefan Wienströer schrieb am 19.08.2013:
Hallo Nicole, im Endeffekt wird das in einen der nächsten Artikel erklärt: http://blog.stevieswebsite.de/2012/12/videos-auswaehlen-im-smart-tv/ Hier wählst du in der zweiten Seite ein Video aus. Statt den Video kannst du durch verschiedene Größen auch einfach eine weitere Navigation einfügen
Nicole schrieb am 19.08.2013:
Erst einmal vielen Dank für die schnelle Antwort. Sorry, da muss ich mich etwas schlecht ausgedrückt haben, ich probiere es besser zu erklären. In der Index ist ja folgendes enthalten: Da möchte ich folgendes erweitern: Das ist so gemeint, dass ich dann im Prinzip eine zweite Navigation machen möchte, die waagerecht verläuft, zu der anderen die senkrecht verläuft, dass dann wenn ich auf Kategorie 2 bin und nach unten drücke this.entries[0].Activate(); in die Kategorie 3, komme. Ich hoffe du verstehst mich jetzt besser? Und hoffe du hast dafür vielleicht eine Lösung für mich? Vielleicht auch so gut erklärt, wie oben das Beispiel.:-) MfG
Stefan Wienströer schrieb am 20.08.2013:
Dachte vorher hab ichs verstanden, jetzt bin ich mir nicht mehr sicher^^ Kannst du mir das vielleicht einfach mal aufmalen und per Mail schicken? (info @ stevieswebsite . de) Ein komplett neuer Artikel passt glaube ich von der Relevanz da nicht rein, kann dir dann aber gerne per Mail weiterhelfen.
Zerberus schrieb am 04.09.2013:
Hallo Stefan, auf der Suche nach der Lösung meines Problemes (?) habe ich Dein Tutorial gefunden. Es geht mir um folgendes: auf meinem Samsung-TV spiele ich Musik-Videos im MP4-Format ab. Leider besitzt der interne Media-Player keine Shuffle- bzw. Zufalls-Wiedergabe (auch SKIP wäre nicht schlecht). Ist eine App denkbar, welche folgendes leistet: Ich wähle von der angeschlossenen externen HDD ein Verzeichnis aus, die darin enthaltenen Video-Dateien werden in zufälliger Wiedergabe abgespielt. Ich habe zwar schon etwas Erfahrung in VB-Programmierung, in Java aber bisher nicht. Viele Grüße Zerberus
Stefan Wienströer schrieb am 05.09.2013:
Hallo Zerberus, ich glaube du suchst AllShare: http://www.samsungdforum.com/Guide/d15/index.html (AllShare) Gruß Stefan
Zerberus schrieb am 06.09.2013:
Hallo Stefan, AllShare dient nur dazu, z.B. den PC im Netzwerk als Datenquelle für den TV zu nutzen. Mir geht es aber darum, unabhängig von der Quelle (also z.B. von einer am TV angeschlossenen USB-HDD) die mp4-Dateien in zufälliger Reihenfolge abzuspielen. Bei der mp3-Musik-Wiedergabe bietet das der interne Samsung-Mediaplayer an. Bei der Video-Wiedergabe leider nicht. Gruß Zerberus
Holger Koch schrieb am 29.09.2015:
Hallo Stefan, vielen Dank für das Tutorial. Es bietet wirklich einen guten Einstieg und Basis in die Entwicklung für Samsungs Smart TV. Bitte aktualisiere doch bei Gelegenheit noch die Main.js. Ich habe mich zwei Tage mit dem Fehler TypeError: 'null' is not an object (evaluating 'document.getElementById("anchor").focus') herumgeschlagen, bis ich in den Kommentaren die Lösung gefunden hatte. Möglicherweise geht es noch anderen so und werden dann abgeschreckt. Mit freundlichen Grüßen Holger Koch
Über uns

Wir entwickeln Webanwendungen mit viel Leidenschaft. Unser Wissen geben wir dabei gerne weiter. Mehr über a coding project