      *  Returns the Y coordinate of the origin of the specified cell.
      */
     public function getCellY(row:int, col:int):Number
-    {        // no cache so we use default heights for each row.
+    { 
+        // no cache so we use default heights for each row.
         if (!isNaN(fixedRowHeight))
             return row * (fixedRowHeight + rowGap);
         
         if (rowList.length == 0)
             return row * (defaultRowHeight + rowGap);
         
-        var node:GridRowNode = rowList.first;
-        var y:Number = 0;
-        var index:int = 0;
+        if (row == 0)
+            return 0;
+        
+        // initialize first node.
+        if (!recentNode2)
+        {
+            recentNode2 = rowList.first;
+            startY2 = recentNode2.rowIndex * (defaultRowHeight + rowGap);
+        }
+        
+        var y:Number;
+        var recentIndex:int = recentNode2.rowIndex;
+
+        if (row == recentIndex)
+            y = startY2;
+        else if (row < recentIndex)
+            y = getPrevYAt(row, recentNode2, startY2);
+        else
+            y = getNextYAt(row, recentNode2, startY2);
+
+        return y;
+    }
+
+    /**
+     *  Returns the starting y value of the specified row. The row must be before
+     *  startNode.
+     *  
+     *  @param row the target row
+     *  @param startNode the node to search from
+     *  @param startY the cumulative y value from the first row to the beginning
+     *  of startNode's row.
+     * 
+     *  @return the y value of the start of the row.
+     */
+    private function getPrevYAt(row:int, startNode:GridRowNode, startY:Number):Number
+    {
+        var node:GridRowNode = startNode;
+        var nodeY:Number = startY;
+        var prevNode:GridRowNode;
+        var currentY:Number = startY;
+        var indDiff:int;
         
         while (node)
         {
-            // success case: row index is <= to node's index.
-            if (row <= node.rowIndex)
+            if (node.rowIndex == row)
+                break;
+            
+            prevNode = node.prev;
+            
+            if (!prevNode || (row < node.rowIndex && row > prevNode.rowIndex))
             {
-                y += (row - index) * (defaultRowHeight + rowGap);
-                return y;
+                // at the beginning or somewhere between nodes
+                // so we've found the target row.
+                indDiff = node.rowIndex - row;
+                currentY -= indDiff * (defaultRowHeight + rowGap);
+                break;
             }
             
-            // otherwise, row index is > node's index
-            
-            // case if node index is much greater than current index.
-            if (node.rowIndex > index)
+            // subtract previous node's height and its gap.
+            indDiff = node.rowIndex - prevNode.rowIndex - 1;
+            nodeY = currentY = currentY - indDiff * (defaultRowHeight + rowGap) - (prevNode.maxCellHeight + rowGap); 
+            node = prevNode;
+        }
+
+        this.recentNode2 = node;
+        this.startY2 = nodeY;
+        
+        return currentY;
+    }
+    
+    /**
+     *  Returns the starting y value of the specified row. The row must be after
+     *  startNode.
+     *  
+     *  @param row the target row
+     *  @param startNode the node to search from
+     *  @param startY the cumulative y value from the first row to beginning of
+     *  startNode's row.
+     * 
+     *  @return the y value of the start of the row.
+     */
+    private function getNextYAt(row:int, startNode:GridRowNode, startY:Number):Number
+    {
+        var node:GridRowNode = startNode;
+        var nodeY:Number = startY;
+        var nextNode:GridRowNode;
+        var currentY:Number = startY;
+        var indDiff:int;
+        
+        while (node)
+        {
+            if (node.rowIndex == row)
+                break;
+            
+            currentY += node.maxCellHeight;
+            if (node.rowIndex < _rowCount - 1)
+                currentY += rowGap;
+            
+            nextNode = node.next;
+
+            if (!nextNode || (row > node.rowIndex && row < nextNode.rowIndex))
             {
-                y += (node.rowIndex - index) * (defaultRowHeight + rowGap);
-                index += node.rowIndex - index; // index == node.rowIndex
+                // at the beginning or somewhere between nodes
+                // so we've found the target row.
+                indDiff = row - node.rowIndex - 1;
+                currentY += indDiff * (defaultRowHeight + rowGap);
+                break;
             }
             
-            // index is now equal to node's index but row index is > node's index so we add node.
-            y += node.maxCellHeight;
-            index++;
-            node = node.next;
-        }
-        
-        // no more nodes, so we just add the rest.
-        y += (row - index) * (defaultRowHeight + rowGap);
-        return y;
+            // add next node's maxCellHeight and rowGap
+            indDiff = nextNode.rowIndex - node.rowIndex - 1;
+            nodeY = currentY = currentY + indDiff * (defaultRowHeight + rowGap);
+            node = nextNode;
+        }
+        
+        this.recentNode2 = node;
+        this.startY2 = nodeY;
+        
+        return currentY;
     }
     
     /**

        rowCount = 0;
        rowList.removeAll();
        this.recentNode = null;
        this.startY = 0;
    }

    /**

        var currentY:Number = startY;
        var nextY:Number;
        var targetY:Number = y;
        
        while (node)
        {
            // check the current node.
