Componentes Personalizados com CustomItem Ricardo da Silva Ogliari
Introdução Devido ao crescente uso da plataforma Java 2 Micro Edition na programação para pequenos dispositivos, a necessidade de alguns componentes especiais surgiu ao natural, muitos deles não inclusos na API da MIDP (Mobile Information Device Profile), como por exemplo, gráficos e tabelas. Atualmente, podemos usar a classe Canvas para criarmos interfaces gráficas mais elaboradas, porém não pdoemos utilizar utilizar um Canvas junto com os outros Itens gerenciáveis pela classe Form. Contudo, uma das novas características presente na versão 2.0 da MIDP, é a inclusão de uma classe chamada CustomItem, que habilita o desenvolver a criar seus próprios componentes personalizados, além disso, as interfaces gráficas baseadas em um Form ganham mais interatividade, isto porque, é possível unir um CustomItem com os Itens presentes na MIDP. Nos próximos tópicos deste artigo, o leitor verá o conhecimento básico necessário para construir seus primeiros componentes, como estudo de caso, será desenvolvido uma projeto chamado JMTable, que objetiva implementar uma tabela semelhante aquela encontrada nos pacotes gráficos Swing e AWT.
Custom Item Semelhante ao que ocorre com uma classe derivada de Canvas, para implementar um componente personalizado deve-se ter ao menos uma classe que estenda da classe CustomItem e implemente alguns métodos de forma obrigatória, sendo eles: getMinContentHeight(), getMinContentWidth(), getPrefContentHeight(), getPrefContentWidth() e paint(). Além disso, o construtor da classe criada, deve ter uma referência ao construtor da sua classe pai, sendo ela o CustomItem, passando uma String que será o rótulo do componente, assim como ocorre com todos os Itens. A listagem 1 mostra o esqueleto da classe JMTable. public class JMTable extends CustomItem{ public JMTable (){ super("Tabela"); } protected int getMinContentHeight() {} protected int getMinContentWidth() {} protected int getPrefContentHeight(int param) {} protected int getPrefContentWidth(int param) {} protected void paint(Graphics graphics, int param, int param2) { }
Listagem 1 – Esqueleto da classe JMTable Os métodos getMinContentHeight() e getMinContentWidth() especificam a altura e largura mínima do componente, em contrapartida, os métodos getPrefContentHeight() e getPrefContentWidth() servem para configurar a altura e largura preferencial. O método paint() é o mais importante dos métodos que devem ser estendidos, pois é onde “desenhamos“ o que o componente exibirá na tela do celular, da mesma forma que especificamos o que uma classe Canvas exibirá. A única diferença entre no paint() das duas abordagens citadas anteriormente, são os
http://www.javafree.org/
A voz Java no Brasil – Pág. 1
parâmetros, no CustomItem é passado uma instância de um objeto Graphics, a largura e a altura do componente.
Estudo de caso: JMTable O objetivo deste estudo de caso é a criação de um componente que exibirá uma tabela no display do dispositivo, por isso o nome escolhido foi JMTable, em referência ao JTable do Swing; o 'M' é de Mobile. A figura 1 demonstra o componente em uso no emulador do software Wireless Toolkit. No Form estão inseridos um StringItem especificando de qual tabela seriam os dados, no caso da leitura ter sido feita em um RecordStore, e abaixo aparece o JMTable.
Figura 1 - Componente JMTable no emulador do Wireless Toolkit. A análise do código começará pelo construtor da classe, veja a listagem 2. O construtor foi modificado para receber as colunas e os dados que a tabela deve mostrar, também, recebe por parâmetro o tamanho da fonte e a largura do display do dispositivo, o último parâmetro é necessário porque desejamos que a tabela preencha toda a parte horizontal do dispositivo, como mostra a figura 1. No corpo do construtor são atribuídos os parâmetros recebidos para as variáveis globais, em seguida, com o auxílio do operador ternário definimos a fonte conforme o seu tamanho, recebido no construtor. A altura é calculada levando em conta o tamanho da fonte que será aplicada no cabeçalho da tabela, somando com a multiplicação entre a quantidade de dados e a altura da fonte aplicada nos dados tabela.
http://www.javafree.org/
A voz Java no Brasil – Pág. 2
public JMTable(Vector colunas, Vector dados, int tam_fonte, int largura) { super(""); this.largura = largura; this.colunas = colunas; this.linhas = dados; this.tam_fonte = tam_fonte; fonte = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, (tam_fonte==0 ? Font.SIZE_SMALL : tam_fonte == 1 ? Font.SIZE_MEDIUM:Font.SIZE_LARGE)); fonteCols = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, (tam_fonte==0 ? Font.SIZE_SMALL : tam_fonte == 1 ? Font.SIZE_MEDIUM:Font.SIZE_LARGE)); }
altura = fonte.getHeight()+(fonteCols.getHeight()*linhas.size());
Listagem 2 – Construtor da classe JMTable A definição das dimensões mínimas e das dimensões preferidas são mostradas na listagem 3, ambas são configuradas com a variável largura, que foi recebida por parâmetro no construtor, a mesma representa o tamanho horizontal da tela do dispositivo. A altura mínima é definida com a altura da instância de Font, chamada fonte, que é aplicada nos nomes das colunas, ou seja, o tamanho mínimo mostrará ao menos o cabeçalho da tabela; por sua vez, a altura preferida é a variável altura que foi definida ainda no construtor. protected int getMinContentHeight() { return fonte.getHeight(); } protected int getMinContentWidth() { return largura; } protected int getPrefContentHeight(int param) { return altura; } protected int getPrefContentWidth(int param) { return largura; }
Listagem 3 – Definição dos tamanhos Para melhor compreensão da explicação do principal e maior método da classe JMTable, o método paint(), de uma olhada rápida na listagem 4. As duas primeiras linhas servem para pintar a área de visualização do item com branco, isso é importante porque nem todas as JVM´s fazem este trabalho automaticamente, portanto, para garantir portabilidade é indicado fazer este procedimento. As linhas 4 e 5 pintam a área do cabeçalho, que terá uma cor acinzentada. Da linha 6 até a linha 12, é feito do desenho das bordas da tabela e das linhas verticais, as linhas 13 à 17 pintam as linhas horizontais. É importante analisar a
http://www.javafree.org/
A voz Java no Brasil – Pág. 3
definição do traçado setStrokeStyle(g.DOTTED).
pontilhado
na
linha,
utilizando
o
método
protected void paint(Graphics g, int w, int h) { 1 g.setColor(255, 255, 255); 2 g.fillRect(0, 0, w-1, h-1); 3 4 g.setColor(200, 200, 200); 5 g.fillRect(0, 0, w, fonte.getHeight()); 6 g.setColor(0, 0, 0); 7 g.drawLine(0, fonte.getHeight(), w, fonte.getHeight()); 8 for (int i = 1; i < colunas.size(); i++) 9 { 10 g.drawLine((w/colunas.size())*i, 0, (w/colunas.size())*i, h-1); 11 } 12 g.drawRect(0, 0, w-1, h-1); 13 g.setStrokeStyle(g.DOTTED); //desenha linhas horizontais entre as linhas da tabela 14 for (int i = 1; i < linhas.size(); i++) 15 { 16 drawLine(0, fonteCols.getHeight()+(fonte.getHeight()*(i)), w, fonteCols.getHeight() + (fonte.getHeight()*(i))); 17 } 18 g.setFont(fonteCols); 20 for (int i = 1; i <= colunas.size(); i++) 21 { 22 g.drawString (colunas.elementAt( i-1 ).toString(), ( (w/colunas.size())/2 ) * I + ( w / colunas.size() / 2 * (i - 1) ), fonteCols.getHeight(), Graphics.HCENTER|Graphics.BOTTOM); 23 } 24 g.setFont(fonte); 25 for (int i = 0; i < linhas.size(); i++) 26 { 27 if (i == indice) 28 g.setColor(0, 0, 255); 29 else if (i == indice_marc) 30 g.setColor(255, 0, 0); 31 else 32 g.setColor(0, 0, 0); 33 String[] dados = (String[]) linhas.elementAt(i); 34 for (int j = 1; j <= colunas.size(); j++) 35 { 36 g.drawString(dados[j-1], ((w/colunas.size())/2)*j + (w/colunas.size()/2*(j-1)), fonteCols.getHeight() + (fonte.getHeight()*(i+1)), Graphics.HCENTER|Graphics.BOTTOM); 37 } 38 } }
Listagem 4 – Definição do método paint O intervalo de linhas, que começa na 18 e vai até a 23, desenha os nomes dos cabeçalhos, e o restante do código desenha as linhas da tabela com seus respectivos valores. O conjunto de if-else presente na linha 28 até a linha 32 testa se o índice de
http://www.javafree.org/
A voz Java no Brasil – Pág. 4
navegação, que é controlado pelas setas direcionais up e down, está encima de um dos itens, ou ainda, se um dos itens está marcado, nenhuma das situações ocorrendo o último else é chamado e então, é usada a cor padrão, o preto. Para entender as variáveis indice e idice_marc olhe o código completo. O último método usado na classe JMTable a ser detalhado é o traverse(), que também é de grande importância, ele é usado para gerenciar o controle do foco, tanto quando o item ganha, mas também quando deve perder. Por padrão este método retorna false, ou seja, qualquer uma das teclas direcionais aplicada sobre o item fará com que o componente perca o foco, porém redefinimos o método para não deixar que isso acontecesse. A listagem 5 ilustra o método traverse(). protected boolean traverse(int dir, int viewportWidth, int viewportHeight, int[] visRect_inout ) { switch ( dir ) { case Canvas.UP: return testaParaCima(); case Canvas.LEFT: return testaParaCima(); case Canvas.DOWN: return testaParaBaixo(); case Canvas.RIGHT: return testaParaBaixo(); case NONE: // do nothing: just a form layout reflow return true; } }
return false;
}
Listagem 5 – Definição do método traverse Dos parâmetros recebidos pelo método traverse, o mais importante é justamente o primeiro, que indica o código da tecla que foi pressionada, por este motivo, no corpo do método é usado à cláusula switch para sabermos o que deve ser feito. As tecla direcionais UP e LEFT chamam o método testaParaCima(), que retorna um valor booleano, indicando se o usuário está em um índice válido, ou, se por exemplo, ele está no primeiro índice da tabela e usou UP, neste caso, o foco deve ser perdido. Da mesma forma as teclas DOWN e RIGHT chamam o método testaParaBaixo() retornando um valor true se o índice for válido, ou, se o usuário estiver no último elemento e pressionar DOWN o item deverá perder o foco. O caso NONE é se houve alguma atualização automática da área de desenho do CustomItem pela JVM, o que retornará sempre true. Por fim, a figura 1 mostra um menu com três sub-menus: adicionar, remover e editar. Estes Command's são inseridos diretamente ao CustomItem, mais uma característica presente na MIDP 2.0, pois na sua versão anterior, os Command's só podiam ser inseridos diretamente a um objeto da classe Form ou Canvas. As ações respectivas estão nos métodos adicionar() e remover() da classe JMTable.
http://www.javafree.org/
A voz Java no Brasil – Pág. 5
Conclusão Como visto ao longo do artigo, a classe CustomItem pode ser muito útil para construção de novos componentes que tenham uma interface mais amigável, e porque não, construir componentes extremamente úteis que podem ser disponibilizados em forma de código aberto. Indo um pouco além, pode-se criar componentes para gráficos semelhantes ao JfreeChart, para relatórios, e assim por diante. O que fica implícito neste artigo é que, com esta nova ferramenta disponível na MIDP 2.0, a imaginação dos desenvolvedores pode ser usada para o desenvolvimento de melhores componentes de GUI, melhorando a qualidade e a abrangência de sistemas J2ME.
Sobre o autor Ricardo da Silva Ogliari é formando em Ciência da Computação pela Universidade de Passo Fundo – RS. Desenvolveu sistemas mobile para empresas do Rio Grande do Sul, atualmente trabalha como desenvolvedor de aplicações direcionadas a telefones celulares na Kwead.com de São Paulo. Também, foi palestrante dos eventos JustJava 2005 e EJES (Encontro Java do Espírito Santo) 2005, além de ministrar alguns cursos sobre a tecnologia J2ME.
http://www.javafree.org/
A voz Java no Brasil – Pág. 6
This document was created with Win2PDF available at http://www.win2pdf.com. The unregistered version of Win2PDF is for evaluation or non-commercial use only. This page will not be added after purchasing Win2PDF.