Criando um Custom Panel - Minimizar - Maximizar - Fechar.
Criando um Custom Panel.
Salve pessoal.
Na maioria dos projetos em que participo ou participei, são comuns os clientes ou usuários finais quererem telas nas quais tenha os botões de maximizar, minimizar, fechar e até mesmo outros tipos de componentes no topo do Panel. Hoje irei demonstrar como criar este componente de uma maneira simples e eficaz. Então vamos ao primeiro passo.
Este componente será similar ao FlexMdi, no qual hoje este faz parte do pacote flexlib.
Passo 1:
Certo primeiramente iremos criar um projeto Flex com a seguinte estrutura de pastas.

Com a estrutura acima criada vamos criar uma classe ‘as’ chamada BaseCustomPanel a qual irá estender um Panel. Esta classe será à base de todas as futuras telas e por sua vez não irá conter os botões de maximizar, minimizar e fechar. Porém a mesma lhe dará a total flexibilidade de adicionar qualquer componente no header (Topo - Cabeçalho) do Panel. Abaixo segue o código AS.
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | package com.imaster.customPanel { import flash.display.DisplayObject; import mx.containers.Panel; import mx.core.Application; import mx.core.UIComponent; import mx.effects.Blur; import mx.events.FlexEvent; import mx.managers.PopUpManager; [Style(name="headerHorizontalGap", type="Number", inherit="no")] /** * @author Fabiel Prestes */ public class BaseCustomPanel extends Panel { /** * Aqui será armazenado todos os componente FILHOS que serão adicionados * ao HEADER */ [ArrayElementType("mx.core.UIComponent")] public var arrayFilhos:Array = []; private var blurIn:Blur; /** * Construtor Padrao */ public function BaseCustomPanel() { super(); } /** * @inheritDoc */ override protected function childrenCreated():void { super.childrenCreated(); configStyle(); /* Para cada Filho adicionado no objeto 'arrayFilhos' * será realizado algumas configurações como: * Adiciona-lo no HEADER do PANEL * Propriedade buttonMode = true ....*/ for each (var child:UIComponent in arrayFilhos) addTitleBarComponent(child); } /** * @private */ private function abreComEfeito():void{ blurIn = new Blur(); blurIn.blurXFrom = 10; blurIn.blurXTo = 0; blurIn.blurYFrom = 10; blurIn.blurYTo = 0; blurIn.duration = 600; blurIn.play([this]); } /** * Realizando a configuração padrão dos Estilos */ private function configStyle():void { this.setStyle("headerHorizontalAlign", "rigth"); this.setStyle("headerVerticalAlign", "middle"); this.setStyle("headerHorizontalGap", 5); } /** * @inheritDoc */ override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); reposicioneElementosNoCabecalho(); } /** * Reposiciona todos os componentes FILHOS ao PAI 'HEADER' */ protected function reposicioneElementosNoCabecalho():void { var componenteFilho:UIComponent; var posicaoX:Number = 0; var tamanhoPai:Number = 0; var posicaoY:Number = 0; var headerHorizontalGap:Number = getStyle('headerHorizontalGap'); /* Caso não tenha sido adicionado Nenhum filho, deve apenas retornar e continuar * o processamento natural. */ if (arrayFilhos.length == 0) return; tamanhoPai = this.width - 10; /* Para cada filho Definido para ser adicionado no cabeçalho deve ser * verificado seu tamanho para assim ser posicionado no componente HEADER do PANEL. */ for (var i:int; i < arrayFilhos.length; i++) { componenteFilho = UIComponent(arrayFilhos[i]); componenteFilho.setActualSize(componenteFilho.getExplicitOrMeasuredWidth(), componenteFilho.getExplicitOrMeasuredHeight()); /* Abaixo é pego a altura do HEADER do PANEL subtraido com a altura do COMPONENTE filho * e DIVIDIDO por 2, afim de pegar o valor que será definido no Y. Desta maneira * o filho ficará no meio do HEADER. */ posicaoY = (getHeaderHeight() - componenteFilho.getExplicitOrMeasuredHeight()) / 2; /* Abixo é pego o tomanho total do PANEL subtraido com o tamanho total do COMPONENTE * filho SUBTRAIDO com o HORIZONTAL GAP, afim de pegar o valor que será definido no X. * Desta maneira os componente FILHOS ficarão sempre a DIREITA do PANEL. * A variavel TAMANHOPAI e tambem sempre recebe o resultado desta subtração para que assim * os FILHOS não irão ficar sobre postos um ao outro.*/ posicaoX = tamanhoPai - componenteFilho.getExplicitOrMeasuredWidth() - headerHorizontalGap; tamanhoPai = posicaoX; /* Move o componente FILHO para o X e Y calculado anteriormente. */ componenteFilho.move(posicaoX, posicaoY); } } /** * Responsavel por Adicionar o Filho ao HEADER e realizar algumas pré configurações */ public function addTitleBarComponent(child:UIComponent):void { child.buttonMode = true; /* Este metodo é responsavel por adicionar o FILHO ao HEADER do PANEL. */ titleBar.addChild(child); invalidateDisplayList(); } public function openPanel():void{ PopUpManager.addPopUp(this, DisplayObject(Application.application)); PopUpManager.centerPopUp(this); abreComEfeito(); } } } |
OK, com a nossa classe base criada iremos agora realizar uma extensão da mesma, fazendo algumas alterações como inclusão dos botões de minimizar, maximizar e fechar.
Segue abaixo o código da classe WindowPanel.as
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | package com.imaster.customPanel { import com.imaster.event.WindowPanelEvent; import flash.events.MouseEvent; import flash.filters.GlowFilter; import mx.containers.Box; import mx.controls.Image; import mx.core.UIComponent; import mx.effects.Blur; import mx.effects.Move; import mx.effects.Parallel; import mx.effects.Resize; import mx.effects.Sequence; import mx.events.EffectEvent; import mx.managers.PopUpManager; import mx.utils.ObjectUtil; [Event(name="minimixar", type="com.imaster.event.WindowPanelEvent")] [Event(name="maximizar", type="com.imaster.event.WindowPanelEvent")] [Event(name="fechar", type="com.imaster.event.WindowPanelEvent")] /** * @author Fabiel Prestes */ public class WindowPanel extends BaseCustomPanel { private var imgFechar:Image; private var imgMaximizar:Image; private var imgMinimizar:Image; private var glow:GlowFilter; private var sequenceMinimizar:Sequence; private var parallelMaximizar:Parallel; private var moveMaximizar:Move; private var resizeMaximizar:Resize; private var parallelMinimizar:Parallel; private var resizeMinimizar:Resize; private var moveMinimizar:Move; private var widthOriginal:Number; private var heightOriginal:Number; [Bindable] private var xOriginal:Number; [Bindable] private var yOriginal:Number; public var panelPai:UIComponent; public var barra:Box; /** * Contrutor Padrão */ public function WindowPanel() { super(); } /** * @inheritDoc */ override protected function childrenCreated():void{ this.setStyle("styleName", "windowpanel"); configBotoes(); super.childrenCreated(); widthOriginal = ObjectUtil.copy(this.width) as Number; heightOriginal = ObjectUtil.copy(this.height) as Number; configEfeitoBotoes(); } /** * @private * Metodo responsavel por guarda a posição X e Y atual da tela para que * assim ao maximizar a tela a mesma volte para a sua posição original. */ private function trateMouseUpMinimizar(evt:MouseEvent):void{ xOriginal = this.x; yOriginal = this.y; } /** * @private * Responsavel por criar e configurar os botoes de Maximizar, Minimizar e Fechar. */ private function configBotoes():void{ imgMaximizar = new Image(); imgMaximizar.source = "assets/img/maximizar.png"; imgMaximizar.width = 20; imgMaximizar.height = 20; imgMinimizar = new Image(); imgMinimizar.source = "assets/img/minimizar.png"; imgMinimizar.width = 20; imgMinimizar.height = 20; imgFechar = new Image(); imgFechar.source = "assets/img/fechar.png"; imgFechar.width = 20; imgFechar.height = 20; imgMaximizar.addEventListener(MouseEvent.MOUSE_OVER, trateMouseOver); imgMaximizar.addEventListener(MouseEvent.MOUSE_OUT, trateMouseOut); imgMaximizar.addEventListener(MouseEvent.CLICK, trateMaximizarClick); imgMinimizar.addEventListener(MouseEvent.MOUSE_OVER, trateMouseOver); imgMinimizar.addEventListener(MouseEvent.MOUSE_OUT, trateMouseOut); imgMinimizar.addEventListener(MouseEvent.CLICK, trateMinimizarClick); imgMinimizar.addEventListener(MouseEvent.MOUSE_UP, trateMouseUpMinimizar); imgFechar.addEventListener(MouseEvent.MOUSE_OVER, trateMouseOver); imgFechar.addEventListener(MouseEvent.MOUSE_OUT, trateMouseOut); imgFechar.addEventListener(MouseEvent.CLICK, trateFecharClick); this.arrayFilhos.push(imgFechar); this.arrayFilhos.push(imgMaximizar); this.arrayFilhos.push(imgMinimizar); } /** * @private * Responsavel por instanciar e configurar os efeitos que serão realizados * pelo click do mouse nas imagens no HEADER. */ private function configEfeitoBotoes():void{ /* Este efeito será utilizado quando o mouse estiver em cima do botoes/imagens * no HEADER */ glow = new GlowFilter(); glow.color = 0xffffff; glow.blurX = glow.blurY = 20; } /** * @private * Responsavel por instanciar e configurar os efeitos que serão realizados * pelo click do mouse na imagem de Maximizar. */ private function configEfeitoMaximizar():void{ if(moveMaximizar == null){ moveMaximizar = new Move(this); moveMaximizar.duration = 600; } moveMaximizar.xFrom = barra.x; moveMaximizar.yFrom = barra.y; moveMaximizar.xTo = xOriginal; moveMaximizar.yTo = yOriginal; if(resizeMaximizar == null){ resizeMaximizar = new Resize(this); resizeMaximizar.heightTo = heightOriginal; resizeMaximizar.widthTo = widthOriginal; resizeMaximizar.duration = 600; } if(parallelMaximizar == null){ parallelMaximizar = new Parallel(this); parallelMaximizar.addChild(moveMaximizar); parallelMaximizar.addChild(resizeMaximizar); } } /** * @private * Responsavel por instanciar e configurar os efeitos que serão realizados * pelo click do mouse na imagem de Minimizar. */ private function configEfeitoMinimizar():void{ if(resizeMinimizar == null){ resizeMinimizar = new Resize(this); resizeMinimizar.widthTo = 200; resizeMinimizar.heightTo = 35; resizeMinimizar.duration = 600; } if(moveMinimizar == null){ moveMinimizar = new Move(this); moveMinimizar.duration = 600; } var filhosBarra:int = barra.getChildren().length; if(filhosBarra >= 1){ moveMinimizar.xTo = (barra.getChildAt(0).width + 10) * filhosBarra; }else{ moveMinimizar.xTo = barra.x; } moveMinimizar.yTo = barra.y; if(parallelMinimizar == null){ parallelMinimizar = new Parallel(); parallelMinimizar.addChild(resizeMinimizar); parallelMinimizar.addChild(new Blur()); parallelMinimizar.addChild(moveMinimizar); } if(sequenceMinimizar == null){ sequenceMinimizar = new Sequence(this); sequenceMinimizar.addChild(parallelMinimizar); sequenceMinimizar.addEventListener(EffectEvent.EFFECT_END, trateFimEfeitoMinimizar); } } /** * @private * Trata o OVER do mouse nas imagens do HEADER */ private function trateMouseOver(evt:MouseEvent):void{ evt.currentTarget.filters =[glow]; } /** * @private * Trata o OUT do mouse nas imagens do HEADER */ private function trateMouseOut(evt:MouseEvent):void{ evt.currentTarget.filters = []; } /** * @private * Trata o CLICK da imagem maximizar do HEADER */ private function trateMaximizarClick(evt:MouseEvent):void{ barra.removeChild(this); PopUpManager.addPopUp(this, panelPai); configEfeitoMaximizar(); parallelMaximizar.play(); this.dispatchEvent(new WindowPanelEvent(WindowPanelEvent.MAXIMIZAR)); } /** * @private * Trata o CLICK da imagem mniimizar do HEADER */ private function trateMinimizarClick(evt:MouseEvent):void{ configEfeitoMinimizar(); sequenceMinimizar.play(); } /** * @private * Trata o CLICK da imagem fechar do HEADER */ private function trateFecharClick(evt:MouseEvent):void{ var blur:Blur = new Blur(this); blur.blurXFrom = 0; blur.blurYFrom = 0; blur.blurXTo = 10; blur.blurYTo = 10; blur.duration = 600; blur.addEventListener(EffectEvent.EFFECT_END, trateFimEfeitoFechar); blur.play(); } /** * @private * Apos terminar o efeito de minimizar, é dispachado o evento do tipo MINIMIZAR */ private function trateFimEfeitoMinimizar(evt:EffectEvent):void{ barra.addChild(this); this.dispatchEvent(new WindowPanelEvent(WindowPanelEvent.MINIMIZAR)); } /** * @private * Apos terminar o efeito de mafechar, é dispachado o evento do tipo FEHCAR * e logo em seguida a tela é removida. */ private function trateFimEfeitoFechar(evt:EffectEvent):void{ try{ PopUpManager.removePopUp(this); barra.removeChild(this); panelPai.removeChild(this); }catch(er:Error){} this.dispatchEvent(new WindowPanelEvent(WindowPanelEvent.FECHAR)); } } } |
O grande detalhe desta classe é que esta armazena a referência de seu Parent (Pai) e da barra onde a mesma ficará minimizada.
Na mesma foi incluído efeitos diferentes em cada ação de botão, por exemplo.
Botão Minimizar: Foi aplicado um efeito paralelo no qual a tela vai se movimentando (Move) e se redimensionando (Resize) até atingir a barra de minimização, com um pequeno Blur ao final.
Botão Maximizar: Neste existe apenas o efeito Resize, deixando mais simples.
Botão Fechar: Neste também foi aplicado apenas o efeito Blur simples mais bem atrativo aos olhos do usuário final, pois se assemelha bastante com o efeito que o Windows Vista oferece.
Também foi aplicado nos botões um efeito de Glow, no momento em que o usuário passa o mouse por cima dos botões, efeito bem similar ao Vista.
Todos os botões tem listener Mouse.Click e Mouse.Mouse_Over e para cada um destes possuem seus próprios comportamentos, porem a cada fim de comportamento os mesmos dispacham seus próprios eventos, afim de tornar mais flexível a sua utilização pelos outros desenvolvedores que utilizarão seu coponente.
É um componente relativamente simples para a construção e agora vocês verão que também é de simples aplicação. Vamos as classes de teste.
WindowPanelTeste.mxml, uma simples classe que extende o WindowPanel e adiciona um VBox com um background Cinza.
Main.mxml, uma simples classe de teste. A mesma já é instanciada com duas janela WindowTeste criada como default, também possui um botão para adicionar novas janelas testes.
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 | <![CDATA[ import com.imaster.WindowPanelTeste; import mx.managers.PopUpManager; import mx.controls.PopUpButton; import com.imaster.customPanel.WindowPanel; public var filhos:Number = 1; private function init():void{ var tela:WindowPanelTeste = new WindowPanelTeste(); tela.panelPai = this; tela.barra = hbBarra; tela.title = "Tela 1"; PopUpManager.addPopUp(tela, this); PopUpManager.centerPopUp(tela); var tela2:WindowPanelTeste = new WindowPanelTeste(); tela2.panelPai = this; tela2.barra = hbBarra; tela2.title = "Tela 2"; PopUpManager.addPopUp(tela2, this); PopUpManager.centerPopUp(tela2); } private function novaJanela():void{ var tela:WindowPanelTeste = new WindowPanelTeste(); tela.panelPai = this; tela.barra = hbBarra; tela.title = "Nova Janela"; tela.openPanel(); } ]]> |
Aí esta um componente simples e flexível. Muitos devem até estar se perguntando, o “porque de se criar este componente se já existe outros.?”. Certamente já existem componentes similares a este, contudo a questão aqui é demonstrar em maneira simples como é possível criar seus próprios componentes e não ficar dependendo da comunidade ou dos criadores do mesmo para resolverem problemas com as lib. E com certeza a partir deste componente você poderá deixá-lo muito mais robusta e atraente basta usar a criatividade.
Espero mais uma vez ter ajudado e até a próxima. Abaixo segue o link do source para download
Exemplo Funcionando:
