Extending our AIR Feed Reader application with Flash Builder 4
Introduction
Previously we created an RSS Feed reading application in Adobe AIR using Flash Builder 4. Our previous application had a simple UI which allowed us to enter a URL for the feed, and has two panes one below another. One to list the items in the feed, and the other to display the summary of the selected item. Note that this tutorial assumes that you have completed the previous one in the series.You can check out the previous article at http://www.thinkdigit.com/d/fb4_tut1_4.
Our previous application could only handle a single feed at a time, which is a rather huge limitation for a feed reading application. In this article we will develop our application further by adding support for reading multiple feeds. We will make a Feed browser of sorts which will display a “tab bar” for switching between active feeds. Each “tab” will display its own list of feed items, and we will be able to select any item read the summary.
Understanding the changes to our application
So how do we go about this exactly? Let us first take into account the visual additions, in the form on controls / components. We will need:
-
A tab bar
We will simply use a button bar for this. A ButtonBar component makes it easy to add multiple buttons in a row. Each button will have a label corresponding to the title of the feed.
-
A button to add a new feed
Since we have to handle multiple feeds now, we need to have a button for adding a new feed. We will re-purpose the “Load” button of our previous application for this.
-
A close button
We should have some way to remove the currently active tab if we want. This could be a simple button labeled “X” or if you wish to make it look better you can create an icon for it.
That’s about it; this will cover our needs from this application.
Now about the changes inside the application. Since we are now going to handle multiple feeds at the same time, we will need to have an array holding data for all the feeds, so it is ready when we click on it.
To wire everything so that it works, with minimal coding we will do the following:
-
We will bind the array of feeds to the ButtonBar. This will automatically ensure that the feeds and tabs are in sync.
-
We will bind the feed data for active tab to the list showing the data items. This way the data will automatically change as we change the current tab.
The HTML component is already bound to this list, so that bit will work as always.
What we need to do is to write the code that will add tabs to this array of tabs and load the corresponding data. We also need code for closing a tab and removing that feed from the array. This rest will work by itself!
The UI
Our UI is not changing drastically. Our load button will now transform to a “Add Feed” button labeled, and we will add a tab strip with a close button on the next line. Our previous code was simply a horizontal group with a text input for the URL and a load button. Right after this we will now add the following:
<s:HGroup width="100%"> <s:ButtonBar id="feedTabs" requireSelection="true" width="100%" /> <s:Button label="X" /> </s:HGroup>
This is another horizontal group with three components. The first is a ButtonBar which will display our tabs and let us switch between them. We have set its width to 100% so it takes us as much space as is available and pushes the close button to the edge. The second is the button to close the currently active tab, labeled simply “X”. The requireSelection=”true” bit in the code is to ensure that at least one tab is selected at any given time (if there is at least one tab of course). This is all we need to add, however the old “Load” button may be renamed to “Add Feed” or simply ” ” if required.
The Code
First of all we will define a new data type for the data associated with out feeds. For each feed tab, we will need to store a title and the feed data at least. Since this is a simple example, we will not manage refreshing the feeds every few minutes, otherwise it would have been wise to store the feed data as well.
[RELATED_ARTICLE]For this we will define a new ActionScript 3 class. This is simple to do with Flash Builder 4; simply right-click on your project, and under the “New” menu select “ActionScript Class”. This will pop up a new dialog box wherein you can enter details of your new class. Here is a little about some of the relevant parameters in the dialog:
-
The first option is labelled “Package”. When you create a new libraries of code, it is a good idea to organize them so that they can be reused later on without problems. If you are creating a new Class for retrieving stocks information from Yahoo called “YahooStocks” for a project called “Stock Manager” you might want to have a package such as com.stockmanager. Although it is recommended that you always place your classes inside a package, we will do without one here since it is a simple example.
-
The second option is for a name for your class. We are giving our class a name of “FeedTab”. The convention is to have a class name where each word begins with a capital such as “MultiTouchManager” or “ArrayCollection”.
-
Superclass is used if you are extending an already existing class. Those familiar with object-oriented programming will understand what this means. Those unfamiliar, should look up inheritance in object-oriented programming. You could use this feature for example to create your custom version of the “Video” class called “SubtitledVideo” to provide support for subtitles.
-
Interfaces are a way of making different classes compatible with each other. For example, while the ArrayCollection, and XMLListCollection classes are very different —one stores data as an unstructured array, and the other as structured XML— both can be used as a source for the list. This is because both implement the IList interface. For them to be part of a list they both need to provide a few features, such as moving forward and backward in a list, getting the length of the list etc. If you create your own custom class which implements IList, it too will be usable a source for the list.
This is the entirety of our “FeedTab” class:
package{ import mx.collections.XMLListCollection; public class FeedTab { public var title:String public var data:XMLListCollection; public function FeedTab(title:String, data:XMLListCollection){ this.title = title; this.data = data; } }}
All it is doing is storing the data needed by our tab, which is: a title for the feed, and the feed data itself. The feed data itself contains a title, however this makes things a little bit simpler. The FeedTab function which serves as a constructor for this Class takes two the two pieces of data this class needs and stores them in the public variables which make up this class.
Finally we will modify our main code. First of all remove the variable feedData, we will no longer need it as we handle multiple feeds. Secondly, we will add a new variable, and array to store all our feeds’ data.
[Bindable]private var feeds:ArrayCollection = new ArrayCollection();
Now in our onFeedLoaded function, we will need to add this feed to our list of feeds instead.
private function onFeedLoaded(event:Event):void { var rssFeed:XML = XML((event.target as URLLoader).data); var feedData:XMLListCollection = new XMLListCollection(rssFeed.channel.item); var feedTab:FeedTab = new FeedTab(rssFeed.channel.titles, feedData); feeds.addItem(feedTab); feedTabs.selectedItem = feed;}
The first line remains unchanged, and in the second line we are temporarily storing the XMLListCollection made up of all the items in the RSS feed, in a variable called feedData. Then we go on to create a new FeedTab item (which is the class we just created), by giving it the title of the feed (which can be found under channel.item of the RSS feed), and the feedData. We then push this FeedTab item into our list of feeds, and finally make the newly added FeedTab the new selected tab.
We should now go ahead and add “feeds” as the dataProvider for the ButtonBar, and set the ButtonBar’s labelField attribute to “title” since that is the name of the field where we are storing the title of the feed which is to be used as a label for the button.
Finally, we need to handle removing tabs. This is easily done by adding a click handler for the close button with the following simple line of code:
feeds.removeItemAt(feedTabs.selectedIndex);
Here feedTabs is the id of the ButtonBar which lists the feed items. We are removing the item at the selectedIndex of the ButtonBar based tab bar, from the feeds array. This is only working as expected as the order of items in the feeds array and the tab bar is the same.
Believe it or not, at this point we have a functional multi-tab feed browser! Go ahead and test your code.
The full code for our application:
<?xml version = "1.0" encoding = "utf-8"?><s:WindowedApplication xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" xmlns:mx = "library://ns.adobe.com/flex/mx" width = "800" height = "600"> <s:layout> <s:VerticalLayout/> </s:layout> <fx:Script> <![CDATA[ import mx.collections.ArrayCollection; import mx.collections.IList; import mx.collections.XMLListCollection; import spark.events.IndexChangeEvent; [Bindable] private var feeds:ArrayCollection = new ArrayCollection(); protected function loadFeed_clickHandler(event:MouseEvent):void { var ul:URLLoader = new URLLoader(); ul.addEventListener(Event.COMPLETE, onFeedLoaded); ul.load(new URLRequest(feedUrl.text)); } private function onFeedLoaded(event:Event):void { var rssFeed:XML = XML((event.target as URLLoader).data); var feedData:XMLListCollection = new XMLListCollection(rssFeed.channel.item); var feed:FeedTab = new FeedTab(rssFeed.channel.title, feedData); feeds.addItem(feed); feedTabs.selectedItem = feed; } protected function feedItems_changeHandler(event:IndexChangeEvent):void { article.htmlText = feedItems.selectedItem.description; } protected function button1_clickHandler(event:MouseEvent):void { feeds.removeItemAt(feedTabs.selectedIndex); } ]]> </fx:Script> <s:HGroup width = "100%"> <s:TextInput id = "feedUrl" width = "100%"/> <s:Button id = "loadFeed" label = "Add Feed" click = "loadFeed_clickHandler(event)"/> </s:HGroup> <s:HGroup width = "100%"> <s:ButtonBar id = "feedTabs" dataProvider = "{feeds}" labelField = "title" requireSelection = "true" width = "100%"/> <s:Button label = "X" click = "button1_clickHandler(event)"/> </s:HGroup> <s:List id = "feedItems" dataProvider = "{feedTabs.selectedItem.data}" labelField = "title" width = "100%" height = "100%" change = "feedItems_changeHandler(event)"/> <mx:HTML id = "article" width = "100%" height = "100%"/></s:WindowedApplication>
You can download a free trial of Adobe Flash Builder 4 from the Adobe website.