Languages

RSS reader in actionscript AS3

This is my first attempt in litterally translating one of my article from french to english, so dear english readers , please don't be too harsh on my mistakes , i'll do my best though as i'm an english-language lover for many years now...

Now , what about that RSS reader in flash AS3 ? I'm not talking about Flex as i've never used it but that was an article about Flex that help me find out how to make it work .
Here's how it look (not so good graphically , i'm pretty bad at design) .

When i was first asked to do a RSS reader in flash (by my company) , i googled a lot and i fell upon that very interesting article by Mike chambers that propose a Flex/Flash solution by using classes from Adobe. I basically used these classes on the reader above, mostly to parse the feed accordingly to the feed .
Those classes are really complete and allow to parse three different types of feed (RSS1 , RSS2 and Atom) .
To have it working in AS3 tough, you'll need to do a number of changes in the classes by Adobe.
First of all, i've changed Mike Chambers code to fit my needs and so put that all in a basic AS3 class. Create your 'RssReader" fla in a repository. Associate it with the RssReader.as (that is the class that it will use) that you can download at the end of this article .
Then you have to download the adobe classes :

  1. download xmlsyndication here
  2. download corelib here

From that loadful of packages, you only need what is in the src/com folder that you'll copy in your own 'com' repository ..
Try to respect the nomenclature when it comes to packages (that is root/com/origin/source ) , it is much easier for everyone if everyone store libs the same way.

Once you've copied the src content from the download package , you can start to compile to see where the problems are .
Now, as my Flash CS3 is in french, you might not get the exact same message for the error codes , but i've translated a bit of those .
Err : 1172: definition mx.formatters:DateBase can't be found .
With the project explorer of Flash, open up the class DateUtils from the lib com/adobe/utils and change import mx.formatters.DateBase; by import com.adobe.utils.DateBase
Next step :
Err 1120: access to undefined property DateBase.
Again this is an MX reference in the code that does not work with AS3..
This time , open DateUtil.as ..Change : import mx.formatters.DateBase; by import com.adobe.utils.DateBase ;
Compile again :
Err .1120: access to undefined property DateBase.
Quite normal, as the class DateBase.as is not in the package utils from adobe !
To get around that problem, Martin Legris (check his useful blog here) created a class Datebase that just do the work fine . Download it from his site
Pour contourner le problème, Mike Chambers a créer une classe basique Datebase qu'il propose au téléchargement sur son site . here and move it in com/adobe/utils/ .
Compile ...

Err 1172: definition mx.utils:StringUtil can't be found .Again an MX Reference ! Change import mx.utils.StringUtil; by import com.adobe.utils.StringUtil ; in the file Channel20.as . This time we're doen with definitions errors.

Luca Mezzalira proposes all the librairies modified (i guess it's basically the changes i've been decribing here) ) here : http://lucamezzalira.com/2009/02/07/parsing-rss-10-20-and-atom-feeds-with-actionscript-3-and-flash-cs4/ , but i didn't try those . As well , i think it's best if you understand where were the problems and get familiar with all this class-thing in AS3 ...
Now , here's my code : i instanciated for each item from the RSS feed an object RssLine that i then add to my main class RssReader .

Here's the code for the class RssLine (though my comments are in french) :

package  {
	
	// liste des packages importés 
	// on importe les objets movieclip car la classe RssLine en descends 
	import flash.display.MovieClip;
	// la classe textfield sert à créer le champ texte de la ligne.
	import flash.text.TextField ;
	// TextFieldAutoSize permet de fixer la taille en largeur du TextField par rapport au contenu de la ligne.
	import flash.text.TextFieldAutoSize;
	// textformat permet de modifier l'affichage du texte (police, etc..)
	import flash.text.TextFormat ;	
	// URL Request est nécessaire à créer un "objet" URL géré par Flash 
	import flash.net.URLRequest;	
	// navigateToUrl est nécessaire au lancement d'un lien via le navigateur
	import flash.net.navigateToURL;
	// les évenements de souris aussi doivent être importés.
	import flash.events.MouseEvent;	
	// classes servant au déplacement (effet tween)
	import fl.transitions.*;
	import fl.transitions.easing.*;
	import fl.transitions.Tween;


	
	
	public class RssLine extends MovieClip{
		
		// Constants:

		// Public Properties: 
		// Private Properties:
		private var _urllink:String ;
		private var _headline:String ;	
		private var _textfield:TextField ;	
		private var _rsslineformat:TextFormat;
		private var _container:MovieClip;	
		private var _mask:MovieClip;			
		private var _myTween:Tween;
		
			
	
		// Initialization:
		public function RssLine(headline:String,urllink:String) {
			buttonMode = true;
			mouseChildren = false 
			_container = new MovieClip() ;
			addChild(_container);

			_mask = new MovieClip();
			_mask.graphics.lineStyle();

			_mask.graphics.beginFill(0xff0000);
 			_mask.graphics.drawRect(0,0,300,20);			
			_mask.graphics.endFill();					
 			
  			

			
			addChild(_mask);

			_rsslineformat = new TextFormat();
			_rsslineformat.bullet = true ;
			
			_rsslineformat.font = 'Arial' ;
			_rsslineformat.size = 12;
			_textfield = new TextField() ;
			_textfield.selectable = false ;
			//_textfield.width = 500 ;
			_container.addChild(_textfield) ;
			_textfield.text = headline ; 
			_textfield.setTextFormat(_rsslineformat);
			_textfield.autoSize = "left"; 
			_headline = headline ;
			_urllink = urllink ;  
			

			_textfield.mask = _mask ;
			
			// events handling
			addEventListener(MouseEvent.CLICK, buttGotoArticle);
			addEventListener(MouseEvent.ROLL_OVER, buttArtOver);
			addEventListener(MouseEvent.ROLL_OUT, buttArtOut);
			
		}
	
		// Public Methods:

		
		// Protected Methods:
		private function buttGotoArticle(e:MouseEvent){
				var requ = new URLRequest(_urllink);
				navigateToURL(requ ,"_blank");	
		}				
		private function buttArtOver(e:MouseEvent){
			_rsslineformat.underline = true;
			_textfield.setTextFormat(_rsslineformat);
			var diff:int = _mask.width - _textfield.width ;
			var speed:Number ;
			if(diff<0){
				// plus ou moins rapide selon la taille du text 
				if(diff>-10){ // si moins de 10pixels, pas d'animation, deplacement simple
					_container.x = _container.x + diff ;
				}else{
					_myTween = new Tween(_container, "x", None.easeIn, 0, (_mask.width- _textfield.width),3, true);
				}
 				
			}
			

		}				
		private function buttArtOut(e:MouseEvent){
			if(_myTween){
				_myTween.stop();
			}
			_rsslineformat.underline = false;
			_textfield.setTextFormat(_rsslineformat);
			_container.x = 0 ;
		
		}						
		
		
	}
	
}

The RssReader class code is basically the same as Mike Chambers, with a nice handling of the XML downloading : you just have to keep in mind that flash acts synchroniously, so you have to handle the fact that you have to finish the downloading of the entire XML before doing anything else, or your app will just not work .. it's a basic thing to know and every newby has been through that thing ,but it is essential to keep in mind , everytime your flash object loads an external file image or whatever , you have to handle the process and wait until the download is done.

package {
	
	import com.adobe.utils.XMLUtil;
	import com.adobe.xml.syndication.rss.Item20;
	import com.adobe.xml.syndication.rss.RSS20;
	import RssLine ;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.navigateToURL;	
	import flash.display.MovieClip;
	import preloader ;		
	import logoFT ;
	// for button more
	import flash.text.TextField ;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat ;		
	
	public class RssReader extends MovieClip{

		// Constants:
		//url of rss 2.0 feed
		
		private static const RSS_URL:String="http://www.euronews.net/rss/fr/home/home.xml";	
		
		// Public Properties:
		// Private Properties:
		private var loader:URLLoader;
  	    // preloader
		private var _preloader:preloader ;
        //  position  de la première ligne
		private var linePosY:int = 58 ;
		// hauteur entre 2 lignes
		private var _ligneHeight:int = 20  ; 
		// Initialization:
		public function RssReader() {
			loader = new URLLoader();

			// add the logo
			var logo = new logoFT();
			addChild(logo) ;
			logo.x = 0;
			logo.y = 0 ;
			logo.scaleX = logo.scaleY = 0.4 ;
			
			logo.buttonMode = true ;
			logo.addEventListener(MouseEvent.CLICK, buttGotoSite);
			
			// add the more button
			var moretext:TextField = new TextField();
			var moreTf = new TextFormat('Arial',12,'0x71a2c3',null,null,null,"http://www.euronews.net/","_self",null,null,null,null,null);
			moretext.text = "More" ;
			moretext.setTextFormat(moreTf);
			addChild(moretext) ;
			moretext.x = 270 ;
			moretext.y = 30;
			

			_preloader =new preloader();
			addChild(_preloader); 
			_preloader.x = 155;
			_preloader.y = 128;
			//var logoFT = new logoFT();
			//trace(logoFT);
			//request pointing to feed
			var request:URLRequest=new URLRequest(RSS_URL);
			request.method=URLRequestMethod.GET;

			//listen for when the data loads
			loader.addEventListener(Event.COMPLETE, onDataLoad);

			//listen for error events
			loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);

			//load the feed data
			loader.load(request);


		}

		// Public Methods:
		// Protected Methods:
		//called once the data has loaded from the feed
		private function onDataLoad(e:Event):void { 
			_preloader.visible = false ;
			//get the raw string data from the feed
			var rawRSS:String=URLLoader(e.target).data;
			//parse it as RSS
			parseRSS(rawRSS);

		}
		// go to FT site
		private function buttGotoSite(e:MouseEvent):void { 
					var requ = new URLRequest("http://www.sensue.com/");
                   navigateToURL( requ,"_self");	
		}
		

		//parses RSS 2.0 feed and prints out the feed titles into
		//the text area
		private function parseRSS(data:String):void {

			//XMLSyndicationLibrary does not validate that the data contains valid
			//XML, so you need to validate that the data is valid XML.
			//We use the XMLUtil.isValidXML API from the corelib library.
			if (! XMLUtil.isValidXML(data)) {
				//writeOutput("Feed does not contain valid XML.");
				writeOutput('Sorry, data is currently unavailable' , '' );
				return;
			}

			//create RSS20 instance
			var rss:RSS20 = new RSS20();

			//parse the raw rss data
			rss.parse(data);

			//get all of the items within the feed
			var items:Array=rss.items;

			//loop through each item in the feed
			for each (var item:Item20 in items) {
				//print out the title of each item
				writeOutput(item.title,item.link);

			}
			
		}

		private function writeOutput(dataTitre:String,dataUrl:String):void {
				var rssline:RssLine = new RssLine(dataTitre,dataUrl);
				rssline.y = linePosY ; 
				addChild(rssline) ;
				linePosY+= _ligneHeight ; 
				
		}

		private function onIOError(e:IOErrorEvent):void {
			writeOutput('Sorry, data is currently unavailable'+e.text+')' , '' );
			_preloader.visible = false ;
		}

		private function onSecurityError(e:SecurityErrorEvent):void {
			//writeOutput("SecurityError : " + e.text);
			writeOutput('Sorry, data is currently unavailable ('+e.text+')'  , '' );
			_preloader.visible = false ;
		}


	}

}
»

Comments

Petite correction..

Salut, bel article! Juste une petite correction:

tu dis: "Pour contourner le problème, Mike Chambers a créer une classe basique Datebase qu'il propose au téléchargement sur son site . here and move it in com/adobe/utils/ ."

c'est plutôt Martin Legris ;)

Merci

sorry martin !

i didn't notice the @author line in the class... i've corrected the article so that what belongs to caesar comes back to caesar ...
Nice blog of yours, i'll keep in my bookmark , to stay in tune with your work . Best regards !!

..by the way

August is missing in the "class"...(sic)