Home > Artigos > Java ME >
Componentes Personalizados com CustomItem
Publicado por Tutoriais Admin em 17/08/2009 - 7.416 visualizações
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 {
publicJMTable () {
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 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.
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 definição do traçado pontilhado na linha, utilizando o método setStrokeStyle (g.DOTTED).
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 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.


Baixar PDF deste tutorial 