Colorindo linhas de um DataGrid no Flex 2

Como assim colorindo?

Há um tempinho atrás, eu precisava alterar a cor de algumas linhas de um DataGrid. O problema é que não era alterar alternadamente (alternatingItemColors) as cores das linhas, e sim alterar a cor de uma linha ou mais, conforme uma regra.

A primeira coisa que me venho à mente, era usar um itemRenderer, até aí tudo bem, eu até achei uma solução no site do Ben Forta, mas não atendia corretamente as minhas necessidades, além de dar alguns problemas ao ordenar os itens no DataGrid.

Então, depois de um tempo pesquisando, e com uma dica do pessoal da Flex Brasil, eu finalmente achei o caminho das pedras, e consegui obter o resultado desejado. A solução foi criar uma nova classe, que estendesse a classe DataGrid, e sobrescrever alguns métodos.

Pois bem, é exatamente essa solução que pretendo mostrar para vocês aqui. É um pouco complicado no começo achar os métodos/propriedades que você deseja. Por isso, se prepare, pois você vai ter que ler muito a documentação do Flex, se realmente desejar aprender.

Vou partir do princípio de que você sabe lidar (pelo menos um pouco) com o Flex Builder e que tenha conhecimento sobre OOP (pelo menos um pouco também). Crie um novo projeto, e dentro dele tenha a seguinte estrutura de pastas:

  • projeto
    • components

O que nós vamos criar é bem simples. Imagine um pequeno DataGrid contendo 2 colunas, uma para o mês e outra para o lucro obtido. Se o lucro for positivo, tudo fica normal, se for igual a 0, a linha deve ficar amarela, e se for menor do que 0, a linha deve ficar vermelha.

Criando a classe DataGridColorido

Crie um arquivo de Classe dentro da pasta components e dê o nome de DataGridColorido (não é muito bonito, mas é didático).

1
2
3
4
5
6
7
8
9
package components
{
   
    public class DataGridColorido
    {
   
    }
   
}

O primeiro passo é estender a classe DataGrid, para que o nosso novo componente tenha todas as propriedades e métodos da classe. A palavra chave extends serve para essa finalidade.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package components
{
    import mx.controls.DataGrid;

    public class DataGridColorido extends DataGrid
    {
   
        /**
         * Construtor.
         */

        public function DataGridColorido()
        {
            // Método construtor da superclasse(DataGrid).
            super();
        }
   
    }

}

Pronto. Agora nossa classe contém todas os métodos e propriedades (públicos e protegidos) da classe DataGrid.

Criando o arquivo MXML

O próximo passo é criarmos o nosso arquivo MXML, para depois voltarmos aqui e implementarmos o restante.
Crie um arquivo na raíz do projeto chamado index.mxml.

1
2
3
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical”>
</mx:Application>

O primeiro passo aí, é adicionarmos o caminho para nossa pasta de componentes, para que então possamos utilizá-los. A propriedade xmlns de Application define isso, portanto basta nós fazermos uma nova declaração, apontando para a nossa pasta components.

1
2
3
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:cp=”components.*” layout=”vertical”>
</mx:Application>

Agora poderemos criar uma instância do nosso componente sem problemas. Mas antes disso, vamos criar os valores que serão adicionados ao nosso componente. Como foi citado anteriormente, teremos 2 colunas: mes e lucro.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:cp=”components.*” layout=”vertical”>
    <mx:Script<
        <![CDATA[
            import mx.collections.ArrayCollection;
           
            [Bindable]
            private var valores:ArrayCollection = new ArrayCollection([
                {mes:”Março”, lucro:-20},
                {mes:”Abril”, lucro:0},
                {mes:”Maio”, lucro:5},
                {mes:”Junho”, lucro:12},
                {mes:”Julho”, lucro:0},
                {mes:”Agosto”, lucro:10},
                {mes:”Setembro”, lucro:15},
                {mes:”Outubro”, lucro:-5},
                {mes:”Novembro”, lucro:20},
            ]);
        ]]>
    </mx:Script>
</mx:Application>

Dentro da tag Script nós podemos criar qualquer código que desejarmos. No caso, criamos a variável privada valores, que contém um ArrayCollection com todas as informações que irão popular o nosso DataGridColorido. Perceba que existem valores negativos, e valores iguais a 0.

Uma vez que temos os valores, vamos adicionar o nosso componente.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:cp="components.*" layout="vertical">
    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
           
            [Bindable]
            private var valores:ArrayCollection = new ArrayCollection([
                {mes:"Março", lucro:-20},
                {mes:"Abril", lucro:0},
                {mes:"Maio", lucro:5},
                {mes:"Junho", lucro:12},
                {mes:"Julho", lucro:0},
                {mes:"Agosto", lucro:10},
                {mes:"Setembro", lucro:15},
                {mes:"Outubro", lucro:-5},
                {mes:"Novembro", lucro:20},
            ]);
        ]]>
    </mx:Script>
   
    <cp:DataGridColorido dataProvider="{valores}">
        <cp:columns>
            <mx:DataGridColumn headerText="Mês" dataField="mes" />
            <mx:DataGridColumn headerText="Lucro (%)" dataField="lucro" />
        </cp:columns>
    </cp:DataGridColorido>
   
</mx:Application>

Teste sua aplicação, e o que você verá é um DataGrid normal, exibindo os valores de mes e lucro, em suas respectivas colunas.

Alterando a cor das linhas

A partir de agora, nosso objetivo é fazer com que todas as linhas que tenham valores inferiores a 0, fiquem da cor vermelha, e que todas as linhas com valores iguais a 0 fiquem amarelas.

Como fazer isso? O primeiro passo é investigar a classe DataGrid na documentação, e procurar algum método que atenda as nossas necessidades. Se você procurar bem, vai achar um método chamado drawRowBackground, e sua funcionalidade é criar o fundo das linhas do DataGrid. Opa! Acho que encontramos o cara certo para a nossa missão.

Uma vez que temos o método correto, basta re-implementarmos ele em nossa classe. Recomendo que você vá até a classe DataGrid, e dê uma olhada no conteúdo desse método.

Obs.: Para abrir uma classe, basta clicar sobre o nome dela (na classe DataGridColorido) pressionando a tecla Ctrl, e o arquivo da classe irá abrir.

Agora que você entendeu como funciona o método drawRowBackground, podemos começar a criar a implementação desse método em nossa classe.

A primeira coisa que iremos fazer, será definir a propriedade rowColorFunction. Essa propriedade vai conter a função que valida os dados e define a cor de fundo do item atual.

8
9
10
11
12
13
14
15
16
        /**
         * Propriedade que deve conter a função que define
         * as cores de cada linha.
         *
         * @param data Contém os dados do item atual.
         * @param color Contém a cor para o fundo do item.
         * @param index Contém o indice do item atual.
         */

        public var rowColorFunction:Function;

Uma vez declarada a propriedade, vamos criar a nossa implementação do método drawRowBackground.

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
        /**
         * Desenha o background das linhas.
         *
         * @param s Um Sprite que contém os gráficos da linha.
         * @param rowIndex Index das linhas exibidas. O header não conta.
         * @param y Posição Y para o fundo.
         * @param height Largura sugerida.
         * @param color Cor sugerida.
         * @param dataIndex Index do item no DataProvider.
         * @return void
         */

        override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint, dataIndex:int):void
        {
            if (this.rowColorFunction != null &amp;&amp; ArrayCollection(this.dataProvider) &amp;&amp; dataIndex &lt; ArrayCollection(this.dataProvider).length)
            {
                var item:Object = ArrayCollection(this.dataProvider).getItemAt(dataIndex);
                var index:int = rowIndex;
               
                color = this.rowColorFunction(item, color, index);
            }
           
            // Chamando o método da superclasse
            super.drawRowBackground(s, rowIndex, y, height, color, dataIndex);
        }

O que fizemos na verdade foi bem simples. Sobrescrevemos o método (override) drawRowBackground. Dentro do método, verificamos se existem valores no DataProvider do componente, e se o dataIndex é menor que o tamanho do DataProvider. Também verificamos se a propriedade rowColorFunction não é nula.

Se todas as condições forem atendidas, é executada a função da propriedade rowColorFunction, e seu retorno é atribuído a color.

Por final, chamamos o método na superclasse para dar continuidade a montagem dos backgrounds. Não esqueça de importar a classe Sprite, que é o tipo utilizado pelo parâmetros.

3
import flash.display.Sprite

Criando uma regra para as cores

Uma vez criada a implementação, tudo o que temos que fazer é criar uma pequena função no nosso MXML, para verificar os valores de cada item, e decidir qual será a cor de fundo do item. Essa função vai ser atribuída a propriedade rowColorFunction do componente.

Dentro da tag Script, adicione a seguinte função:

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
             /**
             * Verifica se os valores são maiores ou diferentes de 0.
             * Para valores menores do que 0, retorna a cor vermelha.
             * Para valores iguais a 0, retorna a cor amarela.
             * Para os demais valores, retorna a cor atual.
             */

            private function defineCor(data:Object, color:uint):uint
            {
                if (data.lucro &lt; 0)
                {
                    color = 0xFFA5A5;
                }
                else if (data.lucro == 0)
                {
                    color = 0xFFFF00;
                }
               
                return color;
            }

Uma vez criado a função, basta atribuí-la à propriedade rowColorFunction do DataGrid:

43
<cp:DataGridColorido dataProvider=”{valores}” rowColorFunction=”defineCor”>

Resultado Final

Teste a aplicação e verá como as linhas com valor negativo ficam vermelhas, e as com valor 0 ficam amarelas. O arquivo MXML completo fica dessa forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:cp="components.*" layout="vertical">
    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
           
            [Bindable]
            private var valores:ArrayCollection = new ArrayCollection([
                {mes:"Março", lucro:-20},
                {mes:"Abril", lucro:0},
                {mes:"Maio", lucro:5},
                {mes:"Junho", lucro:12},
                {mes:"Julho", lucro:0},
                {mes:"Agosto", lucro:10},
                {mes:"Setembro", lucro:15},
                {mes:"Outubro", lucro:-5},
                {mes:"Novembro", lucro:20},
            ]);
           
            /**
             * Verifica se os valores são maiores ou diferentes de 0.
             * Para valores menores do que 0, retorna a cor vermelha.
             * Para valores iguais a 0, retorna a cor amarela.
             * Para os demais valores, retorna a cor atual.
             */
            private function defineCor(data:Object, color:uint):uint
            {
                if (data.lucro < 0)
                {
                    color = 0xFFA5A5;
                }
                else if (data.lucro == 0)
                {
                    color = 0xFFFF00;
                }
               
                return color;
            }
           
        ]]>
    </mx:Script>
   
    <cp:DataGridColorido dataProvider=”{valores}” rowColorFunction=”defineCor”>
        <cp:columns>
            <mx:DataGridColumn headerText="Mês" dataField="mes" />
            <mx:DataGridColumn headerText="Lucro (%)" dataField="lucro" />
        </cp:columns>
    </cp:DataGridColorido>
   
</mx:Application>

E o arquivo da classe DataGridColorido completo ficou dessa forma:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package components
{
    import flash.display.Sprite;
   
    import mx.controls.DataGrid;
   
    public class DataGridColorido extends DataGrid
    {
       
        /**
         * Propriedade que deve conter a função que define
         * as cores de cada linha.
         *
         * @param data Contém os dados do item atual.
         * @param color Contém a cor para o fundo do item.
         * @param index Contém o indice do item atual.
         */

        public var rowColorFunction:Function;
       
        /**
         * Construtor.
         */

        public function DataGridColorido()
        {
            // Método construtor da superclasse (DataGrid).
            super();
        }
       
       
        /**
         * Desenha o background das linhas.
         *
         * @param s Um Sprite que contém os gráficos da linha.
         * @param rowIndex Index das linhas exibidas. O header não conta.
         * @param y Posição Y para o fundo.
         * @param height Largura sugerida.
         * @param color Cor sugerida.
         * @param dataIndex Index do item no DataProvider.
         * @return void
         */

        override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint, dataIndex:int):void
        {
            if (this.rowColorFunction != null &amp;&amp; ArrayCollection(this.dataProvider) &amp;&amp; dataIndex &lt; ArrayCollection(this.dataProvider).length)
            {
                var item:Object = ArrayCollection(this.dataProvider).getItemAt(dataIndex);
                var index:int = rowIndex;
               
                color = this.rowColorFunction(item, color, index);
            }
           
            // Chamando o método da superclasse
            super.drawRowBackground(s, rowIndex, y, height, color, dataIndex);
        }
   
    }

}

Experimente agora implementar novas funcionalidades a outros componentes, procurando sempre na documentação quais métodos podem lhe ajudar.

8 Comments

  1. Apliquei o seu exemplo mas me retorna o erro abaixo, teria alguma idéia ?

    RangeError: Index ’0′ specified is out of bounds.
    at mx.collections::ListCollectionView/getItemAt()
    at ResumoDev::DataGridColor/drawRowBackground()
    at mx.controls::DataGrid/drawRowBackgrounds()
    at mx.controls::DataGrid/updateDisplayList()
    at mx.core::UIComponent/validateDisplayList()
    at mx.managers::LayoutManager/validateDisplayList()
    at mx.managers::LayoutManager/doPhasedInstantiation()
    at Function/http://adobe.com/AS3/2006/builtin::apply()
    at mx.core::UIComponent/callLaterDispatcher2()
    at mx.core::UIComponent/callLaterDispatcher()
    at flash.utils::Timer/_timerDispatch()
    at flash.utils::Timer/tick()

  2. Ronaldo,
    Existia um pequeno erro (culpa da pressa). Temos que verificar se o dataIndex é menor que o tamanho do dataProvider para que esse erro não ocorra.

  3. Ola Mozart, eu segui os passos, porem não consegui um resultado positivo, eu criei meu DataGridColorido chamei o rowColorFunction, mas naum entendi o que devo passar: rowColorFunction(data:Objext, color:uint)

  4. Mozart, testei e funcionou perfeitamente. Só precisei fazer 2 ajustes. o Primeiro foi a importação da classe de ArrayCollections (import mx.collections.ArrayCollection;) no arquivo da classe. E a Segunda, foi a adição do terceiro parâmetro (index) na função defineCor. Obrigado pela Ajuda.

  5. Mozart Rlz!
    Gosto de ver quem apela as Classes e nao a Implementação
    =)

  6. entao cara no meu aplicativo aqui ele fala que nao conseguil resolver o como um componente de implementaçao

    erro:

    Could not resolve o to a component implementation.

    tem commo me dar um help ai?

    valeu

  7. Mozart Petter

    Hugo,
    Qual versão do Flex você está usando?

Trackbacks/Pingbacks

Leave a Comment