自定义组件开发 第二节 MXML组件开发

自定义组件开发 第二节   MXML组件开发
        本节,我们以“自定义播放器”为实例,来学习 MXML 组件开发的方法。

学习目标

1. 了解 MXML 组件开发的特点。

2. 学习使用 MXML 语言开发组件,逻辑部分要结合 ActionScript 语言。

3. 开发组件“自定义播放器”。

 

MXML 组件开发的特点

为什么要开发组件?简而言之,是因为组件有利于代码的维护和复用。组件可以被定义在 MXML 文件(以 .mxml 为后缀的文件)或者 ActionScript 文件(以 .as 为后缀的文件)中。因此,有两种组件开发的形式:MXML 组件开发及 ActionScript 组件开发。凡是定义在 MXML 文件中的组件都可以转化为定义在 ActionScript 文件中的组件。Flex SDK 中的大部分组件都是定义在 ActionScript 文件中的。

然而,MXML 组件开发也有其适用之处。MXML 组件比较适合“组合模式”的组件,通过 MXML,可以很方便的进行子组件的布局及数据绑定。 以下是一些 MXML 组件开发的示例,更多的内容还需读者在实践中体会摸索。

MXML 组件开发的优点:

  1. 可以利用“设计”模式,进行所见即所得的界面开发。
  2. 可以快速的添加子组件。不需要申明一个实例,然后再调用 addChild(),将其添加到父组件的布局中去。
  3. 可以很方便地进行数据绑定。使用 “{ binding_expression }”可以快速将任意可绑定的数据源绑定到指定位置。
  4. 可以很方便的定义类实例,不需要显示的初始化。例如定义<fx:Binding/>后,应用程序运行时会自动初始化该实例。

MXML 语言的缺点:

  1. 没有 ActionScript 的辅助,无法完成复杂的逻辑。
  2. MXML 组件默认都是 public 的,没有访问限制。
  3. MXML 标签中定义的事件是不可以被移除的。如<mx:Button click="doClick()"/>,click 事件是不可以被移除的。
  4. 不能自定义构造函数。

使用 Flash Builder 4 和 MXML 语言开发组件的步骤:

  1. 新建 Flex 库项目。Flex 库项目编译后可以产生 swc 文件,可以作为组件发布的主要形式。
  2. 新建 MXML 组件文件,文件名即为组件名。
  3. 继承现有组件。文件内部第一级标签即为所继承组件名。
  4. 在 MXML 文件主标签里加 implements 属性可以继承接口。
  5. 在 MXML 文件里添加各种标签,包括可视组件,数据服务,验证,特效等。如果是非可视的标签,如特效等要加到 <fx:Declarations> 标签里,这是 Flex 4 的新特点。
  6. 可以在 <fx:Script> 标签里添加 ActionScript 代码,以实现逻辑,比如事件处理等。代码要被 <![CDATA[ 和 ]]> 包围。
  7. 可以在 <fx:Style> 标签里添加CSS样式代码,以设置组件样式。
  8. 可以添加 <fx:XML>、<fx:XMLList>、<fx:Array>、<fx:Model> 等实用标签,以提供更好的功能。这些标签都要加在 <fx:Declarations> 标签里。

 

开发实例

本节通过开发一个“自定义播放器”组件,来讲解 MXML 组件开发技术。界面布局、组件使用等部分采用 MXML 语言,逻辑部分采用 ActionScript 语言。因为是组合式组件,不涉及组件生命周期等,所以可以利用 MXML 来开发,方便布局和属性设置等。

先看一下最终使用效果图:(图五十九)

图五十九

播放器预览

上部即是我们要做的“自定义播放器”组件,中间和下部是文字介绍和播放列表,不属于“自定义播放器”组件。

接下来,和我一起一步一步开发“自定义播放器”。

1. 设计“自定义播放器”组件功能

播放器是一种很常用的组件,既可以是一个独立产品,也可以集成到大系统中,如教学系统、会议系统、娱乐网站等,主要分为音频播放器和视频播放器。

对于视频播放器,Flex 3 框架的 mx.controls.VideoDisplay 类提供了视频播放的视频显示和基本控制,但是没有提供控制界面等播放器必需的元素。Flex 4 框架保留了此类,但是在新增的 spark 组件体系中,有功能更加完善的视频播放器支持类。spark.primitives.VideoElement 类提供了基本控制和显示功能,但是没有外壳,spark.components.VideoPlayer 类是带完整外壳的视频播放器组件,spark.skins.default.VideoPlayerSkin 是 spark.components.VideoPlayer 类的默认皮肤,还有支持组件:VideoPlayerScrubBar(时间条),VideoPlayerVolumeBar(音量条),VideoPlayerVolumeBarMuteButton(静音按钮)。可以说功能非常完善了。

对于音频播放器,Flex 4 框架还没有提供现成的完整组件,但是 flash.media 包提供了对声音的基本控制。互联网上有封装好的开源音频播放器类库,接口类似于 Flex 视频播放器,可以拿来复用。地址为:http://rojored.com/#simple-flex-audio-player

我们的“自定义播放器”就是要结合视频播放器和音频播放器的功能,可以同时播放视频和音频,自动切换显示区的有无。

详细功能列表:

1. 可以加载视频和音频文件,自动判断并播放。

2. 如果是视频,则有显示区,是音频,则无显示区。

3. 播放/暂停按钮,具有播放/暂停控制功能;停止按钮,具有停止并返回开头控制功能。

4. 时间条,可以显示播放进度;拖动控制播放进度。

5. 已播放时间、总时间显示。

6. 音量条,可以控制声音大小。切换视频和音频,音量不变。

7. 静音按钮,可以切换静音。静音时,音量条可以调节,可以设置音量,以便在不静音时生效。

8. 信息,可以显示播放内容的额外信息,如内容描述。

9. 播放视频时,单击显示区切换视频播放和暂停状态。

2. 新建“自定义播放器”组件库项目及文件、示例项目及文件

a. 新建 Flex 库项目,项目名:CustomPlayer。其它选项默认即可。构建后,Flash Builder 4 会自动在 bin 文件夹中生成 swc 文件,即编译打包的组件。

b. 在 src 文件夹中新建包,名称:cn.airia.fb4.customPlayer。Flash Builder 4 会自动创建相应文件夹。

c. 在包 cn.airia.fb4.customPlayer 中新建 MXML 组件 CustomPlayer,继承自 spark.components.Group。宽度 100%,高度不填,布局改为 spark.layouts.VerticalLayout。

d. 新建 Flex 项目,项目名:CustomPlayerSample,作为示例项目,默认新建CustomPlayer.mxml 文件作为主应用程序文件。示例项目需要使用组件,有多种方法,我们采用这种方法:“库路径” > “添加项目” > “CustomPlayer”。在发布时,可以将最终 CustomPlayer 组件的 swc 文件复制到 CustomPlayerSample 项目的 libs 文件夹中,并从库路径中删除 CustomPlayer 项目,这样就可以独立运行 CustomPlayerSample 了。

e. 在CustomPlayerSample项目的“属性” > “项目引用”里添加对 CustomPlayer 项目的引用。在某些时候有用,比如打开CustomPlayerSample项目时也打开CustomPlayer 项目。

3. 编写示例程序类 CustomPlayerSample

示例程序用来使用和测试“自定义播放器”组件。可以简单处理,这里不详解了。

设计界面如下:(图六十)

 

图六十

播放器设计界面

       上部是“自定义播放器”组件。各位读者因为还没编好“自定义播放器”,所以只会显示空白,没有播放控件。 中间是一段介绍文字。下部是播放列表,一个 List,选择项目后,“自定义播放器”会播放相应视音频。

我把示例程序 CustomPlayerSample 的主题设为了Wireframe,如果主题不同,组件在示例程序中的表现也不同,但是组件本身是一样的。

代码如下:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/halo"
               xmlns:customPlayer="cn.airia.fb4.customPlayer.*"
               minWidth="800"
               minHeight="600"
               viewSourceURL="srcview/index.html">
    <!-- 这是 Flex 4 的新布局组件 --> 
    <s:layout>
        <s:BasicLayout/>
    </s:layout>
    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            // 播放列表数据,可绑定 
            [Bindable]
            private var playListDP:ArrayCollection = new ArrayCollection([
                {label: "10秒钟的视频", url: "videos/10.flv"}, 
                {label: "黑猩猩打空手道", url: "videos/空手道.flv"}, 
                {label: "电话来了铃声", url: "audios/dddd.mp3"}]);
            /**
             * 给播放器要播放文件的源路径和额外信息,播放 
             */ 
            private function play(event:Event):void {
                var item:Object = (event.target as List).selectedItem;
                player.source = item.url;
                player.info = item.label;
                player.play();
            }
        ]]>
    </fx:Script>
    <!-- 这就是我们要编写的自定义播放器组件 --> 
    <customPlayer:CustomPlayer id="player"
                               horizontalCenter="0"
                               width="450"
                               bottom="250"
                               top="20">
    </customPlayer:CustomPlayer>
    <!-- 文字介绍 --> 
    <mx:Text y="360"
             width="450"
             horizontalCenter="0"
             color="#EA9FA4"
             text="上面是一个简单的自定义播放器组件,可用于会议系统、教学系统、娱乐系统等。选择播放下面列表里的视频或音频:"/> 
    <!-- 播放列表,选择项目后播放对应视音频 --> 
    <s:List id="playList"
            dataProvider="{playListDP}"
            labelField="label"
            y="400"
            width="450"
            height="100"
            horizontalCenter="0"
            selectionChanged="play(event);"/>

</s:Application>

       

4. 编写组件类 CustomPlayer

CustomPlayer 组合了官方视频类——VideoElement 类,以及开源音频类——rojored 的 Audio 类,并加上统一的控件控制两者,和额外的功能、改进,如点击视频切换播放暂停,静音后还能设置音量值等。

具体讲解可以参见代码注释,结合代码讲解更加直观,易于理解。

设计界面如下:

图六十一

CustomPlayer

在播放视频时会自动显示视频显示区。

代码如下: (代码片段包含了本例讲解、注释)

<?xml version="1.0" encoding="utf-8"?>
<!-- 这是 Flex 4 的新容器组件,没有皮肤,性能更好 --> 
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
         xmlns:s="library://ns.adobe.com/flex/spark"
         xmlns:mx="library://ns.adobe.com/flex/halo"
         xmlns:rojored="com.rojored.view.controls.*"
         width="100%"
         creationComplete="init();">
 
    <!-- 这是 Flex 4 的新布局组件,这里采用垂直布局 --> 
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>
 
    <fx:Script>
        <![CDATA[
            /**
             * 标示播放类型的常量 
             */ 
            public static const VIDEO:String = "video";
            public static const AUDIO:String = "audio";
 
            /**
             * 设置的音量 
             * 不等同于播放对象的音量,在静音时不为0,仍然保持设置值 
             */ 
            [Bindable]
            private var volume:Number;
 
            /**
             * 播放对象 
             * 可选 VideoElement  Audio 两种类 
             * 但是调用的接口方法类似,所以不限定类型 
             */ 
            [Bindable]
            private var playObject:*;
 
            /**
             * 源属性 
             * 直接和播放对象的源挂钩 
             */ 
            [Bindable]
            public function get source():Object {
                return playObject.source;
            }
 
            public function set source(value:Object):void {
                // 设置源后,要设置播放对象,以便选择播放视频或音频 
                setPlayable(value);
            }
 
            /**
             * 显示在控制栏的额外信息 
             */ 
            private var _info:String;
 
            [Bindable]
            public function get info():String {
                return _info;
            }
 
            public function set info(value:String):void {
                _info = value;
            }
 
            /**
             * 初始化 
             * 默认播放是视频,音量值 0.7
             */ 
            private function init():void {
                playObject = video;
                volume = 0.7;
            }
 
            /**
             * 播放 
             * 因为视音频播放对象播放方法一样,所以可以不用判断类型 
             */ 
            public function play():void {
                playObject.play();
            }
 
            /**
             * 暂停 
             * 因为视音频播放对象暂停方法一样,所以可以不用判断类型 
             */ 
            public function pause():void {
                playObject.pause();
            }
 
            /**
             * 切换播放和暂停状态 
             * 如果已播放,则暂停,如果已暂停,则播放 
             */ 
            public function togglePlayPause():void {
                playObject.playing ? pause() : play();
            }
 
            /**
             * 停止 
             * 因为视音频播放对象停止方法一样,所以可以不用判断类型 
             */ 
            public function stop():void {
                playObject.stop();
            }
 
            /**
             * 根据源来设置播放对象 
             * 先判断类型,然后停止非此类型的播放器,切换到此类型的播放器 
             * 如果是视频类型,显示视频容器,如果是音频类型,隐藏视频容器 
             */ 
            private function setPlayable(source:Object):void {
                switch (judgeType(source)) {
                    case VIDEO:
                        audio.source = null;
                        audio.stop();
                        // 显示视频容器 
                        videoContainer.visible = true;
                        // 标记显示列表失效,这样程序会自动更新绘制显示列表,否则视频位置和大小会有问题 
                        videoContainer.invalidateDisplayList();
                        playObject = video;
                        playObject.source = source;
                        break;
                    case AUDIO:
                        video.source = null;
                        video.stop();
                        // 隐藏视频容器 
                        videoContainer.visible = false;
                        playObject = audio;
                        playObject.source = source;
                        break;
                }
            }
 
            /**
             * 判断播放类型 
             * 利用正则表达式判断文件名后缀,如果是.flv,则为视频,是.mp3,则为音频 
             * 还可以添加其它文件类型的判断,现在暂时不考虑 
             */ 
            private function judgeType(source:Object):String {
                if (new RegExp("(\.flv)$", "i").test(source.toString())) {
                    return VIDEO;
                } else if (new RegExp("(\.mp3)$", "i").test(source.toString())) {
                    return AUDIO;
                }
                return null;
            }
 
            /**
             * 格式化时间 
             *  
             * VideoElement 返回的是秒,Audio 返回的是毫秒,要处理好 
             */ 
            private function format(value:Number):String {
 
                var seconds:Number;
 
                if (playObject is Audio) {
                    seconds = Math.floor(value / 1000);
                } else if (playObject is VideoElement) {
                    seconds = value;
                }
 
                var result:String = Math.floor(seconds % 60).toString();
 
                // 如果是秒数是 1 位数,则在前面添加 1  0 
                if (result.length == 1) {
                    result = Math.floor(seconds / 60).toString() + ":0" + result;
                } else {
                    result = Math.floor(seconds / 60).toString() + ":" + result;
                }
                return result;
            }
        ]]>
    </fx:Script>
 
    <!-- 将播放对象的音量绑定为设置音量,如果静音了,则为0 -->
    <fx:Binding destination="video.volume"
                source="muteBtn.selected ? 0 : volume"/>
    <fx:Binding destination="audio.volume"
                source="muteBtn.selected ? 0 : volume"/>
 
    <!-- 音频播放对象,因为是非可视对象,所以要放在 <fx:Declarations> 标签内 --> 
    <fx:Declarations>
        <rojored:Audio id="audio"/>
    </fx:Declarations>
 
    <!-- 视频播放对象,因为 VideoElement 不能接受 click 事件,所以用一个 Group 包装 --> 
    <s:Group id="videoContainer"
             click="togglePlayPause();"
             width="100%"
             height="100%">
 
        <s:VideoElement id="video"
                        width="100%"
                        height="100%"
                        autoRewind="true"/>
        <!-- 停止后自动回复到开头 --> 
 
    </s:Group>
 
    <!-- 播放时间条,将值和播放对象的播放头时间双向绑定,这样就可以同步了 --> 
    <!-- 这里不采用 s:HSlider 组件,否则 value 的双向绑定会导致播放问题 --> 
    <mx:HSlider id="timeSlider"
                width="100%"
                minimum="0"
                maximum="{playObject.totalTime}"
                value="@{playObject.playheadTime}"
                liveDragging="false"/>
 
    <s:Group width="100%">
 
        <!-- 播放暂停按钮,采用切换按钮,切换播放和暂停 --> 
        <s:ToggleButton id="playPauseBtn"
                        label="{playObject.playing ? '暂停' : '播放'}" 
                        width="54"
                        x="0"
                        selected="{playObject.playing}"
                        click="togglePlayPause();"/>
 
        <!-- 停止按钮,停止播放 --> 
        <s:Button id="stopBtn"
                  label="停止" 
                  click="stop();"
                  x="60"
                  width="52"/>
 
        <!-- 播放时间显示,包括当前播放头时间和总时间 --> 
        <mx:Label id="time"
                  x="119"
                  width="76"
                  text="{format(playObject.playheadTime)}/{format(playObject.totalTime)}"/>
 
        <!-- 静音按钮,采用切换按钮,切换后会自动影响绑定它的播放对象音量值 --> 
        <s:ToggleButton id="muteBtn"
                        label="" 
                        width="33"
                        x="218"/>
 
        <!-- 音量条,将值和设置的音量值双向绑定,而非播放对象的当前音量,因此不受静音影响 --> 
        <!-- 因为 valueInterval 默认为1,不合要求,要改为0,也就是无值间隔 --> 
        <s:HSlider id="volumeSlider"
                   width="86"
                   x="252"
                   y="5"
                   minimum="0"
                   maximum="1"
                   valueInterval="0"
                   value="@{volume}"
                   liveDragging="true"/>
 
        <!-- 信息标签,显示赋给的额外信息 --> 
        <mx:Label id="infoLabel"
                  text="{info}"
                  x="346"
                  width="104"/>
 
    </s:Group>
 

</s:Group>

总结:

      本节使用了官方组件和开源类库自定义播放器组件,满足自己的特殊需要。比如可以同时播放视频和音频、点击视频能暂停等。 MXML 组件开发利用 MXML 做界面布局,ActionScript 做逻辑运算,各得其所,方便高效。

      开发时,经常要自定义组件,这时可以充分利用已有的组件,减少重复开发带来的浪费。所以,要多多关注全世界的类库,同时,也可以把自己的成果贡献给世界。

你可能感兴趣的:(xml)