不同的列数列表(List with different column count)

2019-09-21 02:07发布

由于PIC节目(这是通过PhotoShop的绘制,尚未实现),我想implemnt一个List像这样的。 它有不同的列数,比方说第一排只有一个项目,其他的则有两个项目。 我试图用itemRendererFunction检测不同项目(第一行治疗作为rendererA,别人当作另一个rendererB),但没有奏效。

Answer 1:

最干净的解决这个问题,是创建一个自定义布局(我们在评论中讨论了如何罗米的解决方案最终会导致太多的问题)。 然而,这通常不是一件容易的事。

我会给你这是什么自定义布局可能看起来像一个草图,所以你可以使用它作为一个出发点,以创建一个没有你需要的东西。 要创建一个自定义布局,你必须继承BaseLayout和重写和实现其updateDisplayListmeasure方法。

为了使事情变得容易多了(为了不倾倒在这里500行代码),我使用了一些硬编码变量的这个例子。 它假定总是会有两列,第一个项目将永远是200×200像素,和其他项目将永远是100x100像素。 没有horizo​​ntalGap的或的verticalGap。
其结果当然是,你可以使用这个自定义布局(就像现在)只对这个特定的名单和这些特定的itemRenderer的。 如果你希望它是更通用的,你就必须做更多的计算。

但是,现在的代码:

public class MyCustomLayout extends LayoutBase {

    //hardcoded variables
    private var columnCount:int = 2;

    private var bigTileWidth:Number = 200;
    private var bigTileHeight:Number = 200;
    private var smallTileWidth:Number = 100;
    private var smallTileHeight:Number = 100;

    override public function updateDisplayList(width:Number, height:Number):void {
        var layoutTarget:GroupBase = target;
        if (!layoutTarget) return;

        var numElements:int = layoutTarget.numElements;
        if (!numElements) return;

        //position and size the first element
        var el:ILayoutElement = useVirtualLayout ? 
            layoutTarget.getVirtualElementAt(0) : layoutTarget.getElementAt(0);
        el.setLayoutBoundsSize(bigTileWidth, bigTileHeight);
        el.setLayoutBoundsPosition(0, 0);

        //position & size the other elements in 2 columns below the 1st element
        for (var i:int=1; i<numElements; i++) {
            var x:Number = smallTileWidth  * ((i-1) % 2);
            var y:Number = smallTileHeight * Math.floor((i-1) / 2) + bigTileHeight;

            el = useVirtualLayout ? 
                layoutTarget.getVirtualElementAt(i) : 
                layoutTarget.getElementAt(i);
            el.setLayoutBoundsSize(smallTileWidth, smallTileHeight);
            el.setLayoutBoundsPosition(x, y);
        }

        //set the content size (necessary for scrolling)
        layoutTarget.setContentSize(
            layoutTarget.measuredWidth, layoutTarget.measuredHeight
        );
    }

    override public function measure():void {
        var layoutTarget:GroupBase = target;
        if (!layoutTarget) return;

        var rowCount:int = Math.ceil((layoutTarget.numElements - 1) / 2);

        //measure the total width and height
        layoutTarget.measuredWidth = layoutTarget.measuredMinWidth = 
            Math.max(smallTileWidth * columnCount, bigTileWidth);
        layoutTarget.measuredHeight = layoutTarget.measuredMinHeight = 
            bigTileHeight + smallTileHeight * rowCount;
    }

}

你可以使用它像这样:

<s:List dataProvider="{dp}" height="300">
    <s:layout>
        <l:MyCustomLayout />
    </s:layout>
</s:List>


Answer 2:

每当你想改变定义现有组件的行为,总是先检查一下,如果你能剥皮解决问题。 这是一个非常强大的功能,我的弯曲,也可以提供在这种情况下的解决方案。

所以,让我们开始吧,假设你已经在你的列表,你只需要创建一个自定义皮肤,其“分裂”两个部分数据提供者,第一个项目,和所有其他人。 所以,让我们假设我们有这个初始设置:

<fx:Script>
    <![CDATA[
        import mx.collections.ArrayCollection;

        [Bindable]
        private var c:ArrayCollection = new ArrayCollection([
            "String 1",
            "String 2",
            "String 3",
            "String 4",
            "String 5",
            "String 6",
            "String 7",
            "String 8",
            "String 9",
            "String 10",
            "String 11",
            "String 12",
            "String 13",
            "String 14",
            "String 15"]);
    ]]>
</fx:Script>

<s:List skinClass="CustomSkinList" dataProvider="{c}" />

正如你所看到的,我们定义了一个自定义列表的皮肤,这仅仅是一个副本spark.skins.spark.ListSkin ,默认皮肤spark.components.List元素。

在我们处理数据提供器逻辑,我们需要看看列表项的呈现方式。 这是通过使用一个完成DataGroup元素,添加到皮肤,就像这样:

<s:Scroller left="0" top="0" right="0" bottom="0" id="scroller" minViewportInset="1" hasFocusableChildren="false">
    <!--- @copy spark.components.SkinnableDataContainer#dataGroup -->
    <s:DataGroup id="dataGroup" itemRenderer="spark.skins.spark.DefaultItemRenderer">
        <s:layout>
            <!--- The default layout is vertical and measures at least for 5 rows.  
            When switching to a different layout, HorizontalLayout for example,
            make sure to adjust the minWidth, minHeight sizes of the skin -->
            <s:VerticalLayout gap="0" horizontalAlign="contentJustify" requestedMinRowCount="5" />
        </s:layout>
    </s:DataGroup>
</s:Scroller>

这里我们将不得不做出改变,为了得到呈现不同的第一个元素的地方。 我们需要做的,只是添加另一个DATAGROUP,在自定义的方式呈现的第一个元素(当然,这意味着使用自定义项目渲染)。 现在,我们的滚动条是这样的:

<s:Scroller left="0"
            top="0"
            right="0"
            bottom="0"
            id="scroller"
            minViewportInset="1"
            hasFocusableChildren="false">
    <!--- @copy spark.components.SkinnableDataContainer#dataGroup -->
    <s:VGroup width="100%" height="100%">

        <s:DataGroup id="firstItemDataGroup"
                     width="100%"
                     itemRenderer="CustomItemRenderer"
                     height="20">
            <s:layout>
                <s:VerticalLayout />
            </s:layout>
        </s:DataGroup>

        <s:DataGroup id="dataGroup" itemRenderer="spark.skins.spark.DefaultItemRenderer">
            <s:layout>
                <!--- The default layout is vertical and measures at least for 5 rows.
                When switching to a different layout, HorizontalLayout for example,
                make sure to adjust the minWidth, minHeight sizes of the skin -->
                <s:TileLayout horizontalAlign="center" requestedColumnCount="2" />
            </s:layout>
        </s:DataGroup>
    </s:VGroup>
</s:Scroller>

注意“firstItemDataGroup”另外,还在于它采用了不同的项目渲染器,而不是默认dataGroup时元的事实。 有了这个新的容器,我们可以继续再现元素。 自定义皮肤需要重写父initializationComplete()方法,像这样:

        override protected function initializationComplete():void
        {
            useChromeColor = true;

            if (hostComponent.dataProvider && hostComponent.dataProvider.length > 0)
            {
                var allItems:Array = hostComponent.dataProvider.toArray().concat();

                firstItemDataGroup.dataProvider = new ArrayCollection([hostComponent.dataProvider.getItemAt(0)]);

                var remainingItems:Array = allItems.concat().reverse();
                remainingItems.pop();

                var reversed:Array = remainingItems.reverse();
                dataGroupProvider = new ArrayCollection(reversed);
            }

            super.initializationComplete();
        }

什么加入还只是“如果”块和私有变量,命名dataGroupProvider。 这是因为我们将设置新dataProvider,所述一个从所述第二元素开始,到dataGroup时元件,在updateDisplayList()方法。 这里是什么样子:

        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
        {
            if (getStyle("borderVisible") == true)
            {
                border.visible = true;
                background.left = background.top = background.right = background.bottom = 1;
                scroller.minViewportInset = 1;
            }
            else
            {
                border.visible = false;
                background.left = background.top = background.right = background.bottom = 0;
                scroller.minViewportInset = 0;
            }

            // Here we assign the new data provider to the dataGroup element
            if (dataGroupProvider)
                dataGroup.dataProvider = dataGroupProvider;

            borderStroke.color = getStyle("borderColor");
            borderStroke.alpha = getStyle("borderAlpha");

            super.updateDisplayList(unscaledWidth, unscaledHeight);
        }

总之,只要通过建立我们的列表元素的自定义皮肤,我们可以用两个容器呈现的第一个项目以不同的方式,从元素的其余部分。 你不应该低估的Flex剥皮的力量:)

希望这可以帮助。 祝你有美好的一天!



文章来源: List with different column count