пятница, 18 сентября 2009 г.

Flex Tile(TileDirection.HORIZONTAL) с изменяющейся высотой

 Во Flex 3 очень много разнообразных элементов управления, использовать их одно удовольствие. Например, элемент mx.containers.Tile способен отображать элементы, особенность его заключается в том что элементы которые не влезают в текущую строчку переносятся на следующую и так циклично пока не будут отображены все элементы.
 Он очень полезен в отображении данных с плавающей длинной, но при увеличении строчек размер самого контрола остается фиксированный. Не всегда есть возможность установить высоту на 100 процентов (height="100%"), но как сделать так что бы все строчки были видны и не появлялась прокрутка? Решение данной проблемы хочу описать в этом посте.

Для решения этой проблемы начал смотреть исходники элемента Tile. Сразу обратил внимание на переопределенные методы measure, updateDisplayList. Первый отвечал за определение размеров и был полезен только когда свойство direction было установлено в значение TileDirection.VERTICAL, второй же отвечал за расстановку элементов. Не в одном из методов полная высота не вычислялась, но во втором при расстановке было понятно что можно извлечь положние Y последнего элемента. Переписывать весь контрол не стал (не благодарное это дело), решил написать своего наследника.

<?xml version="1.0" encoding="utf-8"?>
<mx:Tile xmlns:mx="http://www.adobe.com/2006/mxml" resize="change_size()" creationComplete="init()">
 <mx:Script>
  <![CDATA[
          import mx.core.mx_internal;
          use namespace mx_internal;
          import mx.core.IUIComponent;
          import mx.containers.TileDirection;
          import mx.core.EdgeMetrics;
          
          private function init():void{
           change_size();
          }
          
          private function change_size():void{
           if (isNaN(cellWidth) || isNaN(cellHeight))
              findCellSize();
           
           var vm:EdgeMetrics = viewMetricsAndPadding;
          
          var paddingLeft:Number = getStyle("paddingLeft");
          var paddingTop:Number = getStyle("paddingTop");
  
          var horizontalGap:Number = getStyle("horizontalGap");
          var verticalGap:Number = getStyle("verticalGap");
         
          var horizontalAlign:String = getStyle("horizontalAlign");
          var verticalAlign:String = getStyle("verticalAlign");
  
          var xPos:Number = paddingLeft;
          var yPos:Number = paddingTop;
  
          var n:int = numChildren;
          var i:int;
          var child:IUIComponent;
          
          if (direction == TileDirection.HORIZONTAL)
          {
              var xEnd:Number = Math.ceil(unscaledWidth) - vm.right;
              for (i = 0; i < n; i++)
              {
                  child = IUIComponent(getChildAt(i));  
                  if (!child.includeInLayout)
                      continue;
  
                  // Start a new row?
                  if (xPos + cellWidth > xEnd)
                  {
                      // Only if we have not just started one...
                      if (xPos != paddingLeft)
                      {
                          yPos += (cellHeight + verticalGap);
                          xPos = paddingLeft;
                      }
                  }
                              
                  xPos += (cellWidth + horizontalGap);
              }
          }
          
          height = yPos + cellHeight + verticalGap;
          }
  ]]>
 </mx:Script>
</mx:Tile>

Это решение можно увидеть ниже. У стандартного элемента Tile установил зеленый фон, у моего - жёлтый.


"Пример 1" и "Пример 2" действительно демонстрируют что все отлично работает :).

2 комментария: