Flex播放器(实现播放、缓冲进度条和音频曲线显示)
一时兴起,玩起了flex,本来还想要做个flex博客,不过目前还只能在里面树个公告。。。没办法做完啊,河蟹的个杯具的!flex布局不像是css,精美flash动画不是拖一个两个控件就能做出来滴,而是一笔一条线绘制出来滴!这些我都还不熟悉,所有折腾快一个星期了,每天都是搞到头大才睡觉,今天终于能出一个简单的播放器。
一直很喜欢音乐这个东西,喜欢jay,更喜欢他的歌,也很崇拜小猪,他的一段灰色空间曾让我激流奋进,想过自己能做个播客放自己喜欢听的歌曲,出于自恋那样会更有一点点满足感。呃~走神了,前二天无意看到一群教师的个人博客,深深的被他们的博文所吸引,无论是谈技术还是记录生活的,写得都是那么的真切,还有坚持每日一博的,坚持不放弃...
mx:progressbar实现加载歌曲缓冲进度条
progressbar有三大mode模式,分别为event、manual、polled,event为基于事件驱动模式,可设置source对象自动显示加载进程;manual为手动模式,需要调用progressbar.setprogress()方法设置滚动条进度;polled为轮询模式,本例使用的manual模式,sound加载load请求歌曲添加一个progressevent.progress处理中监听事件,然后根据sound已加载的bytes和bytestotal数,设置setprogress进度。这里需要注意在切换歌曲的时候先要移除progressevent.progress事件,否则之前播放歌曲还未加载完又切换load新歌曲时回出现progressbar触发多个progress事件被设置进度出现来回滚动的问题。
mx:hslide调节滑秆
这个控件在本例中2处使用,实现对播放进度和声音大小的控制。最一开始调整播放进度的问题难倒了我很久,因为在歌曲播放过程中hslide要自动滑动当前播放位置,同时又需要能手动拖动播放位置,hslide本来有一个很好的change事件用来侦听改变,但是我使用定时器设置hslide的value的时竟然也给我触发change事件,参考了adobe哥官网的帮助文档,说是slider 组件的值因鼠标或键盘交互操作而改变时调度,如果 livedragging 属性是 true,则在用户移动滑块时持续调度该事件。 如果 livedragging 是 false,则在用户释放滑块时调度该事件。但是无论我怎么设置,在代码里改变了hslide的value值怎会触发change事件,不是说在用户交互操作而改变时调度吗?无赖啊,后来只能折中采取监听thumbdrag滑秆拖动时事件,这个事件adobe哥的解释是当按下滑块并随后随鼠标移动时调度,这样会有一个小问题,就是需要拖动滑秆按下时才会触发,点击无效。
soundmixer.computespectrum()分析音频曲线
本例你看到显示的音频曲线其实是右64个绘制成条状的canvas控件排列而成,然后使用定时器每间隔100毫秒重新设置他们的scaley位置以呈现出变幻曲线的效果,代码只有三行很简单,具体可参见我下面源码给出的timertick事件。这里为什么要用定时器呢?在网上看别人是监听event.enter_frame事件重绘音频曲线的,不想搞那么麻烦就直接用定时器了,随便根据bytestotal和bytesloaded计算下歌曲播放时间,使用100毫秒的定时器也并好耗站资源,cpu没有涨很高。
效果图:
mxml代码如下:
<?xml version="1.0" encoding="utf-8"?>
<mx:application xmlns:mx="http://www.adobe.com/2006/mxml" verticalgap="0" scroll="false" backgroundalpha="0"
horizontalscrollpolicy="off" verticalscrollpolicy="off" verticalalign="middle" horizontalalign="center"
initialize="init(event)" layout="vertical" fontsize="14" paddingleft="0" paddingtop="0" paddingright="0" paddingbottom="0" >
<mx:script>
<![cdata[
import mx.formatters.dateformatter;
import mx.effects.soundeffect;
import mx.events.sliderevent;
import mx.core.soundasset;
import mx.controls.alert;
import mx.managers.cursormanager;
import flash.media.*;
import flash.utils.timer;
[embed(source="images/cursor.gif")]
private var cursorhand : class;//图标
private var xml:xml;
private var xmlpath:string = "/flex/bin-debug/song.xml";
private var currindex : number = 0;
private var song :sound;
private var channel :soundchannel;
private var position : number = 0;
// 保存 512 个声音波形的快照
private var bytes:bytearray = new bytearray();
// soundbar 的个数
private var barnum:uint = 64;
// 保存所有 soundbar 的引用
private var soundbars:array = new array();
//定时器
private var timer : timer;
//application的initialize初试化事件
private function init(event:event):void
{
var loader:urlloader = new urlloader();
loader.load(new urlrequest(xmlpath));
loader.addeventlistener(event.complete,xml_complete);
timer = new timer(100);
timer.addeventlistener(timerevent.timer,timertick);
var barwidth:number = boxsoundbar.width*1.00/barnum;
// 初始化canvas为音频条,放入舞台并加入数组
for (var i:uint = 0; i < barnum; i++) {
var soundbar:canvas = new canvas();
soundbar.width = barwidth;
soundbar.height = boxsoundbar.height;
soundbar.x = i * barwidth;
soundbar.y = 0;
var g:graphics = soundbar.graphics;
g.linestyle(1,0x6688aa,1);
g.begingradientfill(gradienttype.radial,[0x33cc00,0x456628],[1,1],[0,255],null,spreadmethod.reflect,interpolationmethod.rgb,0);
g.drawrect(0,0,soundbar.width,soundbar.height);
g.endfill();
boxsoundbar.addchild(soundbar);
soundbars.push(soundbar);
}
// 隐藏一些内建的鼠标右键菜单项
this.contextmenu.hidebuiltinitems();
var contextmenuitem : contextmenuitem = new contextmenuitem("powered by: jonllen");
contextmenuitem.enabled = false;
contextmenu.customitems.push(contextmenuitem);
this.contextmenu.customitems.push(contextmenuitem);
//更改鼠标图标
cursormanager.setcursor(cursorhand);
}
//读取xml文件完成事件
private function xml_complete(event:event):void
{
xml = new xml(event.target.data);
if(xml.item.length()>=1)
{
listsong.dataprovider= xml.item.name;
listsong.selectedindex = 0;
//手动触发list的change事件
listsong.dispatchevent(new mx.events.listevent(event.change, true, false));
}
}
//list选择歌曲改变事件
private function xml_change(event:event):void
{
currindex = event.target.selectedindex;
timer.stop();
//停止声音文件的加载
if( song!=null )
{
//移除之前加载progress事件对songprogress进度条的控制
song.removeeventlistener(progressevent.progress,songprogress_change);
if( song.isbuffering )
song.close();
}
song = new sound();
var url : string = xml.item[currindex].url;
var source:urlrequest = new urlrequest(url);
song.load(source);
song.addeventlistener(progressevent.progress, songprogress_change);
song.addeventlistener(ioerrorevent.io_error, songprogress_error);
position = 0;
songstart();
}
//歌曲播放完成
private function songprogress_complete(e:event):void {
if(currindex == xml.item.length()-1) {
currindex = 0;
}else {
currindex++;
}
listsong.selectedindex = currindex;
listsong.dispatchevent(new mx.events.listevent(event.change, true, false));
}
//加载歌曲失败
private function songprogress_error(e:ioerrorevent):void {
alert.show("文件不存在!","系统提示");
}
//开始播放歌曲
private function songstart():void {
if ( channel != null ){
channel.stop();
}
lblname.text = xml.item[currindex].name;
channel = song.play(position,int.max_value);
var length :number = song.length*song.bytestotal/song.bytesloaded;
var date : date = new date();
date.time = length;
var dt : dateformatter = new dateformatter();
dt.formatstring="nn:ss";
var totaltime : string = dt.format(date);
date.time = channel.position;
lbltime.text = dt.format(date) + " | " + totaltime;
lblstatus.text = "播放";
var soundcontrol : soundtransform = channel.soundtransform;
soundcontrol.volume = volumeslider.value;
channel.soundtransform= soundcontrol;
timer.start();
boxsoundbar.visible = true;
}
//停止歌曲播放
private function songstop():void {
timer.stop();
position = 0;
boxsoundbar.visible = false;
lbltime.text = "00:00 |"+lbltime.text.split("|")[1];
lblstatus.text = "停止";
songslider.value = songslider.minimum;
songprogress.setprogress(songprogress.minimum,songprogress.maximum);
if ( channel != null )
{
channel.stop();
}
}
//暂停歌曲播放
private function songpause():void {
if ( channel != null ){
timer.stop();
position = channel.position;
channel.stop();
lblstatus.text = "暂停";
}
}
//加载歌曲进度条显示
private function songprogress_change(e:progressevent):void {
var percent:int = math.round(e.bytesloaded * 100 / e.bytestotal);
songprogress.setprogress(e.bytesloaded,e.bytestotal);
}
//定时器方法
private function timertick( e:timerevent):void {
if( channel!=null) {
var length :number = song.length*song.bytestotal/song.bytesloaded;
var date : date = new date();
date.time = length;
var dt : dateformatter = new dateformatter();
dt.formatstring="nn:ss";
var totaltime : string = dt.format(date);
date.time = channel.position;
lbltime.text = dt.format(date) + " | " + totaltime;
songslider.value=100*channel.position/length;
if( songslider.value>=songslider.maximum){
timer.stop();
songprogress_complete(null);
return;
}
soundmixer.computespectrum(bytes, false, 0);
for (var i:uint = 0; i < barnum; i++) {
soundbars[i].scaley = bytes.readfloat();
}
}
}
//歌曲进度调整事件
internal function songslider_change(e:sliderevent):void{
timer.stop();
if ( channel != null ){
var length :number = song.length*song.bytestotal/song.bytesloaded;
position = e.value*length/100;
songstart();
}
}
//声音大小调整事件
internal function changevolume(evt:sliderevent):void{
if ( channel != null ){
var soundcontrol : soundtransform = channel.soundtransform;
soundcontrol.volume = evt.value;
channel.soundtransform= soundcontrol;
}
}
//设置歌曲播放时间和总时间
private function settimestatus():void {
var length :number = song.length*song.bytestotal/song.bytesloaded;
var date : date = new date();
date.time = length;
var dt : dateformatter = new dateformatter();
dt.formatstring="nn:ss";
var totaltime : string = dt.format(date);
date.time = channel.position;
lbltime.text = dt.format(date) + " | " + totaltime;
}
]]>
</mx:script>
<mx:hbox width="100%" verticalgap="0" verticalalign="middle" horizontalalign="center">
<mx:canvas width="440" bordercolor="#cccccc" borderstyle="solid" height="171">
<mx:label id="lblname" x="5" fontsize="18" y="10" text=""/>
<mx:hbox id="boxsoundbar" horizontalgap="0" verticalalign="middle"
width="192" height="50" x="5" y="39" visible="false"></mx:hbox>
<mx:progressbar id="songprogress" label=""
width="290" height="3" mode="manual" textalign="left"
labelplacement="center" fontsize="3" x="10" y="97"
minimum="0" maximum="100" barcolor="yellow"
trackcolors="[white, halosilver]"/>
<mx:hslider id="songslider" stylename="song" value="0"
showtrackhighlight="true" x="5" y="85" thumbdrag="songslider_change(event)"
width="300" height="22" minimum="0" maximum="100"
livedragging="false" snapinterval="1" tooltip="拖动调整播放进度">
</mx:hslider>
<mx:label id="lblstatus" x="243" y="41" text=""/>
<mx:label id="lbltime" x="205" y="66" text="00:00 | 5:23"/>
<mx:button x="10" y="124" label="play" click="songstart()"/>
<mx:button x="74" y="124" label="pause" click="songpause()"/>
<mx:button x="152" y="124" label="stop" click="songstop()"/>
<mx:hslider id="volumeslider" stylename="volume" change="changevolume(event)"
showtrackhighlight="true" value="0.5" x="222" y="133"
width="81" minimum="0" maximum="10" livedragging="true"
snapinterval="0.1" tooltip="音量调节" />
<mx:label x="308" fontsize="18" y="10" text="歌曲列表"/>
<mx:list id="listsong" alpha="0.25" x="308" y="43" height="116"
change="xml_change(event)" width="130" tooltip="点击选择歌曲"></mx:list>
</mx:canvas>
</mx:hbox>
</mx:application>
上一篇: Flex中怎么给表格中的滚动条定位避免刷新回到原处
下一篇: FLEX给页面添加滚动条实现思路及代码