List虚拟布局

<s:Application xmlns:fx=" http://ns.adobe.com/mxml/2009 " xmlns:s=" library://ns.adobe.com/flex/spark " xmlns:local=" * " width=" 440 " viewSourceURL=" srcview/index.html " > <fx:Script> <![CDATA[ import mx . collections . ArrayList; private function createItems ( n: int ): ArrayList { var arr: Array = new Array ( ); for ( var i: int = 0; i < n; i ++ ) { arr . push ( i ); } return new ArrayList ( arr ); } ] ] > </fx:Script> <s:layout> <s:VerticalLayout horizontalAlign=" center " paddingTop=" 10 " /> </s:layout> <s:Label text=" useVirtualLayout = false, 20 items " fontWeight=" bold " fontSize=" 16 " /> <s:List id=" notVirtual " itemRenderer=" SimpleRenderer " width=" 400 " height=" 116 " useVirtualLayout=" false " dataProvider=" { createItems (20 ) } " > <s:layout> <local:SimpleHorizontalLayout /> </s:layout> </s:List> <s:Label text=" useVirtualLayout = true, 1000 items " fontWeight=" bold " fontSize=" 16 " /> <s:List id=" virtual " itemRenderer=" SimpleRenderer " width=" 400 " height=" 116 " useVirtualLayout=" true " dataProvider=" { createItems (1000 ) } " > <s:layout> <local:SimpleHorizontalLayout /> </s:layout> </s:List> </s:Application>

 

package
{
import flash.geom.Rectangle;
import mx.core.ILayoutElement;
import spark.components.supportClasses.GroupBase;
import spark.core.NavigationUnit;
import spark.layouts.supportClasses.LayoutBase;
public class SimpleHorizontalLayout extends LayoutBase
{
private var minEligibleScrollPosition:Number = 0;
private var maxEligibleScrollPosition:Number = 0;
/**
*  Called when the user clicks on the left scroll button or the left part of the track of the scrollbar
*/
override protected function getElementBoundsLeftOfScrollRect(scrollRect:Rectangle):Rectangle
{
var offset:Number = horizontalScrollPosition % typicalLayoutElement.getLayoutBoundsWidth();
var width:Number = typicalLayoutElement.getLayoutBoundsWidth();
var height:Number = typicalLayoutElement.getLayoutBoundsHeight();
return new Rectangle(scrollRect.x - width + offset,scrollRect.y,width,height);
}
/**
*  Called when the user clicks on the right scroll button or the right part of the track of the scrollbar
*/
override protected function getElementBoundsRightOfScrollRect(scrollRect:Rectangle):Rectangle
{
var offset:Number = horizontalScrollPosition % typicalLayoutElement.getLayoutBoundsWidth();
var width:Number = typicalLayoutElement.getLayoutBoundsWidth();
var height:Number = typicalLayoutElement.getLayoutBoundsHeight();
return new Rectangle(scrollRect.right + width - offset,scrollRect.y,width,height);
}
/**
* TODO: This works with getNavigationDestinationIndex() for keyboard scrolling a List based target.
*
* Given this layout's assumptions this method easily handles both the virtual and non-virtual case
* since an element's position can be calculated solely by its index and the typicalLayoutElement width
*
*/
override public function getElementBounds(index:int):Rectangle
{
if (!target)
return null;
if ((index < 0) || (index >= target.numElements))
return null;
var eltX:Number = typicalLayoutElement.getLayoutBoundsWidth() * index;
var eltY:Number = 0
var eltW:Number = typicalLayoutElement.getLayoutBoundsWidth()
var eltH:Number = typicalLayoutElement.getLayoutBoundsHeight();
return new Rectangle(eltX, eltY, eltW, eltH);
}
/**
*
* This method is used by subclasses of ListBase to handle keyboard navigation. Given a current index
* (the index that is currently focused) this method returns the index to navigate to given a keyboard
* navigation command like LEFT/RIGHT/HOME/END.
*
*/
override public function getNavigationDestinationIndex(currentIndex:int, navigationUnit:uint, arrowKeysWrapFocus:Boolean):int
{
if (!target || target.numElements < 1)
return -1;
// When the user hits the PAGE keys we need to decide how many indices it should skip
            // forward or backwards, for this simple layout we just skip by roughly the number of items in view
            var indicesPerPage:int = target.getLayoutBoundsWidth() / typicalLayoutElement.getLayoutBoundsWidth();
switch (navigationUnit)
{
// the first index
                case NavigationUnit.HOME:
return 0;
// the last index
                case NavigationUnit.END:
return target.numElements - 1;
// one less than the currrent index
                case NavigationUnit.LEFT:
return Math.max(0, currentIndex - 1);
// one more than the current index
                case NavigationUnit.RIGHT:
return Math.min(target.numElements - 1, currentIndex + 1);
// a few less than the current index
                case NavigationUnit.PAGE_UP:
case NavigationUnit.PAGE_LEFT:
return Math.max(0, currentIndex - indicesPerPage);
// a few more than the current index
                case NavigationUnit.PAGE_DOWN:
case NavigationUnit.PAGE_RIGHT:
return Math.min(target.numElements - 1, currentIndex + indicesPerPage);
default:
return -1;
}
}
override public function measure():void
{
if (!target)
return;
if (useVirtualLayout)
measureVirtual(target);
else
measureReal(target);
// Use Math.ceil() to make sure that if the content partially occupies
            // the last pixel, we'll count it as if the whole pixel is occupied.
            target.measuredWidth = Math.ceil(target.measuredWidth);
target.measuredHeight = Math.ceil(target.measuredHeight);
target.measuredMinWidth = Math.ceil(target.measuredMinWidth);
target.measuredMinHeight = Math.ceil(target.measuredMinHeight);
}
/**
*  Compute potentially approximate values for measuredWidth,Height and
*  measuredMinWidth,Height.
*
*  This method does not get layout elements from the target except
*  as a side effect of calling typicalLayoutElement.
*
*  It assumes all elements are of equal size and equal to the size of the
*  typicalLayoutElement.
*/
private function measureVirtual(layoutTarget:GroupBase):void
{
var eltCount:uint = layoutTarget.numElements;
layoutTarget.measuredWidth = (eltCount * typicalLayoutElement.getLayoutBoundsWidth());
layoutTarget.measuredHeight = typicalLayoutElement.getLayoutBoundsHeight();
layoutTarget.measuredMinWidth = layoutTarget.measuredWidth;
layoutTarget.measuredMinHeight = layoutTarget.measuredHeight;
}
/**
*  Compute exact values for measuredWidth,Height and measuredMinWidth,Height.
*
*  Measure each of the layout elements. We then only
*  consider the height of the elements remaining.
*/
private function measureReal(layoutTarget:GroupBase):void
{
var preferredHeight:Number = 0; // max of the elements' preferred widths
            var preferredWidth:Number = 0;  // sum of the elements' preferred heights
            var minHeight:Number = 0;       // max of the elements' minimum widths
            var minWidth:Number = 0;        // sum of the elements' minimum heights

var element:ILayoutElement;
for (var i:int = 0; i < layoutTarget.numElements; i++)
{
element = layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
// Consider the size of each element
                preferredWidth += element.getPreferredBoundsWidth();
preferredHeight = Math.max(preferredHeight, element.getPreferredBoundsHeight());
minWidth += element.getMinBoundsHeight();
minHeight = Math.max(minHeight, element.getMinBoundsHeight());
}
layoutTarget.measuredHeight = preferredHeight;
layoutTarget.measuredWidth = preferredWidth;
layoutTarget.measuredMinHeight = minHeight;
layoutTarget.measuredMinWidth  = minWidth;
}
override public function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (!target)
return;
if (!typicalLayoutElement)
return;
if (target.numElements <= 0)
return;
if (useVirtualLayout)
updateDisplayListVirtual(unscaledWidth, unscaledHeight);
else
updateDisplayListReal(unscaledWidth, unscaledHeight);
}
/**
* Gets called by updateDisplayList when useVirtualLayout = true
*
* NOTE: Assumes all elements are of equal size and equal to the typicalLayoutElement
*/
private function updateDisplayListVirtual(containerWidth:Number, containerHeight:Number):void {
// Step 1: Figure out what we are given:
            //      - target.horizontalScrollPosition
            //      - target.width
            //      - typicalLayoutElement.getLayoutBoundsWidth();

var firstIndexInView:int = 0;
var firstIndexInViewOffset:Number = 0;
var lastIndexInView:int = 0;
var numIndicesInView:int = 0;
// Step 2: Given the scroll position, figure out the first index that should be in view
            // keep track of the remainder in case the first index is only partially in view

firstIndexInView = target.horizontalScrollPosition / typicalLayoutElement.getLayoutBoundsWidth();
firstIndexInViewOffset = target.horizontalScrollPosition % typicalLayoutElement.getLayoutBoundsWidth();
// Step 3: Figure out how many indices are in view

numIndicesInView = Math.ceil(target.width / typicalLayoutElement.getLayoutBoundsWidth());
// Step 4: Figure out the last index in view

lastIndexInView = Math.min(firstIndexInView + numIndicesInView, target.numElements - 1);
// Step 5: Figure out what coordinates to position the first index at

var x:Number = target.horizontalScrollPosition - firstIndexInViewOffset;
var y:Number = 0;
// Step 6: Position the renderer of each index that is in view using getVirtualElementAt

//  loop through each index that is in view
            for (var k:int = firstIndexInView; k <= lastIndexInView; k++)
{
// WARNING: getVirtualElementAt() should NEVER be called outside of
                // the updateDisplayList() method of the layout
                var element:ILayoutElement = target.getVirtualElementAt(k);
// position the element
                element.setLayoutBoundsPosition(x, y);
// resize the element to its preferred size by passing
                // NaN for the width and height constraints
                element.setLayoutBoundsSize(NaN, NaN);
// find the size of the element
                var elementWidth:Number = element.getLayoutBoundsWidth();
var elementHeight:Number = element.getLayoutBoundsHeight();
// update the target's contentWidth and contentHeight
                target.setContentSize(Math.ceil(typicalLayoutElement.getLayoutBoundsWidth() * target.numElements),
Math.ceil(Math.max(elementHeight, target.contentHeight)));
// update the x position for where to place the next element
                x += elementWidth;
}
//
            // Step 7: Keep track of the extent of the renderers that are partially in view
            //
            // ie: how much they stick out of view. Keeping track of that allows us to call
            // invalidateDisplayList() less in scrollPositionChanged().
            //

minEligibleScrollPosition = target.horizontalScrollPosition;
maxEligibleScrollPosition = minEligibleScrollPosition + target.width;
// now subtract the left offset
            minEligibleScrollPosition -= firstIndexInViewOffset;
// and add the right offset
            maxEligibleScrollPosition += typicalLayoutElement.getLayoutBoundsWidth() - firstIndexInViewOffset;
}
/**
* Gets called by updateDisplayList when useVirtualLayout = false
*/
private function updateDisplayListReal(containerWidth:Number, containerHeight:Number):void {
// the x value to position the next element at
            var x:Number = 0;
// the y value to position the next element at
            var y:Number = 0;
// loop through every element (even those not in view)
            for (var k:int = 0; k < target.numElements; k++)
{
var element:ILayoutElement = target.getElementAt(k);
// position the element
                element.setLayoutBoundsPosition(x, y);
// resize the element to its preferred size by passing
                // NaN for the width and height constraints
                element.setLayoutBoundsSize(NaN, NaN);
// find the size of the element
                var elementWidth:Number = element.getLayoutBoundsWidth();
var elementHeight:Number = element.getLayoutBoundsHeight();
// update the layoutTarget's contentWidth and contentHeight
                target.setContentSize(Math.ceil(x + elementWidth),
Math.ceil(Math.max(elementHeight, target.contentHeight)));
// update the x position for where to place the next element
                x += elementWidth;
}
}
/**
* In a virtual layout you need to invalidate the display list if the user has
* scrolled by a large enough distance to expose items that don't currently
* have renderers created.  We keep track of this range of scroll positions
* using minEligibleScrollPosition/maxEligibleScrollPosition.
*/
override protected function scrollPositionChanged():void
{
super.scrollPositionChanged();
if (!target)
return;
if (useVirtualLayout){
// an optimization: only invalidate the display list if the scroll position
                // has changed enough that new items are coming into view

if (horizontalScrollPosition < minEligibleScrollPosition ||
horizontalScrollPosition > maxEligibleScrollPosition - target.width){
target.invalidateDisplayList();
}
}
}
}
}

<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
width="50" height="100">
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
<s:State name="selected" />
<s:State name="normalAndShowsCaret" stateGroups="caret" />
<s:State name="hoveredAndShowsCaret" stateGroups="caret" />
<s:State name="selectedAndShowsCaret" stateGroups="caret" />
</s:states>
<fx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
if (data == null){
return;
}
color.color = Number(data) % 2 == 0 ? 0x000066 : 0x000033;
txt.text = String(data);
}
]]>
</fx:Script>
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor id="color"/>
</s:fill>
<s:stroke>
<s:SolidColorStroke color.normal="black"
color.hovered="blue"
color.selected="red"
color.normalAndShowsCaret="black"
color.hoveredAndShowsCaret="blue"
color.selectedAndShowsCaret="red" weight="2"/>
</s:stroke>
</s:Rect>
<s:Label color="white" id="txt" horizontalCenter="0" verticalCenter="0" />
</s:ItemRenderer>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值