写真の通り(これはPhotoShopで描いたもので、まだ実装されていません)、List
このようなものを実装したいと思います。列数が異なります。たとえば、最初の行にはアイテムが 1 つしかなく、他の行にはアイテムが 2 つあるとします。別のアイテムを検出するために使用しようとしましitemRendererFunction
た(最初の行はrendererAとして扱い、他の行は別のrendererBとして扱います)が、うまくいきませんでした。
2 に答える
この問題に対する最もクリーンな解決策は、カスタム レイアウトを作成することです (コメントで、Romi の解決策が最終的に非常に多くの問題を引き起こすことについて説明しました)。ただし、これは通常、簡単なことではありません。
このカスタム レイアウトがどのように見えるかについて大まかなスケッチを示しますので、これを出発点として使用して、まさに必要なことを行うレイアウトを作成してください。カスタム レイアウトを作成するには、そのおよびメソッドをサブクラス化BaseLayout
し、オーバーライドして実装する必要があります。updateDisplayList
measure
簡単にするために (そしてここに 500 行のコードをダンプしないようにするために)、この例ではハードコードされた変数をいくつか使用しました。常に 2 つの列があり、最初のアイテムは常に 200x200 ピクセルで、他のアイテムは常に 100x100 ピクセルであると想定しています。horizontalGap または verticalGap はありません。
その結果、もちろん、この特定の List とこれらの特定の 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>
既存のコンポーネントの定義済みの動作を変更する場合は常に、最初にスキニングで問題を解決できるかどうかを確認してください。これは Flex の非常に強力な機能であり、この場合のソリューションも提供できます。
それでは始めましょう。すでにリストがあると仮定して、データ プロバイダーを 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();
}
追加されたのは、「if」ブロックと、dataGroupProvider という名前のプライベート変数だけでした。これは、updateDisplayList() メソッドで、2 番目の要素から始まる新しい dataProvider を dataGroup 要素に設定するためです。これは次のようになります。
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);
}
結論として、List 要素のカスタム スキンを作成するだけで、残りの要素とは異なる方法で最初の項目をレンダリングするために 2 つのコンテナーを使用できます。Flex Skinning の力を過小評価してはいけません :)
お役に立てれば。すてきな一日を!