|
|
Tiago Silveira
Muitos componentes Swing usam Renderers em seu funcionamento. Saiba como utilizar ao máximo sua capacidade.
Download do material relacionado ao tutorial
Desenhando componentes
Todos os componentes swing são desenhados exatamente do mesmo modo:
Primeiro, pinta-se o fundo, caso o componente seja opaco. Se não for, esta parte não acontece
A seguir, pinta-se o componente em si. Você pode alterar como um componente é desenhado na tela sobrescrevendo o método paintComponent(Graphics) da classe JComponent.
A seguir, se o componente tiver borda, ela é desenhada.
Se o componente tiver filhos, eles são desenhados em cima.
Todos os componentes são desenhados com um clipping, isto é, um retângulo que delimita a área onde eles podem pintar. Se você desenhar algo fora dos Bounds de um JComponent, não vai aparecer nada na tela.
Componentes filhos e performance
Se você fosse implementar uma JTable, como você faria? Provavelmente tentaria algo como um GridLayout e colocaria um JLabel ou outro componente para cada célula da tabela, certo?
JTables e JLists usam uma abordagem diferente: sabendo que suas células só precisam desenhar-se e serão muito parecidas, por que não utilizar apenas um componente para pintar tudo? Para uma JTable, cada coluna tem seu próprio Renderer. Uma JList utiliza um único renderer para todas as suas células.
Ou seja, quando uma tabela pinta uma coluna, ela escolhe o renderer certo, move-o para a posição onde quer que ele seja desenhado, e chama seu método paintComponent() diretamente. A seguir, move-o para a próxima posição, lê os valor daquela célula do TableModel, passa esse valor para o Renderer se configurar, e pinta-o novamente. Por isso, se você escolher um JButton para renderer, suas células não serão objetos clicáveis: um desenho de um botão não é um botão!
Um JLabel comum gera um evento quando seu estado muda, pedindo para ser pintado novamente. Renderers são pintados sob demanda e não geram eventos. Mais tarde voltaremos a isso.
Um Renderer de exemplo
Suponhamos que você tem um editor de texto que mostra coloridos tokens (grupos de palavras ou caracteres) pré-definidos, e uma das telas de configuração possui uma tabela em que cada linha contém os seguintes dados:
Tipo de token
Cor
Para a coluna "Cor", você quer que apareça não um nome de cor, mas a cor em si. Você pode definir um TableCellRenderer assim:
1 public class ColorRenderer extends DefaultTableCellRenderer {
2 ...
3 }
|
Um DefaultTableCellRenderer é um JLabel especial que não gera eventos quando seu estado muda e implementa a interface javax.swing.table.TableCellRenderer. Esse é o método que queremos sobrescrever:
1 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
2 Color color = (Color) value;
3 super.setBackground(color);
4 return this;
5 }
|
O que veremos? A célula inteira é pintada com a cor que estiver no modelo. Podemos fazer algo mais genérico do que isso:
01 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
02 Class c = table.getColumnClass(column);
03 if (Color.class.isAssignableFrom(c)) {
04 // esta coluna pode ser tratada como cor
05 Color color = (Color) value;
06 super.setBackground(color);
07 return this;
08 }
09 else {
10 return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
11 }
12 }
|
O Renderer acima pode desenhar qualquer coluna, não apenas colunas contendo instâncias de java.awt.Color. Repare que utilizamos o método getColumnClass() da tabela, e o seu TableModel deve estar configurado para retornar a classe certa quando pedirmos.
Agora vamos imaginar que não queremos a coluna inteira pintada, mas apenas um retângulo no centro com a cor certa. Para isso, vamos definir uma variável que guarda a cor e alterar como um JLabel se desenha assim:
01 public void paintComponent(Graphics g) {
02 // primeiro, pintamos um JLabel normal:
03 super.paintComponent(g);
04 // calculamos o retângulo baseado no tamanho do componente:
05 Rectangle r = super.getBounds();
06 r = (Rectangle) r.clone();
07
08 // não queremos pintar sob a borda:
09 Insets insets = super.getInsets();
10 r.x += insets.left;
11 r.y += insets.top;
12 r.width -= insets.right;
13 r.height -= insets.bottom;
14
15 // vamos usar 80% da área disponível para nossa cor
16 r.width -= r.width/5;
17 r.height -= r.height/5;
18 // preenchemos o retângulo com a cor escolhida...
19 g.setColor(colorValue);
20 g.fillRect(r.x, r.y, r.width, r.height);
21 // ...e desenhamos uma pequena borda preta
22 g.setColor(Color.BLACK);
23 g.drawRect(r.x, r.y, r.width, r.height);
24 }
|
O código completo da classe está [link href=ColorRenderer.java]aqui[/link].
Utilizando outros componentes como Renderers
Em vez de um JLabel, você pode contruir um JCheckbox como Renderer assim:
1 public class CheckBoxRenderer extends JCheckBox implements TableCellRenderer {
2 ...
3 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
4 Boolean newSelectedValue = (Boolean) value;
5 setSelected(newSelectedValue.booleanValue());
6 return this;
7 }
8 }
|
Neste caso, você terá que sobrescrever os métodos que geram eventos de tela (validate(), revalidate(), repaint()) e de JavaBeans (firePropertyChange()). Isso porque você não quer que a AWT se preocupe em pintar novamente seu Renderer quando seu estado mudar, nem notifique outros componentes.
O código completo da classe está em anexo ao tutorial.
Conclusão
Como você pode perceber, é fácil inventar renderers complexos para suas aplicações. Todos baseiam-se nos mesmos princípios apresentados aqui.
Divirta-se!!
|
|
|