代码之家  ›  专栏  ›  技术社区  ›  Marty Pitt

列表的LayoutBase中xxScrollPosition的用途是什么?

  •  1
  • Marty Pitt  · 技术社区  · 15 年前

    为列表编写自定义布局时,需要考虑在LayoutBase类本身上使用horizontalScrollPosition/verticalScrollPosition属性(与layoutTarget上的相应属性相反)

    看着 HorizontalLayout 类作为示例,它总是引用和更新 layoutTarget.horizontalScrollPosition . LayoutBase类的horizontalScrollPosition是否被引用/使用过?

    澄清: 我真正要问的是:

    2 回复  |  直到 11 年前
        1
  •  0
  •   Eugene    15 年前

    LayoutBase类的horizontalScrollPosition是否被引用/使用过?

    please look! 在代码find horizontalScrollPosition中,至少有3个函数使用此参数。

    如果你能告诉我你到底想用它干什么,那就更好了?

        2
  •  0
  •   Eugene    15 年前

    布局目标

    /**
     *  @private
     *  Work-around the Player globalToLocal and scrollRect changing before
     *  a frame is updated. 
     */
    private function globalToLocal(x:Number, y:Number):Point
    {
        var layoutTarget:GroupBase = target;
        var parent:DisplayObject = layoutTarget.parent;
        var local:Point = parent.globalToLocal(new Point(x, y));
        local.x -= layoutTarget.x;
        local.y -= layoutTarget.y;
    
        var scrollRect:Rectangle = getScrollRect();
        if (scrollRect)
        {
            local.x += scrollRect.x;
            local.y += scrollRect.y;
        }
        return local;
    }
    

    还有这个

    /**
     *  Calculates how much to scroll for the specified <code>dropLocation</code>
     *  during a drag and drop gesture.
     *
     *  Called by the <code>showDropIndicator()</code> method to calculate the scroll 
     *  during drag-scrolling.
     *
     *  @param context A valid DropLocation object previously obtained
     *  by calling the <code>calculateDropLocation()</code> method.
     *
     *  @param elapsedTime The duration, in milliseconds, since the drag scrolling start.
     *
     *  @return How much to drag scroll, or null if drag-scrolling is not needed.
     *
     *  @see spark.layouts.supportClasses.DropLocation 
     *  @see #calculateDropIndex()
     *  @see #calculateDropIndicatorBounds()
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    protected function calculateDragScrollDelta(dropLocation:DropLocation, elapsedTime:Number):Point
    {
        var layoutTarget:GroupBase = target;
        if (layoutTarget.numElements == 0)
            return null;
    
        var scrollRect:Rectangle = getScrollRect();
        if (!scrollRect)
            return null;
    
        // Make sure that the drag-scrolling regions don't overlap 
        var x:Number = dropLocation.dropPoint.x;
        var y:Number = dropLocation.dropPoint.y;
    
        var horizontalRegionSize:Number = Math.min(dragScrollRegionSizeHorizontal, layoutTarget.width/2);
        var verticalRegionSize:Number = Math.min(dragScrollRegionSizeVertical, layoutTarget.height/2);
        // Return early if the mouse is outside of the drag-scroll region.
        if (scrollRect.left + horizontalRegionSize < x && x < scrollRect.right - horizontalRegionSize &&
            scrollRect.top + verticalRegionSize < y && y < scrollRect.bottom - verticalRegionSize )
            return null;
    
        if (elapsedTime < dragScrollInitialDelay)
            return new Point(); // Return zero point to continue firing events, but not actually scroll.
        elapsedTime -= dragScrollInitialDelay;
    
        // Speedup based on time elapsed
        var timeSpeedUp:Number = Math.min(elapsedTime, 2000) / 2000;
        timeSpeedUp *= 3;
        timeSpeedUp += 1;
        timeSpeedUp *= timeSpeedUp * dragScrollSpeed * dragScrollInterval / 50;
    
        var minDeltaX:Number = -scrollRect.left;
        var minDeltaY:Number = -scrollRect.top;
        var maxDeltaY:Number = target.contentHeight - scrollRect.bottom;
        var maxDeltaX:Number = target.contentWidth - scrollRect.right;
    
        var deltaX:Number = 0;
        var deltaY:Number = 0;
    
        if (minDeltaX != 0 && x - scrollRect.left < horizontalRegionSize)
        {
            // Scroll left
            deltaX = 1 - (x - scrollRect.left) / horizontalRegionSize;
            deltaX *=  deltaX * timeSpeedUp;
            deltaX = -Math.round(deltaX) - 1;
        }
        else  if (maxDeltaX != 0 && scrollRect.right - x < horizontalRegionSize)
        {
            // Scroll right
            deltaX = 1 - (scrollRect.right - x) / horizontalRegionSize;
            deltaX *= deltaX * timeSpeedUp;
            deltaX = Math.round(deltaX) + 1;
        }
    
        if (minDeltaY != 0 && y - scrollRect.top < verticalRegionSize)
        {
            // Scroll up
            deltaY = 1 - (y - scrollRect.top) / verticalRegionSize;
            deltaY *=  deltaY * timeSpeedUp;
            deltaY = -Math.round(deltaY) - 1;
        }
        else  if (maxDeltaY != 0 && scrollRect.bottom - y < verticalRegionSize)
        {
            // Scroll down
            deltaY = 1 - (scrollRect.bottom - y) / verticalRegionSize;
            deltaY *= deltaY * timeSpeedUp;
            deltaY = Math.round(deltaY) + 1;
        }
    
        deltaX = Math.max(minDeltaX, Math.min(maxDeltaX, deltaX));
        deltaY = Math.max(minDeltaY, Math.min(maxDeltaY, deltaY));
    
        if (deltaX == 0 && deltaY == 0)
            return null;
        return new Point(deltaX, deltaY);
    }
    

    水平滚动位置

    /**
     *  @private
     *  Updates the scroll position and dispatches a DragEvent.
     */
    private function dragScroll(event:TimerEvent):void
    {
        // Scroll the target
        horizontalScrollPosition += _dragScrollDelta.x;
        verticalScrollPosition += _dragScrollDelta.y;
    
        // Validate target before dispatching the event
        target.validateNow();
    
        // Re-dispatch the event so that the drag initiator handles it as if
        // the DragProxy is dispatching in response to user input.
        // Always switch over to DRAG_OVER, don't re-dispatch DRAG_ENTER
        var dragEvent:DragEvent = new DragEvent(DragEvent.DRAG_OVER,
                                                _dragScrollEvent.bubbles,
                                                _dragScrollEvent.cancelable, 
                                                _dragScrollEvent.dragInitiator, 
                                                _dragScrollEvent.dragSource, 
                                                _dragScrollEvent.action, 
                                                _dragScrollEvent.ctrlKey, 
                                                _dragScrollEvent.altKey, 
                                                _dragScrollEvent.shiftKey);
    
        dragEvent.draggedItem = _dragScrollEvent.draggedItem;
        dragEvent.localX = _dragScrollEvent.localX;
        dragEvent.localY = _dragScrollEvent.localY;
        dragEvent.relatedObject = _dragScrollEvent.relatedObject;
        _dragScrollEvent.target.dispatchEvent(dragEvent);
    }
    

    还有这个:

    public function set horizontalScrollPosition(value:Number):void 
    {
        if (value == _horizontalScrollPosition) 
            return;
    
        _horizontalScrollPosition = value;
        scrollPositionChanged();
    }
    

    scrollPositionChanged();

     /**
     *  Called when the <code>verticalScrollPosition</code> or 
     *  <code>horizontalScrollPosition</code> properties change.
     *
     *  <p>The default implementation updates the target's <code>scrollRect</code> property by
     *  calling <code>updateScrollRect()</code>.
     *  Subclasses can override this method to compute other values that are
     *  based on the current <code>scrollPosition</code> or <code>scrollRect</code>.</p>
     *
     *  @see #updateScrollRect()
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */  
    protected function scrollPositionChanged():void
    {
        var g:GroupBase = target;
        if (!g)
            return;
    
        updateScrollRect(g.width, g.height);
    }
    

    也让我们看看这部分代码

    /**
    *  Computes the <code>verticalScrollPosition</code> and 
    *  <code>horizontalScrollPosition</code> deltas needed to 
    *  scroll the element at the specified index into view.
    * 
    *  <p>This method attempts to minimize the change to <code>verticalScrollPosition</code>
    *  and <code>horizontalScrollPosition</code>.</p>
    * 
    *  <p>If <code>clipAndEnableScrolling</code> is <code>true</code> 
    *  and the element at the specified index is not
    *  entirely visible relative to the target's scroll rectangle, then 
    *  return the delta to be added to <code>horizontalScrollPosition</code> and
    *  <code>verticalScrollPosition</code> that scrolls the element completely 
    *  within the scroll rectangle's bounds.</p>
    * 
    *  @param index The index of the element to be scrolled into view.
    *
    *  @return A Point that contains offsets to horizontalScrollPosition 
    *      and verticalScrollPosition that will scroll the specified
    *      element into view, or null if no change is needed. 
    *      If the specified element is partially visible and larger than the
    *      scroll rectangle, meaning it is already the only element visible, then
    *      return null.
    *      If the specified index is invalid, or target is null, then
    *      return null.
    *      If the element at the specified index is null or includeInLayout
    *      false, then return null.
    * 
    *  @see #clipAndEnableScrolling
    *  @see #verticalScrollPosition
    *  @see #horizontalScrollPosition
    *  @see #udpdateScrollRect()
    *  
    *  @langversion 3.0
    *  @playerversion Flash 10
    *  @playerversion AIR 1.5
    *  @productversion Flex 4
    */
    public function getScrollPositionDeltaToElement(index:int):Point
    

    那么,如果我们讨论一下LayoutBase和HorizontalLayout中这些参数的区别呢?

    在HL中它用于 private function updateDisplayListVirtual():void 但是在LayoutBase中没有这样的函数,因为LayoutBase只是一个基类,但是它有另一个称为 public function updateDisplayList(width:Number, height:Number):void 但它是空的。

    所以,如果我们试着想想这些函数都做了些什么,imho,HL只是扩展了standart updateDisplayList 对于两个函数,一个是相同的,另一个是虚拟的。

    override public function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);
    
        var layoutTarget:GroupBase = target; 
        if (!layoutTarget)
            return;
    
        if ((layoutTarget.numElements == 0) || (unscaledWidth == 0) || (unscaledHeight == 0))
        {
            setColumnCount(0);
            setIndexInView(-1, -1);
            if (layoutTarget.numElements == 0)
                layoutTarget.setContentSize(paddingLeft + paddingRight, paddingTop + paddingBottom);
            return;         
        }
    
        if (useVirtualLayout) 
            updateDisplayListVirtual();
        else
            updateDisplayListReal();
    }
    

    所以一切都是 使用虚拟布局 ,但它来自LayoutBase,让我们看看:

     /**
     *  A container can hold any number of children. 
     *  However, each child requires an instance of an item renderer. 
     *  If the container has many children, you might notice performance degradation 
     *  as you add more children to the container. 
     *
     *  <p>Instead of creating an item renderer for each child, 
     *  you can configure the container to use a virtual layout. 
     *  With virtual layout, the container reuses item renderers so that it only creates 
     *  item renderers for the currently visible children of the container. 
     *  As a child is moved off the screen, possible by scrolling the container, 
     *  a new child being scrolled onto the screen can reuse its item renderer. </p>
     *  
     *  <p>To configure a container to use virtual layout, set the <code>useVirtualLayout</code> property 
     *  to <code>true</code> for the layout associated with the container. 
     *  Only the DataGroup or SkinnableDataContainer with the VerticalLayout, 
     *  HorizontalLayout, and TileLayout supports virtual layout. 
     *  Layout subclasses that do not support virtualization must prevent changing
     *  this property.</p>
     *
     *  <p><b>Note: </b>The BasicLayout class throws a run-time error if you set 
     *  <code>useVirtualLayout</code> to <code>true</code>.</p>
     * 
     *  <p>When <code>true</code>, layouts that support virtualization must use 
     *  the <code>target.getVirtualElementAt()</code> method, 
     *  rather than <code>getElementAt()</code>, and must only get the 
     *  elements they anticipate will be visible given the value of <code>getScrollRect()</code>.</p>
     * 
     *  <p>When <code>true</code>, the layout class must be able to compute
     *  the indices of the layout elements that overlap the <code>scrollRect</code> in its 
     *  <code>updateDisplayList()</code> method based exclusively on cached information, not
     *  by getting layout elements and examining their bounds.</p>
     * 
     *  <p>Typically virtual layouts update their cached information 
     *  in the <code>updateDisplayList()</code> method,
     *  based on the sizes and locations computed for the elements in view.</p>
     * 
     *  <p>Similarly, in the <code>measure()</code> method, virtual layouts should update the target's 
     *  measured size properties based on the <code>typicalLayoutElement</code> and other
     *  cached layout information, not by measuring elements.</p>
     * 
     *  <p>Containers cooperate with layouts that have <code>useVirtualLayout</code> = <code>true</code> by 
     *  recycling item renderers that were previously constructed, but are no longer in use.
     *  An item is considered to be no longer in use if its index is not
     *  within the range of <code>getVirtualElementAt()</code> indices requested during
     *  the container's most recent <code>updateDisplayList()</code> invocation.</p>
     *
     *  @default false
     * 
     *  @see #getScrollRect
     *  @see #typicalLayoutElement
     *
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    

    当做

    推荐文章