【问题标题】:Flex Mobile - TileLayout performance issuesFlex Mobile - TileLayout 性能问题
【发布时间】:2012-04-19 00:02:19
【问题描述】:


我正在为平板设备构建移动应用程序。
我在使用平铺布局的 List 组件时遇到了巨大的性能问题。
我在屏幕上显示大约 20-24 个项目的页面,用户可以滚动浏览页面。不幸的是,列表组件生成新页面的速度非常慢。
总体而言,我使用平铺布局测试了独立列表,它在每种情况下的性能都非常慢(我已经在 iPad1 和 iPad2 上测试过)。
如果您知道问题的一些解决方案,请提供建议。

谢谢。

【问题讨论】:

    标签: apache-flex list mobile tiles flex-mobile


    【解决方案1】:

    通常,Tile 的 flex 速度非常慢。这是因为它允许每个项目渲染器的大小可变,并且 measure / updateDisplayList 需要很多时间。

    1. 在这种情况下,我建议您使用自定义(自己制作的)磁贴列表并删除 flex 组件。如果您仅将其用于布局,您可以手动/以编程方式计算图块中每个项目的位置并将其移动到右侧 x/y 像素。

    2. 还要注意效果!它们可能在 AIR 模拟器或桌面浏览器中运行良好,但在移动设备上会占用大量资源。

    3. 如果您仍然想使用 flex tile 组件,而不是创建自己的 tile 控件,那么我建议您对显示在其中的所有项目使用带渲染器的固定,并在 TileList 中指定 columnWidth 和 rowHeight泄爆。这可能会提高一点性能... :)

    还提供一些示例代码并没有什么坏处。也许你做错了什么……不要:额外的失效,糟糕的渲染等等……!

    【讨论】:

    • 感谢您的建议,我将编写自己的组件。经过一些调试后,我发现创建了更多 itemRenderer 实例,这是最重要的部分。创建 70 个实例大约需要 20 sek,而且它们非常简单,就像 IconItemRenderer 一样。
    • 我建议您只使用一些简单的二维数学来定位您的渲染器/IconLoader。我知道;使用现有控件更容易,但这并不难实现,而且速度会非常快;)顺便说一句,您正在测试的设备是什么?
    • 设备是 iPad 1 和 iPad 2。是的,是运行测试,我已将 TiledLayout 交换为 UIComonent,它创建精灵并定位它们,差异是不可取的。
    • 您好@ChavdarSlavov,我不确定我是否理解您的评论?!使用自定义组件更好还是没有区别?
    • 这样更好,但是你必须使用像 Sprite 和 UIComponent 这样的基础组件。 TileLayout 所做的计算可能是瓶颈,但应用程序只是使用低级组件。
    【解决方案2】:

    已经有一段时间了,所以我不记得具体细节了。这是我使用的代码,但请记住,我编写的组件是用于特定目的的,并且包含一些不良做法以在很短的时间内完成特定问题。不过我希望有用。

    分页列表

    package components.pagedList
    {
        import components.pagedList.vo.PageItemVo;      
        import mx.collections.ArrayCollection;
        import mx.collections.IList;
        import mx.core.IFactory;
        import mx.core.UIComponentGlobals;
        import mx.events.CollectionEvent;
        import mx.events.CollectionEventKind;
    
        import spark.components.List;
        import spark.layouts.HorizontalLayout;
    
    /**
     * 
     */
    public class PagedList extends List
    {
    
    
        //--------------------------------------------------------------------------
        //
        //  Constructor
        //
        //--------------------------------------------------------------------------        
    
        public function PagedList()
        {
            super();
        }
    
    
        //--------------------------------------------------------------------------
        //
        //  Variables
        //
        //--------------------------------------------------------------------------    
    
        /**
         * Dimentions for the inner items should be specifyed because 
         * this component needs to know how many items can be placed in 
         * one page, but don't have the ability to create this items.
         * Default value is 1 to avoid division by zero.
         */
        public var innerItemWidth:Number = 1;
    
        public var innerItemHeight:Number = 1;
    
        /**
         * When inner item dimentions are set the component calculates how many items 
         * (per row and col) can be shown on one scren and pass these data to the page 
         * trough the dataProvider for the page.
         */
        private var pageRowCount:int = 1;
    
        private var pageColCount:int = 1;   
    
        /**
         * Count of the items that can fit in one page.
         * This count is used to slice the data into picies
         * with the same length. 
         */
        private var itemsPerPage:int = 0;   
    
        /**
         * Original data provider is saved here so we can re-slice it when
         * size change occure and listen for data change event.
         * 
         * TODO: implement the data chage listener
         */
        private var _originalDataProvider:IList;
    
        /**
         * 
         */
        private var cachedUnscaledWidth:Number = 0;
        private var cachedUnscaledHeight:Number = 0;
    
        //----------------------------------
        //  Custom tiles data
        //----------------------------------
    
        private var _customTilesData:Array;
    
        private var customTilesDataChanged:Boolean;
    
        public function set customTilesData(value:Array):void
        {
            _customTilesData = value;
            customTilesDataChanged = true;
            invalidateProperties();
        }
    
        public function get customTilesData():Array
        {
            return _customTilesData;
        }
    
        //----------------------------------
        //  dataProvider
        //----------------------------------
    
        private var originalDataProviderChanged:Boolean;
    
        [Inspectable(category="Data")]  
        /**
         * 
         */
        override public function set dataProvider(value:IList):void
        {
            if (_originalDataProvider)
                _originalDataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, originalDataProvider_collectionChangeHandler);     
    
            originalDataProviderChanged = true;
    
            if (value)
                value.addEventListener(CollectionEvent.COLLECTION_CHANGE, originalDataProvider_collectionChangeHandler, false, 0, true);
    
            _originalDataProvider = value;
            updatePagedData();
        }   
    
        /**
         * 
         */
        private function originalDataProvider_collectionChangeHandler(event:CollectionEvent):void
        {
            //trace('data changed:', event.kind, 'location:', event.location);
            if (event.kind == CollectionEventKind.REPLACE)
            {
                updateReplacedItem(event.location);
            }
    
        }   
    
        /**
         * 
         */
        private function updateReplacedItem(index:int):void
        {
            if (dataProvider)
            {
                var pageNumber:int = int(index / itemsPerPage);
                var itemIndex:int = index - (pageNumber * itemsPerPage);
                if (dataProvider[pageNumber])
                {
                    var pageData:PageItemVo = PageItemVo(dataProvider[pageNumber])
                    pageData.data[itemIndex] = _originalDataProvider[index];
                }
            }
        }   
    
        //----------------------------------
        //  innerItemRenderer
        //----------------------------------
    
        private var _innerItemRenderer:IFactory;
    
        private var innerItemRendererChanged:Boolean;
    
        public function set innerItemRenderer(value:IFactory):void
        {
            _innerItemRenderer = value;
            innerItemRendererChanged = true;
            invalidateProperties();
        }
    
        public function get innerItemRenderer():IFactory
        {
            return _innerItemRenderer;
        }
    
    
        //----------------------------------
        //  gaps
        //----------------------------------    
    
        /**
         * 
         */
        public function set verticalGap(value:Number):void
        {
            _verticalGap = value;
            gapsChanged = true;
            invalidateProperties();
        }
    
        public function get verticalGap():Number
        {
            return _verticalGap;
        }
    
        /**
         * 
         */
        public function set horizontalGap(value:Number):void
        {
            _horizontalGap = value;
            gapsChanged = true;
            invalidateProperties();
        }
    
        public function get horizontalGap():Number
        {
            return _horizontalGap;
        }
    
        private var _verticalGap:Number;
    
        private var _horizontalGap:Number;
    
        private var gapsChanged:Boolean;
    
        //--------------------------------------------------------------------------
        //
        //  Overridden methods
        //
        //--------------------------------------------------------------------------
    
        protected function updatePagedData():void
        {
            if (_originalDataProvider)
            {
                var pagedData:IList = createPagedData(_originalDataProvider);
                super.dataProvider = pagedData;
                invalidateProperties();
            }
        }
    
        private function createPagedData(value:IList):IList
        {
            var nestedData:Array = [];
            var dataList:Array = value.toArray();
            var pageData:PageItemVo;
            if (itemsPerPage)
            {
                var customTilesCount:int = customTilesData ? customTilesData.length : 0;
                var normalItemsPerPage:int = itemsPerPage - customTilesCount;
                while (dataList.length)
                {
                    pageData = new PageItemVo();
                    var data:Array = dataList.splice(0, normalItemsPerPage);
                    for (var i:int = 0 ; i < customTilesCount ; i++)
                    {
                        data.push( customTilesData[i] );
                    }
                    pageData.data = new ArrayCollection( data );
                    pageData.colsCount = pageColCount;
                    pageData.rowsCount = pageRowCount;
                    pageData.itemWidth = innerItemWidth
                    pageData.itemHeight = innerItemHeight;
                    pageData.horizontalGap = horizontalGap;
                    pageData.verticalGap = verticalGap;
                    pageData.innerItemRenderer = _innerItemRenderer;
                    nestedData.push(pageData);
                }
            }
            return new ArrayCollection(nestedData);     
        }   
    
        //----------------------------------
        //  Component lifecycle
        //----------------------------------    
    
        override protected function commitProperties():void
        {
            if (gapsChanged || innerItemRendererChanged || customTilesDataChanged)
            {
                updatePagedData();
                gapsChanged = false;
                innerItemRendererChanged = false;
                customTilesDataChanged = false;
            }       
        }
    
        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
        {
            super.updateDisplayList(unscaledWidth, unscaledHeight);
            caluclateItemsPerPage(unscaledWidth, unscaledHeight);
            // We have to update the dataProvider so it can re-slice the pages 
            // in case of orientation rotation or some other resize
            if (_originalDataProvider)
                if ( cachedUnscaledWidth != unscaledWidth || cachedUnscaledHeight != unscaledHeight )
                    dataProvider = _originalDataProvider;
    
            cachedUnscaledWidth = unscaledWidth;
            cachedUnscaledHeight = unscaledHeight;
        }
    
        protected function caluclateItemsPerPage(unscaledWidth:Number, unscaledHeight:Number):void
        {
            var myLayout:HorizontalLayout = HorizontalLayout(layout);
            var horizontalPaddings:Number = myLayout.paddingLeft + myLayout.paddingRight;
            var verticalPaddings:Number = myLayout.paddingTop + myLayout.paddingRight;
            pageRowCount = (unscaledHeight - verticalPaddings) / (innerItemHeight + verticalGap);
            pageColCount = (unscaledWidth - horizontalPaddings) / (innerItemWidth + horizontalGap);
            itemsPerPage = pageRowCount * pageColCount; 
        }   
    
    
    }
    }
    

    PageItemRenderer

    package skins.pagedList
    {
        import components.pagedList.vo.PageItemVo;
    
        import flash.display.DisplayObject;
    
        import mx.collections.ArrayCollection;
        import mx.core.IDataRenderer;
        import mx.core.IFactory;
        import mx.core.UIComponent;
        import mx.events.FlexEvent;
    
        import spark.components.IItemRenderer;
    
    
    
    //--------------------------------------
    //  Events
    //--------------------------------------
    
    /**
     *  Dispatched when the <code>data</code> property changes.
     *
     *  <p>When you use a component as an item renderer,
     *  the <code>data</code> property contains the data to display.
     *  You can listen for this event and update the component
     *  when the <code>data</code> property changes.</p>
     * 
     *  @eventType mx.events.FlexEvent.DATA_CHANGE
     */
    [Event(name="dataChange", type="mx.events.FlexEvent")]  
    
    
    
    
    
    /**
     * 
     * ASDoc comments for this item renderer class
     * 
     */
    public class PageItemRenderer extends UIComponent
        implements IDataRenderer, IItemRenderer
    {
    
    
    
    
    
        //--------------------------------------------------------------------------
        //
        //  Constructor
        //
        //--------------------------------------------------------------------------        
    
        public function PageItemRenderer()
        {
            super();
            cacheAsBitmap = true;
        }   
    
    
    
        //--------------------------------------------------------------------------
        //
        //  Variables 
        //
        //--------------------------------------------------------------------------
    
        //----------------------------------
        //  data
        //----------------------------------
    
        /**
         *  @private
         */
        private var _data:PageItemVo;
    
        private var dataChanged:Boolean;
    
        [Bindable("dataChange")]
    
        /**
         *  The implementation of the <code>data</code> property
         *  as defined by the IDataRenderer interface.
         *  When set, it stores the value and invalidates the component 
         *  to trigger a relayout of the component.
         */
        public function get data():Object
        {
            return _data;
        }
    
        /**
         *  @private
         */
        public function set data(value:Object):void
        {
            _data = PageItemVo(value);
            colCount = _data.colsCount;
            rowCount = _data.rowsCount;
            itemWidth = _data.itemWidth;
            itemHeight = _data.itemHeight;
            horizontalGap = _data.horizontalGap;
            verticalGap = _data.verticalGap;
            innerItemRenderer = _data.innerItemRenderer;
            _tilesData = ArrayCollection(_data.data);
    
            dataChanged = true;
    
            if (hasEventListener(FlexEvent.DATA_CHANGE))
                dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
    
            invalidateProperties();
        }   
    
        //----------------------------------
        //  gaps
        //----------------------------------    
    
        /**
         * 
         */
        public function set verticalGap(value:Number):void
        {
            _verticalGap = value;
            gapsChanged = true;
            invalidateProperties();
        }
    
        public function get verticalGap():Number
        {
            return _verticalGap;
        }
    
        /**
         * 
         */
        public function set horizontalGap(value:Number):void
        {
            _horizontalGap = value;
            gapsChanged = true;
            invalidateProperties();
        }
    
        public function get horizontalGap():Number
        {
            return _horizontalGap;
        }   
    
        private var _verticalGap:Number = 5;    
    
        private var _horizontalGap:Number = 5;
    
        private var gapsChanged:Boolean;
    
    
        //----------------------------------
        //  itemIndex
        //----------------------------------
    
        private var _itemIndex:int;
    
        public function get itemIndex():int
        {
            return _itemIndex;
        }
    
        public function set itemIndex(value:int):void
        {
            if (value == _itemIndex)
                return;
    
            invalidateDisplayList();
            _itemIndex = value;
        }       
    
        //----------------------------------
        //  showsCaret
        //----------------------------------
    
        private var _showsCaret:Boolean = false;
    
        public function get showsCaret():Boolean
        {
            return _showsCaret;
        }
    
        /**
         *  @private
         */    
        public function set showsCaret(value:Boolean):void
        {
            if (value == _showsCaret)
                return;
    
            _showsCaret = value;
            invalidateDisplayList();
        }
    
    
        //----------------------------------
        //  selected
        //----------------------------------
    
        private var _selected:Boolean = false;
    
        public function get selected():Boolean
        {
            return _selected;
        }
    
        public function set selected(value:Boolean):void
        {
            if (value == _selected)
                return;
    
            _selected = value; 
        }
    
    
        //----------------------------------
        //  dragging
        //----------------------------------
    
        private var _dragging:Boolean = false;
    
        public function get dragging():Boolean
        {
            return _dragging;
        }
    
        public function set dragging(value:Boolean):void
        {
            _dragging = value;
        }   
    
        //----------------------------------
        //  label
        //----------------------------------
    
        private var _label:String;
    
        public function get label():String
        {
            return _label;
        }
    
        /**
         *  @private  
         */
        public function set label(value:String):void
        {
            _label = value;
        }       
    
        //----------------------------------
        //  item properties
        //----------------------------------
    
        /**
         * Dimentions for the inner items should be specifyed because 
         * this component needs to know how many items can be placed in 
         * one page, but don't have the ability to create this items.
         * Default value is 1 to avoid division by zero.
         */
        public var itemWidth:Number = 1;
    
        public var itemHeight:Number = 1;
    
        /**
         * When inner item dimentions are set the component calculates how many items 
         * (per row and col) can be shown on one scren and pass these data to the page 
         * trough the dataProvider for the page.
         */
        public var rowCount:int = 1;
    
        public var colCount:int = 1;        
    
        private var _tilesData:ArrayCollection;
    
        private var sizeChanged:Boolean;
    
        private var _tileContainer:UIComponent;
    
        /**
         * 
         */
        private var innerItemRenderer:IFactory;
    
        //--------------------------------------------------------------------------
        //
        //  Overridden methods
        //
        //--------------------------------------------------------------------------
    
        //----------------------------------
        //  Component lifecycle
        //----------------------------------        
    
        override protected function commitProperties():void
        {
            super.commitProperties();
    
            if (dataChanged)
            {
                dataChanged = false;
                createTiledContent();
            }
            if (gapsChanged)
            {
                createTiledContent();
            }
        }
    
        override protected function measure():void
        {
            super.measure();
    
            measuredMinHeight = measuredWidth =  parent.width;
            measuredMinHeight = measuredHeight = parent.height;
        }   
    
        override protected function updateDisplayList(unscaledWidth:Number,
                                                      unscaledHeight:Number):void
        {
            super.updateDisplayList(unscaledWidth, unscaledHeight);
            var tileCntWidth:Number = colCount * (itemWidth + horizontalGap);
            _tileContainer.x = 0.5 * (unscaledWidth - tileCntWidth);
        }
    
    
    
    
    
    
        //--------------------------------------------------------------------------
        //
        //  Methods
        //
        //--------------------------------------------------------------------------    
    
        protected function createTiledContent():void
        {
            clearChildren();
            _tileContainer = new UIComponent();
            var itemsCount:int = _tilesData.length;
            var row:int, col:int, item:DisplayObject;
            for (var i:int = 0 ; i < itemsCount ; i++ )
            {
                row = int( i / colCount );
                col = i - row * colCount;
                if (_tilesData[i].hasOwnProperty("itemFactory"))
                {
                    item = IFactory(_tilesData[i].itemFactory).newInstance();
                }
                else
                {
                    item =  innerItemRenderer.newInstance();
                }
                Object(item).data = _tilesData[i];
                item.x = col * (itemWidth + horizontalGap);
                item.y = row * (itemHeight + verticalGap);
                _tileContainer.addChild(item);
            }
            addChild(_tileContainer);
            invalidateSize();
            invalidateDisplayList();
        }
    
        //--------------------------------------------------------------------------
        //
        //  Helper methods
        //
        //--------------------------------------------------------------------------    
    
        private function clearChildren():void
        {
            var numChildren:int = this.numChildren;
            for (var i:int = 0 ; i < numChildren ; i++ )
            {
                this.removeChildAt(0);
            }
        }       
    
    }
    }
    

    PagedItemVo

    package components.pagedList.vo
    {
        import flash.events.EventDispatcher;
        import flash.events.IEventDispatcher;
    
        import mx.collections.IList;
        import mx.core.IFactory;
    
        [Bindable]
        public class PageItemVo extends EventDispatcher
        {
    
            public var data:IList;
    
            public var rowsCount:int;
    
            public var colsCount:int;
    
            public var itemWidth:Number;
    
            public var itemHeight:Number;
    
            public var verticalGap:Number;
    
            public var horizontalGap:Number;
    
            public var innerItemRenderer:IFactory;
    
            public function PageItemVo(target:IEventDispatcher=null)
            {
                super(target);
            }
        }
    }
    

    ScrollingListSkin

    package skins.pagedList
    {
        import flash.display.Graphics;
        import flash.display.Sprite;
        import flash.events.MouseEvent;
    
        import mx.core.DPIClassification;
        import mx.core.ScrollPolicy;
        import mx.events.FlexEvent;
        import mx.events.TouchInteractionEvent;
    
        import spark.events.IndexChangeEvent;
        import spark.events.RendererExistenceEvent;
        import spark.skins.mobile.ListSkin;
        import spark.skins.mobile.supportClasses.MobileSkin;
    
        public class ScrollingListSkin extends ListSkin
        {
            private var pageIndicator:Sprite;
            private var indicatorSize:uint;
            private var _isHorizontal:Boolean;
            private var _suspendPageIndicatorShortcut:Boolean;
    
            public function ScrollingListSkin()
            {
                super();
    
                switch (applicationDPI)
                {
                    case DPIClassification.DPI_320:
                    {
                        indicatorSize = 32;
                        break;
                    }
                    case DPIClassification.DPI_240:
                    {
                        indicatorSize = 24;
                        break;
                    }
                    default:
                    {
                        indicatorSize = 16;
                        break;
                    }
                }
            }
    
            //--------------------------------------------------------------------------
            //
            //  Overridden methods
            //
            //--------------------------------------------------------------------------
    
            override protected function createChildren():void
            {
                super.createChildren();
    
                scroller.setStyle("skinClass", PagedListScrollerSkin);
    
                // page indicator
                pageIndicator = new Sprite();
    
                // TODO (jasonsj): extend pageIndicator hit area to use the entire 
                // width/height of the List as a shortcut. Currently this only works
                // in the tiny area where the indicators are.
                //pageIndicator.addEventListener(MouseEvent.MOUSE_DOWN, pageIndicaterMouseHandler);
                //pageIndicator.addEventListener(MouseEvent.MOUSE_UP, pageIndicaterMouseHandler);
                //pageIndicator.addEventListener(MouseEvent.MOUSE_MOVE, pageIndicaterMouseHandler);
    
                addChild(pageIndicator);
    
                // listen for changes to the list
                dataGroup.addEventListener(FlexEvent.UPDATE_COMPLETE, dataGroupUpdateComplete);
                scroller.addEventListener(TouchInteractionEvent.TOUCH_INTERACTION_START, touchInteractionStart);
                scroller.addEventListener(TouchInteractionEvent.TOUCH_INTERACTION_END, positionChanged);
            }
    
            override protected function commitProperties():void
            {
                super.commitProperties();
    
                // isHorizontal
                /*var hScrollPolicy:Boolean = getStyle("horizontalScrollPolicy") == ScrollPolicy.ON;
                var vScrollPolicy:Boolean = getStyle("verticalScrollPolicy") == ScrollPolicy.ON;
                _isHorizontal = hScrollPolicy && !vScrollPolicy;*/
                _isHorizontal = true;
            }
    
            override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void
            {
                super.drawBackground(unscaledWidth, unscaledHeight);
    
                var pos:Number = (isHorizontal) ? scroller.viewport.horizontalScrollPosition :
                    scroller.viewport.verticalScrollPosition;
                var viewportSize:Number = (isHorizontal) ? scroller.viewport.width :
                    scroller.viewport.height;
    
                var selectedIndex:int = Math.round(pos / viewportSize);
                var numElements:int = dataGroup.numElements;
    
                var g:Graphics = pageIndicator.graphics;
                g.clear();
    
                // if we have only one page there shouldn't be any scrollbar visuals
                if (numElements == 1) return;
    
                var axisPos:Number = 0;
                var centerPos:Number = indicatorSize / 2;
                var radius:Number = indicatorSize / 4;
                //TODO: make so the color could be specifyed outisde 
                var selectionColor:Number = 0x000000; //getStyle("selectionColor");
    
    
                //var elementsPerPage:int = Math.floor(unscaledWidth/FileIconItemRenderer.ITEM_WIDTH) * Math.floor(unscaledHeight/FileIconItemRenderer.ITEM_HEIGHT);
    
                for (var i:uint = 0; i < numElements; i++)
                {
                    if (i == selectedIndex)
                        g.beginFill(selectionColor, 1);
                    else
                        g.beginFill(0, .25);
    
                    if (isHorizontal)
                        g.drawCircle(axisPos + centerPos, centerPos, radius);
                    else
                        g.drawCircle(centerPos, axisPos + centerPos, radius);
    
                    g.endFill();
    
                    axisPos += indicatorSize;
                }
    
                var pageIndicatorX:Number = (isHorizontal) ? (unscaledWidth - axisPos) / 2 :
                    unscaledWidth - (indicatorSize * 1.5);
                var pageIndicatorY:Number = (isHorizontal) ? unscaledHeight - (indicatorSize * 1.5):
                    (unscaledHeight - axisPos) / 2;
    
                setElementPosition(pageIndicator, Math.floor(pageIndicatorX), Math.floor(pageIndicatorY));
            }
    
            override public function styleChanged(styleProp:String):void
            {
                super.styleChanged(styleProp);
    
                var allStyles:Boolean = !styleProp || styleProp == "styleName";
    
                if (allStyles || styleProp == "horizontalScrollPolicy" ||
                    styleProp == "verticalScrollPolicy")
                {
                    invalidateProperties();
                    invalidateDisplayList();
                }
            }
    
            private function get isHorizontal():Boolean
            {
                return _isHorizontal;
            }
    
            //--------------------------------------------------------------------------
            //
            //  Event Handlers
            //
            //--------------------------------------------------------------------------
    
            private function dataGroupUpdateComplete(event:FlexEvent):void
            {
                invalidateDisplayList();
            }
    
            private function touchInteractionStart(event:TouchInteractionEvent):void
            {
                _suspendPageIndicatorShortcut = true;
            }
    
            private function positionChanged(event:TouchInteractionEvent):void
            {
                invalidateDisplayList();
                _suspendPageIndicatorShortcut = false;
            }
    
            private function pageIndicaterMouseHandler(event:MouseEvent):void
            {
                event.preventDefault();
    
                if (_suspendPageIndicatorShortcut)
                    return;
    
                // Mouse events on the pageIndicator sprite will jump to the selected page
                var pos:Number = (isHorizontal) ? event.localX : event.localY;
                var size:Number = (isHorizontal) ? pageIndicator.width : pageIndicator.height;
    
                pos = Math.min(Math.max(pos, 0), size) - (indicatorSize / 2);
    
                var viewportSize:Number = (isHorizontal) ? scroller.viewport.width : scroller.viewport.height;
                viewportSize = viewportSize * dataGroup.numElements;
    
                var viewportPosition:Number = (pos / size) * viewportSize;
    
                if (isHorizontal)
                    scroller.viewport.horizontalScrollPosition = viewportPosition;
                else
                    scroller.viewport.verticalScrollPosition = viewportPosition;
            }
        }
    }
    

    示例:

    <pagedList:PagedList id="displayList" 
                     width="100%" height="100%"
                     itemRenderer="skins.pagedList.PageItemRenderer"
                     innerItemRenderer="components.pagedList.tiles.FolderTileItem"
                     innerItemWidth="146" innerItemHeight="150"
                     skinClass="skins.pagedList.ScrollingListSkin"
                     verticalScrollPolicy="off"
                     horizontalScrollPolicy="on"
                     pageScrollingEnabled="true"
                     horizontalGap="15" verticalGap="20"
                     contentBackgroundAlpha="0"
                     selectionColor="0xFFFFFF">
    <pagedList:layout>
        <s:HorizontalLayout columnWidth="{displayList.width}" 
                            variableColumnWidth="false" 
                            gap="0"
                            paddingTop="30"/>
    </pagedList:layout>
    

    很遗憾,我无法与您分享 FolderTileItem 源代码,但它是一个扩展 Sprite 的简单组件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-21
      • 1970-01-01
      • 2014-10-06
      • 2012-11-05
      • 2013-07-31
      • 2011-07-03
      • 2017-04-13
      相关资源
      最近更新 更多