|
por Biswajit Sarkar 20/11/2008 |
Animações e transições são duas características muito interessantes do Lightweight User Interface Toolkit (LWUIT). Neste artigo veremos como usar esses recursos em aplicações reais. Temos também que desenvolver uma animação transição personalizada. Embora muito simples, esta transição demonstra plenamente o processo de tal personalização.
Tenho usado o Sprint Wireless Toolkit 3.3.1 (Sprint WTK) para as telas, e também para construir a CustomTransitionDemo. Tenho referido também o código fonte para o LWUIT aplicação demonstração, que vem com o pacote LWUIT download para ilustrar o uso das capacidades de animação e transição do LWUIT. Para uma boa compreensão do presente artigo, o conjunto de ferramentas e o pacote LWUIT devem ser baixados e instalados em seu computador. Versão 3.3.2 do Sprint WTK foi disponibilizado; Eu testei-o e conclui que não há existe problema em executar nossa demonstração sobre a nova versão.
Animação
Foi possível animar imagens sobre a plataforma Java ME, mesmo antes do LWUIT aparecer em cena. Existe um número de funcionalidades no pacote javax.microedition.lcdui.game animação que suporta animação. Além disso, a API JSR 226 fornece suporte para animação SVG-based. LWUIT lida com animação um pouco diferente, fornecendo suporte para uma capacidade que é ampla e simples de usar. No contexto do LWUIT, animação essencialmente permite objetos para pintar quadros sucedendo em intervalos cronometrados. Animação suporta no LWUIT também tornar fácil a transição para ser implementada.
Para estar capacitado de ser animado, um objeto tem que implementar a interface Animation. Animation tem dois métodos.
* animate: Este é um método callback invocado uma vez a cada frame.
* paint: Este método é chamado se animate retorna true. Se o objeto a ser animado é um componente, ele terá um método paint e que é um método a ser chamado como a assinatura dos dois métodos, que é idêntico.
A classe Component estende Animation. Isto torna cada widget capaz de animação. No entanto, para realmente realizar animação, um objeto também deve ser registrado para animação por chamar o método registerAnimated no seu formulário pai. O método animate de um objeto registrado é chamado uma vez para cada frame da animação, conforme determinado pela classe Display. Se animate retorna true, o método paint do objeto é chamado e um repaint ocorre. Para parar a animação, o objeto deve ser desregistrado por chamar o método deregisterAnimated no formulário pai.


Figura 1. Dois quadros de demonstração de animação
A demonstração mostra dois exemplos de animação: um usa uma imagem animada do Duke e o outro chama repetidamente um conjunto de imagens para simular movimento. Em ambos os casos, o componente básico que é animado é um botão. Outros componentes também podem ser animados de uma forma semelhante. Pode substituir os botões no código para AnimationDemo com labels e a demonstração continuará a funcionar bem. Basta lembrar de importar a classe Label.
Ao executar o método de AnimationDemo, o primeiro botão - animation - é instanciados e adicionados ao formulário (f), que foi passado como um parâmetro para o método:
Button animation = new Button(UIDemoMIDlet. |
A segunda e terceira linhas são apenas para efeitos visuais e a quarta define o gerenciador de layout para o formulário. A coisa interessante para notar aqui é que o botão não foi registrado para a animação. Isso é porque a própria imagem é animada e não exige repintagem. A imagem de Duke utilizado aqui é um gif animado. Neste momento LWUIT suporta apenas formato gif animado; como uma questão de fato, o Resource Editor não reconhece qualquer outro formato animado.
Para o segundo botão, o conjunto de imagens é criado para o método paint:
|
Temos agora um conjunto de doze imagens, que estão desenhadas sequencialmente. O código para criar o segundo botão parece com o que segue:
Button animation2 = new Button() {
|
Aqui temos um botão que implementa ambos os métodos animate e paint. O método animate será chamado a cada intervalo frame. Se mais de 50 milissegundos tiverem decorrido desde a última chamada, animate incrementará o ponteiro para o conjunto de imagens e retorna true, caso contrário, ele retornará apenas falsa. Se true for devolvido, em seguida, o método paint será chamado e as imagens serão tiradas.
O resto do código para o segundo botão é principalmente para a fixação de parâmetros visuais e para adicionar o botão ao formulário. Observe a última linha de código, o qual mostra que o botão foi registrado pela animação. Isso garante que o método animate será chamado para cada frame.
animation2.setPreferredSize(new Dimension |
E isso é tudo o que existe para aplicar a animação!
Transições
O termo transition refere-se para o caminho um formulário que é trazido para dentro (animate em transição) e levadas para fora do visor (animate fora transição). A classe base para a execução de transição é Transition,, que implementa Animation. Isto é porque transições trabalha basicamente como animações e utiliza o mesmo mecanismo de chamada como qualquer outro componente animados. No entanto, Transition é diferente de Component no sentido de que um Component anima a sua própria renderização enquanto Transition controla a renderização de formulários e diálogos animados.
Existe também uma diferença entre os caminhos em que componente animações e transições são utilizados. Para usar transições, não é necessário registrar o formulário em questão de animação. Quando um formulário trasição ocorre, o objeto Display diretamente coloca o formulário na fila para receber chamadas.
A classe Transition é um abstract e não pode ser instanciado. A biblioteca LWUIT inclui duas subclasses concretas de Transition que pode ser utilizada para aplicar transições. Essas classes são:
- CommonTransitions
-
Transition3D
- Slide: O novo formulário slides dentro, empurrando o atual.
- Fade: O novo formulário fades dentro, enquanto o atual fades sai.
CommonTransitions trabalha com um suporte a classe - Motion - que fornece modelos para simulação de movimento físico. Os três tipos de movimentos que contém Motion são:
- Friction
- Spline
- Linear
A classe Transition3D trabalha com a API M3G (JSR 184) para fornecer animações de transições com efeitos 3D. JSR 184 support é necessário para estas transições para funcionar corretamente, e assim a classe Motion não é requerida para Transition3D. Uma limitação atual desta classe é que as suas transições trabalham apenas com formulários, mas que é susceptível de alterar soon. O 3D suite de transições contém o que segue.
- Cube: Simula um cubo giratório, com a forma atual, sobre a face rotativa fora da tela e do novos em face rotativa na tela.
- Fly In: O novo arquivo formulário dentro.
- Rotation: Uma versão de duas dimensões de Cube iem que um avião em vez de um cubo roda.
- Static Rotation: Aplicável a uma transição de um formulário dentro de um dialogo. Só o diálogo gira enquanto o formulário permanece estático.
- Swing In: O formulário oscila em qualquer lugar, de cima para baixo ou de baixo para cima.
Transições são muito fáceis de usar, como se pode ver o código seguinte, a partir de fragmentos (da classe TransitionDemo da aplicação demonstração LWUIT), que são exemplos de como criar transições usando a métodos factory aplicável:
//creates an out transition |
Você pode ver que não existe diferença na forma como é criada uma transição, independentemente de saber se é uma transição comum ou uma transição 3D. Depois de criar uma transição, tem que ser instalado por uma forma desejada. Isso também é simples:
//f is the form for which transitions are being set |
Note que se você definir a saída de transição para uma forma que não seja necessária definir, a futura transição para o nova forma que está entrando na tela; conforme a forma antiga sai é automaticamente substituída por uma nova. Quando uma aplicação tem uma série de formas, acho que é uma boa prática definir apenas a saída de transições para todas as formas. Esta é a forma de se evitar conflitos e transições perdidas.
Uma Transição Personalizada
Uma das grandes coisas sobre LWUIT, é que ele suporta um bom negócio de personalização. Portanto, se você deseja uma transição que não está disponível classes transition, que vêm com a biblioteca, você pode escrever facilmente as suas próprias. Nesta seção iremos desenvolver uma simples transição para demonstrar as técnicas básicas para a criação de transições personalizadas. Antes de darmos início em nosso projeto, porém, gostaria de acrescentar uma ressalva aqui. A nova transição é apenas uma ferramenta para entender como transitions e motions trabalham. Para manter as coisas simples, tenho impostas algumas restrições que tornam esta classe imprópria para o uso geral. Alguns destes são:
- Não suporta diálogos.
-
Não é configurável e tem apenas um tivo de movimento -- horizontal, para a direita.
- Não verifica todas condições que podem se rencontradas em um dispositivo real.
Nossa transição é chamada Step Transition. Como o próprio nome sugere, ele faz a tela destino mover para a posição de uma série de passos discretos, ao contrário do Slide Transition. Outra diferença entre as duas transições é que com step transition, a próxima tela não aparece para empurrar o fonte da tela de saída - que se move sobre a fonte, gradualmente cobrindo-a.
Para criar uma transição personalizada, temos que escrever uma classe que estende Transition. Neste caso, a classe é StepTransition. Precisamos também de um modelo adequado para o movimento e, para a nossa demonstração, que é a classe StepMotion.
Começamos por olhar para a classe StepTransition. A classe Transition tem o seguintes métodos abstratos, que devem ser implementados por StepTransition:
- public abstract Transition copy()
- public abstract boolean animate()
- public abstract void paint(Graphics g)
Além disso, há o método vazio initTransition que deverá ser devidamente aplicado. Dependendo da natureza da transição, esse método não pode ser exigido a todos, caso em que você pode omiti-lo totalmente.
O método initTransition é uma chamada que é invocada no início de cada transição. Então este é o lugar para inicializar todos os parâmetros para o início da transição. Também deve-se instanciar o objeto StepMotion, que vai trabalhar fora da posição para renderizar a tela destino (lembre-se que essa é a única tela que se move) para cada frame. Nosso código para initTransition é bastante simples:
public void initTransition() |
Graças à simplicidade das nossas especificações, precisamos fazer apenas algumas coisas para iniciar a transição. O que fazemos é inicializar as variáveis necessárias para criar o objeto motion, criar o motion, e iniciá-lo em execução.
O método animate é chamado uma vez para cada frame. Este método verifica se o objeto motion está ausente ou se a transição está maior. A verificação para a conclusão da transição envolve chamada para o método isFinished de StepMotion, que retorna true se a transição for feita. Se lá não existir nenhum objeto motion ou se a transição estiver concluída, false é retornado para que esta atividade transição seja arquivada. Caso contrário, a transição continuará e a posição para desenhar a tela destino correspondente ao próximo frame é obtido. Finalmente, animate retorna true, assim, o próximo frame pode ser desenhado:
public boolean animate() |
Desde que StepTransition implementa Animation, tem um método paint que é invocado para recarregar a tela destino para cada frame. Como vemos no segmento de código acima, a posição para desenhar a tela destino é retornado pelo método getStep da classe StepMotion. Desde que o valor da posição altera apenas em passos discretos, ele pode permanecer inalterado ao longo de uma série de frames. Desenhando a(s) tela(s) para cada frame seria um desperdício de tempo de CPU. Para evitar isto, getStep retorna -1 se a situação manteve-se inalterada desde o último frame. O método paint verifica o valor da posição e chama paintSlideAtPosition para fazer a própria pintura somente quando necessário. O código para o método paint é dado abaixo:
public void paint(Graphics g) |
A atual renderização é feita pelo método paintComponent da classe Component após os contextos gráficos foi preparado pelos seguintes métodos:
- private void paintSlideAtPosition(Graphics g, int slideX, int slideY)
-
private void paint(Graphics g, Component cmp, int x, int y)
Para a nossa aplicação simples, poderíamos ter combinado todos os métodos paint. No entanto, tem conservada a estrutura da classe CommonTransitions que vem com a biblioteca. Espero que isto torne mais fácil de seguir a sequência de método para quem deseja estudar o código fonte.
O método paintSlideAtPosition primeiro verifica se a primeira forma de aplicação está sendo exibida e, nesse caso, inibe a transição. Caso contrário, configura a região clip e, mais importante para esta aplicação, as coordenadas para a tradução, de modo que o deslocamento apropriado da tela destino possa ocorrer. O próximo método cuida da real tradução, chama o paintComponent no Component para extrair a tela destino na posição certa e restaura a tradução original. O programa demonstrativo, assim, faz com que a tela destino mova-se sobre uma fonte estacionária. Isto acontece porque apenas a tela destino é repintada para cada nova posição. Se você quer conseguir um efeito push, basta descomentar as duas linhas de código em negrito no método paintSlideAtPosition. Isto fará com que a tela fonte igualmente seja repintada e você verá mover-se fora da área de exposição como a tela destino se move dentro. Os dois métodos são mostrados abaixo:
private void paintSlideAtPosition( |
O método copy retorna apenas uma cópia da etapa do objeto transição. Note que uma cópia é retornada e não o próprio objeto, assim a classe Display quer trabalhar com uma cópia.
|
O construtor de StepMotion tem quatro inteiros como parâmetros.
- sourcevalue: Esta é a posição inicial. Se a nova tela começa em movimento a partir da borda esquerda, então este será zero.
- destinationvalue: Este é a posição de finalização. Se a nova tela deverá parar na margem direita, então esta vai ser igual à largura da área de exposição.
- duration: O período de tempo (em milisegundos) que ao longo da transição deverá durar.
- steps: O número de passos de transição que deverá assumir.
O construtor, calcula o valor pelo qual a posição da tela destino tem que ser incrementada de um passo para o outro. Ele também calcula o tempo mínimo que deve decorrer antes da tela poder ser recarregada.
|
Já temos encontrado os três métodos de StepMotion. O único ponto a notar aqui é que o tamanho do passo calculado não pode ser um fator da distância total (destinationvalue - sourcevalue) a serem abordados, como estamos usando inteiro matemático. Nesse caso, o método getStep assegurará que a distância faltante é feita como se ela sempre pega mais um passo do que calculado número de passos. Isso é verdade porque isFinished retorna true quando o número de passos dados (stepnumber) excede o número de passos. Esse passo extra realmente não causa qualquer problema, como a position não é permitida para ultrapassar destinationvalue. Os métodos são:
//save the time as the beginning of an interval |
O MIDlet que põe todo o conjunto, também é bastante simples. Depois de chamar a init em Display, que inicia os parâmetros para a transição, e define-se o theme. Em seguida, cria dois rótulos para os dois formulários que serão utilizados na demonstração. Gostaríamos que os dois formulários fosse olhado um tanto diferente, de modo que a transição se tornasse visualmente proeminente. Então, o primeiro formulário é criado e suas barras de títulos são feitas diferentes a partir do tema configurado. O rótulo srclabel é acrescentado ao primeiro formulário e, assim, são os comandos. O MIDlet é então definido como o comando ouvinte para o formulário. Por último, o primeiro formulário é feito visível:
//init the LWUIT Display |
A próxima parte do código, que trata do segundo formulário, é quase o mesmo. A diferença fundamental é que as transições in e out são definidas para este formulário. Como esta demonstração tem apenas dois formulários, foi violada a própria recomendação e configuradas ambas transições para o mesmo formulário.
//create the second form |
Toda a ação tem lugar no método startApp. Agora, vamos cuidar dos comandos:
public void actionPerformed(ActionEvent ae) |
As três telas nas figuras 2 a 4 mostram imagens progressivas do passo da transição do primeiro para o segundo formulário.

Figura 2. Passo número 2 da transição

Figura 3. Passo número 5 da transição
Figura 4. Passo número 8 da transição
Conclusão
Vimos como usar as funções de animação e transição do LWUIT e como criar a nossa própria transição. LWUIT é uma biblioteca de evolução e somos obrigados a ver acréscimos ao seu atual repertório. Os desenvolvedores terão que atualizar constantemente os seus conhecimentos para permanecer a par das novas funções, capacidades e refinamentos. Vai ser preciso algum trabalho duro, mas será muito divertido também!
Recursos
- src_codes.zip: Código fonte e arquivos de recursos para a aplicação demonstração
- " Using Styles, Themes, and Painters with LWUIT" tem uma seção em arquivos de recursos e como usar Resource Editor para a construção de um arquivo de recurso.
- Lightweight User Interface Toolkit (LWUIT) project home tem um link para o código fonte.
- A baixa do pacote LWUIT
-
Sprint Wireless Toolkit pode ser descarregado a partir daqui.
Biswajit Sarkar é engenheiro elétrico com especialização em programação em automação industrial.