In this lesson we will refactor the feed loading to centralize the logic into home_scene.brs. We will also add a parser for the external JSON and display the feed as a grid of thumbnails.
OK, let's go:
-
In home_scene.brs, add update
init():function init() ? "[home_scene] init" m.category_screen = m.top.findNode("category_screen") m.content_screen = m.top.findNode("content_screen") m.category_screen.observeField("category_selected", "onCategorySelected") m.category_screen.setFocus(true) end function
Notice the new
content_screenreference, we'll get to that in a minute. Right now let's deal with the field observer for the category screen. Update the rest of the file, essentially cutting and pasting the code from Lesson 3 into the home_scene:sub onCategorySelected(obj) ? "onCategorySelected field: ";obj.getField() ? "onCategorySelected data: ";obj.getData() list = m.category_screen.findNode("category_list") ? "onCategorySelected checkedItem: ";list.checkedItem ? "onCategorySelected selected ContentNode: ";list.content.getChild(obj.getData()) item = list.content.getChild(obj.getData()) loadFeed(item.feed_url) end sub sub loadFeed(url) m.feed_task = createObject("roSGNode", "load_feed_task") m.feed_task.observeField("response", "onFeedResponse") m.feed_task.url = url m.feed_task.control = "RUN" end sub
Notice the use of
findNode("category_list"), this syntax looks up the RadioButtonList on the category screen to access the selected ContentNode.
Now all that is missing is theonFeedResponsehandler for the feed data. Add this:sub onFeedResponse(obj) response = obj.getData() data = parseJSON(response) if data <> Invalid and data.items <> invalid m.category_screen.visible = false m.content_screen.visible = true m.content_screen.feed_data = data else ? "FEED RESPONSE IS EMPTY!" end if end sub
Pay attention to the
visiblevalue on the screens. This logic hides the category list, and shows the content grid. Also notice the use ofparseJSON(), this is a global method that will convert a JSON string into an Associative Array. -
Go back into
category_screen.brsand delete all the code so you are left with just theinit()function:function init() m.category_list=m.top.findNode("category_list") m.category_list.setFocus(true) end function
Then open
category_screen.xmland add a field the interface:<interface> <field id="category_selected" type="assocarray" alias="category_list.itemSelected" /> </interface>
The
aliasattribute allows the component to expose a child's field. This will allow the home_scene to observe the list changes.
Here are the docs: https://sdkdocs.roku.com/display/sdkdoc/interface -
The next steps will create the
content_screenreferenced above. Openhome_scene.xmland update it to include the new child:<?xml version="1.0" encoding="utf-8" ?> <component name="home_scene" extends="Scene" initialFocus="category_screen"> <script type="text/brightscript" uri="pkg:/components/home_scene.brs" /> <children> <category_screen id="category_screen" visible="true" translation="[0,0]" /> <content_screen id="content_screen" visible="false" translation="[0,0]" /> </children> </component>
In the
components/screensdirectory, create a filecontent_screen.xmland add the following:<?xml version="1.0" encoding="utf-8" ?> <component name="content_screen" extends="Group" initialFocus="content_grid" > <script type="text/brightscript" uri="pkg:/components/screens/content_screen.brs" /> <interface> <field id="feed_data" type="assocarray" onChange="onFeedChanged" /> </interface> <children> <Label translation="[75, 75]" id="header" color="0xFFFFFF" font="font:LargeBoldSystemFont" text=""/> <PosterGrid id="content_grid" translation="[100,175]" basePosterSize="[500,280]" itemSpacing="[110,110]" caption1NumLines="1" caption2NumLines="1" numColumns="3" numRows="4" /> </children> </component>
Create the companion file
content_screen.brsand add the following:sub init() m.content_grid = m.top.FindNode("content_grid") m.header = m.top.FindNode("header") end sub sub onFeedChanged(obj) feed = obj.getData() m.header.text = feed.title postercontent = createObject("roSGNode","ContentNode") for i=0 to 5 step 1 node = createObject("roSGNode","ContentNode") node.HDGRIDPOSTERURL = "http://10.0.0.42:8888/roku_lessons/images/thumbnail-comedy1.jpg" node.SHORTDESCRIPTIONLINE1 = "ITEM No. "+ i.toStr() node.SHORTDESCRIPTIONLINE2 = "This is a description." postercontent.appendChild(node) end for showpostergrid(postercontent) end sub sub showpostergrid(content) m.content_grid.content=content m.content_grid.visible=true m.content_grid.setFocus(true) end sub
This code creates some placeholder data to verify the PosterGrid is rendering correctly. The pattern is very similar to the data provided to the category list. There is a single parent node, then a
forloop creates some dummy nodes to populate it.
Run the app and select a category, a grid of content should appear with the title from the JSON at the top of the screen:

-
Finally, edit the
onFeedChanged()function to use the data provided by the feed. Some of these properties will be used later when video playback is added to the app.sub onFeedChanged(obj) feed = obj.getData() m.header.text = feed.title postercontent = createObject("roSGNode","ContentNode") for each item in feed.items node = createObject("roSGNode","ContentNode") node.streamformat = item.streamformat node.title = item.title node.url = item.url node.description = item.description node.HDGRIDPOSTERURL = item.thumbnail node.SHORTDESCRIPTIONLINE1 = item.title node.SHORTDESCRIPTIONLINE2 = item.description postercontent.appendChild(node) end for showpostergrid(postercontent) end sub
Notice how almost all the application logic is now centralized in the home scene. You could think of the home scene as the "controller" in a Model View Controller architecture. As the course progresses, most of the additional functionality will be added here.
That's it for Lesson 4.
