Como impressionar seus amigos com Java
Olá, já estou a algum tempo na Caelum mas só agora estreando no blog e, para compensar a demora, resolvi fazer um pouco diferente. O título deve estar chamando a atenção e antes de colocar o assunto de verdade (está bem, sei que estou valorizando um pouco), vou apenas lembrar da pessoa que me mostrou o assunto e da que me sugeriu colocar no blog. Ricardo e Paulo. Pronto, direto ao assunto.
Certamente muitos desenvolvedores Java já passaram pelo problema de ter de mostrar alguns recursos interessantes da plataforma. Certamente o HelloWorld não é um grande atrativo. Talvez demonstrar como é fácil fazer relacionamentos com tabelas associativas e chaves compostas através do Hibernate ajude bastante, mas ainda falta certo apelo visual.
Existe uma API feita em Java de livre uso, chamada Prefuse que utiliza-se do Java 2D para tornar simples tarefas complicadas visualmente, como por exemplo, renderizar grafos de maneira visualmente agradável e ainda por cima animada, permitindo inclusive uma interação do usuário através de drag-and-drop.
Uma aplicação mais prática e um tanto quanto útil, é a representação do relacionamento de tabelas ou mesmo de objetos, quando você precisa navegar entre uma grande quantidade de informações e quer uma maneira mais rápida em vez de pular de página em página. A idéia deste post é apenas fazer a introdução ao prefuse, sem entrar em muitos detalhes da API.
Veja abaixo o código para criar uma aplicação mostrando visualmente o relacionamento das fronteiras dos estados do Brasil.
/\*\* \* @author Thadeu Russo \* \*/ public class Visualizacao { /\*\* Display onde será desenhada a visualização \*/ private Display display; /\*\* Lista de actions relacionadas ao layout \*/ private ActionList layout; /\*\* Lista de actions relacionada as cores \*/ private ActionList color; /\*\* renderizador dos labels \*/ private LabelRenderer labelRenderer; /\*\* \* Carrega as informações que estao no \* arquivo sourceFile para um novo grafo \* @param sourceFile \* @return graph \* @throws DataIOException no caso de \* problemas com a leitura dos dados \* \*/ private Graph loadGraph(String sourceFile) throws DataIOException{ return new GraphMLReader().readGraph(sourceFile); } /\*\* \* Este método retorna o display para a visualization informada \* @return display \* @throws DataIOException \* \*/ private Display getDisplay() throws DataIOException { Visualization visualization = this.buildVisualization( this.loadGraph("data/brasil.xml") ); /\*\* troquei a visualização? \*/ if (this.display == null || !this.display.getVisualization().equals(visualization)) { this.display = new Display(visualization); this.display.setSize(720, 500); this.display.addControlListener(new DragControl()); this.display.addControlListener(new PanControl()); this.display.addControlListener(new ZoomControl()); this.display.addControlListener(new WheelZoomControl()); this.display.pan(200, 350); this.display.setForeground(Color.GRAY); this.display.setBackground(Color.WHITE); } return this.display; } /\*\* \* Recupera as actions relacionadas ao layout \* @return ActionList relacionados ao layout \* \*/ private ActionList getLayout() { if (this.layout == null) { this.layout = new ActionList(Activity.INFINITY); this.layout.add(new ForceDirectedLayout("graph")); this.layout.add(new RepaintAction()); } return this.layout; } /\*\* \* Recupera a configuração de cores e actions relacionadas a esta \* @return action list \* \*/ private ActionList getColor(){ if (this.color == null) { DataColorAction fill = new DataColorAction("graph.nodes", "region", Constants.NOMINAL, VisualItem.FILLCOLOR, this .getColorPalette()); ColorAction text = new ColorAction("graph.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0));
ColorAction edges = new ColorAction("graph.edges", VisualItem.STROKECOLOR, ColorLib.gray(200));
this.color = new ActionList(); color.add(fill); color.add(text); color.add(edges); } return this.color; } /\*\* \* Monta a visualização a ser mostrada no display \* @param graph O grafo a ser renderizado \* @return visualization \* \*/ private Visualization buildVisualization(Graph graph){ Visualization visualization = new Visualization(); visualization.add("graph", graph); visualization.setRendererFactory( new DefaultRendererFactory(this.getLabelRenderer()) ); visualization.putAction("color", this.getColor()); visualization.putAction("layout", this.getLayout()); visualization.run("color"); visualization.run("layout"); return visualization; } /\*\* \* Recupera o renderizador de rótulos (labels) \* @return label renderer \* \*/ private LabelRenderer getLabelRenderer() { if (this.labelRenderer == null) { this.labelRenderer = new LabelRenderer("state"); this.labelRenderer.setRoundedCorner(8, 8); } return this.labelRenderer; }
/\*\* \* Recupera a paleta de cores a ser usada na renderização \* @return paleta de cores \* \*/ private int \[\] getColorPalette(){ return new int\[\] { ColorLib.rgb(255, 128, 128), ColorLib.rgb(255, 128, 64), ColorLib.rgb(176, 176, 100), ColorLib.rgb(100, 255, 64), ColorLib.rgb(128, 255, 255) }; } public static void main(String\[\] args) throws DataIOException { Visualizacao visualizacao = new Visualizacao(); JFrame frame = new JFrame("Grafo do Mapa do Brasil por Regiões"); frame.setDefaultCloseOperation(JFrame.EXIT\_ON\_CLOSE); frame.add(visualizacao.getDisplay()); frame.pack(); frame.setVisible(true); } }
Aqui, a informação para gerar o grafo apresentado é lida de um XML. Poderia ser muito bem carregada a partir de um banco de dados (interessante ler o metadata do banco para gerar relacionamento entre tabelas, não?). O código acima serve de boa base para você fazer sua própria aplicação. Creio que o prefuse seja uma opção bastante interessante em vez de usar tecnologias de visualização como Flash, onde você ainda teria o trabalho de fazer toda a integração, em vez de diretamente implementar listeners java.
E vocês, que APIs consideram interessantes para demonstrar o poder e em especial a simplicidade do Java?