Towel – Release 1.2.2

30 06 2011

Hoje está sendo liberado um release do projeto Towel com uma pequena alteração enviado por Paulo Henrique.

A unica diferença é que agora é possivel mudar o texto do botão que aparece no Popup do CalendarView.

Para isso, basta usar o método setTodayString do CalendarView.

Ex:

CalendarView view = new CalendarView();
view.setTodayString("Hoje");

Para baixar a nova versão, basta entrar na pagina Towel Project do blog e seguir o link para o github.





CalendarView

1 06 2011

Esse artigo é sobre uma classe do projeto Towel, para utiliza-la, baixe a biblioteca na pagina Towel Project desse blog.

Esse componente foi feito em 1999 pelo meu professor de Java enquanto ele fazia estagio na area, por esse motivo eu desculpo ele por ter usado null-layout, mas refatorei ele para ser mais generico e independente de LayoutManagers.

O CalendarView é um componente que exibe um JTextField e um JButton juntos.
O JTextField serve para digitar dadas no formato ‘dd/MM/yyyy’ e o JButton abre uma JWindow que permite escolher a data em um calendario.

Para utiliza-lo é bem simples:

import java.awt.event.*;
import javax.swing.*;

import com.towel.swing.calendar.CalendarView;

public class CalendarViewTest {
	public CalendarViewTest() {
		JFrame frame = new JFrame("CalendarView");
		JPanel content = new JPanel();
		final CalendarView view = new CalendarView();
		JButton button = new JButton("X");
		content.add(view);
		content.add(button);
		button.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				System.out.println(view.getSelectedDate());
			}
		});

		frame.setContentPane(content);
		frame.pack();
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}

	public static void main(String[] args) {
		new CalendarViewTest();
	}
}

Esse codigo exibe a seguinte janela:

E quando pressionado o botao “..” a JWindow aparece com o DatePicker para escolher uma data.

Escolhendo alguma data o valor é exibido no campo de texto.

Para obter o valor que o usuario digitou (em uma ação por exemplo), temos dois métodos:

  • getText() que retorna o texto do TextField
  • getSelectedDate() que retorna um objeto Calendar com a data selecionada pelo usuario.
  • E no caso do código de exemplo, ao pressionar ‘X’ é exibido o toString do objeto Calendar que for escolhido.

    Como sempre, ainda é possivel melhorar esse componente, e essa é a lista do que já pode ser feito:

  • Um Layout melhor para o JWindow.
  • Internacionalizar os textos (nome do mês, inicial do dia, botão “hoje”)
  • Poder digitar datas no formato MM/dd/yyyy




  • Towel – Release 1.2

    25 05 2011

    Hoje dia 25/05 está sendo liberado o novo release do projeto Towel.

    As novidades são:

    • FieldResolver consegue obter/setar os valores dos atributos através da hierarquia do objeto;
    • Não é obrigatório declarar as anotações @Resolvable em todos os atributos, nesses casos o DefaultFormatter vai ser utilizado e o FieldHandler será o FieldAcessHandler padrão;
    • Um novo componente enviado pelo meu professor de Java foi incluido no pacote com.towel.swing sob o nome de CalendarView, que é um DatePicker para Swing; (Artigo em breve)

    Para baixar a nova versão, basta entrar na pagina Towel Project do blog e seguir o link para o github.

    Mais uma coisa está acontecendo hoje:

    Dia 25/05 é o Towel Day, em homenagem a Douglas Adams.
    Esse release foi planejado. ;)

    Abraço a todos, e feliz dia da toalha!





    JTableView

    14 02 2011

    Algo que eu planejava a algum tempo é oferecer um modo onde seja possivel ter colunas em uma JTable com valores agregados como, soma, média, menor, maior e etc.., foi com essa intenção que criei as AggregateFunctions. O model que faz isso para as colunas está pronto, e é o AggregateModel.
    Mas encontrei alguns desafios não triviais para fazer isso funcionar, não existe nem na JTable nem no JScrollPane um modo de adicionar um footer, a unica maneira que pensei para resolver isso é criando uma classe que estende JPanel que tenha um JScrollPane com uma “Main Table” e outro com uma linha fixa com o model de agregação dos valores da tabela principal. Então para encapsular essa “gambiarra” criei a classe JTableView, com ela é possivel utilizar esse novo recurso. Mas ainda tem alguns poucos defeitos que ainda não consegui resolver (uma contribuição se alguem souber é bem vinda ^^).

    Para utiliza-la, é necessario instanciar o JTableView com um modelo qualquer e depois as funções que serão utilizadas nas colunas do footer.

    Como exemplo, usaremos o modelo “Person”  para exibir alguns valores e aplicaremos algumas funções nas colunas.

    Person.java

    public class Person {
    	@Resolvable(colName = "Name")
    	private String name;
    	@Resolvable(colName = "Age")
    	private int age;
    	@Resolvable(colName = "Live")
    	private boolean live;
    
    	public Person(String name, int age, boolean live) {
    		this.name = name;
    		this.age = age;
    		this.live = live;
    	}
    }
    

    Com isso criamos o modelo e adicionamos alguns dados.
    PS:Para quem não sabe como funciona o ObjectTableModel pode ver aqui.

    		ObjectTableModel model = new ObjectTableModel(
    				new AnnotationResolver(Person.class), "name,age,live");
    
    		model.setEditableDefault(true);
    
    		model.add(new Person("A", 10, true));
    		model.add(new Person("B", 20, true));
    		model.add(new Person("C", 30, false));
    		model.add(new Person("D", 40, true));
    		model.add(new Person("E", 50, true));
    

    PS: JTableView não funciona apenas com o ObjectTableModel, mas ele é o mais simples de ser utilizado em qualquer ocasião.

    Agora com o modelo pronto, só precisamos criar o JTableView e colocar as funções de agregações que queremos na tabela.

    		JTableView view = new JTableView(model);
                    //Os indices são 0 based.
    		view.getFooterModel().setFunction(0, new FuncConcat("-"));
    		view.getFooterModel().setFunction(1, new FuncSum());
    

    PS: Para saber como funciona a API de AggregateFunctions, ver em CollectionsUtils.

    Não é necessario ter uma função para todas as colunas, estas ficaram em branco.

    Agora só basta colocar esse componente em algum lugar, como em um JFrame e exibi-lo.

    		JFrame frame = new JFrame();
    		frame.getContentPane().add(view);
    
    		frame.pack();
    		frame.setLocationRelativeTo(null);
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		frame.setVisible(true);
    

    Com isso temos o seguinte resultado.

    Ao alteramos os valores na tabela principal o novo resultado é calculado no footer.

    Como eu disse, este componente não está perfeito, ele tem suporte a re-alocação de colunas, caso voce esteja clicando no header da main table e arrastando a coluna para outro lugar.

    Mas ela não tem suporte para resize de colunas, o resultado se esticar algumas colunas vai ser bem estranho, se alguém souber como resolver será muito interessante para o projeto.
    Então ainda não indico utilizar este componente a não ser em caso que deixem a tabela fixada e sem suporte para mudar o tamanho das colunas, uma versão melhorada desse componente vai ser liberado assim que for arrumado estes erros pendentes.





    DynamicFormatter

    12 02 2011

    Essa classe é uma contribuição de Felipe Priuli para o projeto Towel, e também foi ele que escreveu este artigo de como utiliza-lo.

    Esta classe implementa com.towel.bean.Formatter e foi criado, primeiramente, para ser utilizado no ObjectComboBoxModel, permitindo criar um Formatter dinamicamente para
    as classes que serão utilizada como model de um ComboBox. Em outras palavras, ao invés de criar um Formatter para cada classe é possível utilizar este Formatter
    para representar as classes de maneira genérica.

    Ele funciona permitindo adicionar FieldResolvers a uma lista que será utilizada para obter os valores dos objetos do modelo, para ver uma explicação detalhada de como o FieldResolver funciona, veja no artigo sobre ObjectTableModel.

    Vamos para o exemplo:

    Primeiro! Vamos criar a classe Person:

    public class Person {
            private String name;
            private int age;
            public Person(String str, double d) {
                    this.name = str;
                    this.age = (int) d;
            }
            public String toString() {
                    return "Name: " + name + " age: " + age;
            }
    }
    

    Nós queremos mostrar informações sobre a pessoa em cada iten do JComboBox, mas ao inves de criar um Formatter para a classe Pessoa, vamos utilizar o DynamicFormatter.

    Abaixo um exemplo de como criar um JComboBox de ‘Person’ utilizando o model ObjectComboBoxModel com o DynamicFormatter:

    import javax.swing.*;
    
    import com.towel.awt.ann.*r;
    import com.towel.bean.DynamicFormatter;
    import com.towel.combo.swing.ObjectComboBoxModel;
    import com.towel.el.FieldResolver;
    
    public class ComboBoxDynamicFormatterTest extends JFrame {
    	private ObjectComboBoxModel model;
    	@Action(method = "showPerson")
    	private JButton button;
    
    	public ComboBoxDynamicFormatterTest() {
    		super("ComboBoxModel");
    		model = new ObjectComboBoxModel();
    
    		DynamicFormatter formatter = new DynamicFormatter(
    				Person.class, " - ");
    		formatter.addField(new FieldResolver(Person.class, "name"));
    		formatter.addField(new FieldResolver(Person.class, "age"));
    
    		model.setFormatter(formatter);
    		// Adicionado as classes Person no model
    		model.add(new Person("A", 10.0));
    		model.add(new Person("B", 20.0));
    		model.add(new Person("C", 30.0));
    		model.add(new Person("D", 40.0));
    		model.add(new Person("E", 50.0));
    
    		JComboBox combo = new JComboBox(model);
    		JPanel cont = new JPanel();
    		cont.add(combo);
    		cont.add(button = new JButton("Show"));
    		setContentPane(cont);
    		pack();
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		setLocationRelativeTo(null);
    		setVisible(true);
    
    		new ActionManager(this);// Necessary to map @Action to the method
    	}
    
    	private void showPerson() {
    		Person p = model.getSelectedObject();
    		System.out.println(p.toString());
    	}
    
    	public static void main(String[] args) {
    		new ComboBoxDynamicFormatterTest();
    	}
    }
    

    Ele ainda tem a vantagem que se for necessario exibir um campo que seja GregorianCalendar, é possivel adicionar o Formatter ao FieldResolver desse atributo para exibir um valor mais amigavel do que o valor padrão do toString.

    Os métodos utilizados para criar os itens em um ComboBox de maneira generica, pela classe DynamicFormatter são:

    • addField(FieldResolver) : Ele permite adicionar um resolver a lista que será utilizada para montar a String que representa o objeto.
    • format(Object) : O método ‘format’ faz a conversão do objeto para texto. O texto é aquele que será mostrado no item do ComboBox.
    • setSeparator(String) : Este método permite que você indique o texto que irá separar o texto dos itens e é usado quando existir mais de um campo que irá aparecer no item de JComboBox
    • getFieldList() : Este método retorna uma lista de FieldResolver. O FieldResolver é responsável por obter os valores dos atributos (Fields) dos objetos, estes valores são então mostrados nos itens combobox.




    JImagePanel

    11 02 2011

    Essa classe está no projeto Towel, para utiliza-la, entre na pagina “Towel Project” e baixe a versão mais atual para adicionar ao classpath.

    JImagePanel é uma classe criada pelo ViniGodoy, é um JPanel que tem uma imagem de background no fundo e modificada por mim para ter suporte a exibir uma sequencia de imagens através do tempo.

    Com ela, é possivel criar telas em Swing com imagens de fundo e ainda ter componentes por cima, diferente do que seria possivel com um JLabel, e ela é extremamente simples de usar.

    Para utilizar:

    • Criar uma instancia com uma ou varias imagens;
    • Escolher o FillType, opcional, este é o modo de como a imagem vai se comportar quando a area disponivel for maior que ela, as opções são: RESIZE (crescer), CENTER (centralizado), SIDE_BY_SIDE (Lado a lado, assim como no Desktop do windows). Por default o FillType é RESIZE;
    • Adicionar ela a um Container, como um JFrame por exemplo;
    • Adicionar componentes sobre ela, isso é feito assim como em um JPanel;
    • Exibi-la.

    O seguinte código representa isso.

    import java.awt.Dimension;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    
    import com.towel.swing.img.JImagePanel;
    
    public class JImagePanelSingleTest {
    	public static void main(String[] args) throws Throwable {
    		JImagePanel panel = new JImagePanel(
    				loadImage("/home/marcos/imgs/1.png"));
    
    		JFrame frame = new JFrame();
    		frame.setPreferredSize(new Dimension(100, 100));
    		frame.add(panel);
    		frame.pack();
    		frame.setLocationRelativeTo(null);
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		frame.setVisible(true);
    	}
    
    	private static BufferedImage loadImage(String file) throws IOException {
    		return ImageIO.read(new File(file));
    	}
    }
    

    Com isso, temos um resultado como esse:

    Se não quisermos que aumente nossa imagem, podemos usar o FillType como no seguinte código.

    public class JImagePanelSingleTest {
    	public static void main(String[] args) throws Throwable {
    		JImagePanel panel = new JImagePanel(
    				loadImage("/home/marcos/imgs/1.png"));
    
    		panel.setFillType(JImagePanel.FillType.CENTER);
    
    		JFrame frame = new JFrame();
    		frame.setPreferredSize(new Dimension(100, 100));
    		frame.add(panel);
    		frame.pack();
    		frame.setLocationRelativeTo(null);
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		frame.setVisible(true);
    	}
    
    	private static BufferedImage loadImage(String file) throws IOException {
    		return ImageIO.read(new File(file));
    	}
    }
    

    Com isso temos o seguinte resultado:

    Para utilizar o modo em loop, basta usar o construtor que recebe um long e um array de imagens, o primeiro parametro é o tempo que levará para mudar entre uma imagem e outra em millisegundos.
    Assim que criado uma Thread é disparada para atualizar as imagens, essa Thread é daemon e não vai interferir caso a Thread principal termine.
    Também é possivel utilizar o FillType em modo de loop.

    O código é praticamente o mesmo, ficando assim:

    public class JImagePanelLoopTest {
    	public static void main(String[] args) throws Throwable {
    		JImagePanel panel = new JImagePanel(10, new BufferedImage[] {
    				loadImage("/home/marcos/imgs/1.png"),
    				loadImage("/home/marcos/imgs/2.png"),
    				loadImage("/home/marcos/imgs/3.png"),
    				loadImage("/home/marcos/imgs/4.png"),
    				loadImage("/home/marcos/imgs/5.png"),
    				loadImage("/home/marcos/imgs/6.png"),
    				loadImage("/home/marcos/imgs/7.png"),
    				loadImage("/home/marcos/imgs/8.png"),
    				loadImage("/home/marcos/imgs/9.png"),
    				loadImage("/home/marcos/imgs/10.png"),
    				loadImage("/home/marcos/imgs/11.png"),
    				loadImage("/home/marcos/imgs/12.png") });
    
    		JFrame frame = new JFrame();
    		frame.setPreferredSize(new Dimension(100, 100));
    		frame.add(panel);
    		frame.setLocationRelativeTo(null);
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		frame.setVisible(true);
    	}
    
    	private static BufferedImage loadImage(String file) throws IOException {
    		return ImageIO.read(new File(file));
    	}
    }
    

    O resultado da execução é uma animação, então não é possivel colocar uma imagem aqui.

    Ainda a fazer nessa classe é o seguinte:

    • Começar a animação com um método start() por exemplo
    • Oferecer um modo para executar a animação apenas uma vez e parar em alguma imagem.




    Towel v1.1

    9 02 2011

    Changelog da versão 1.1 do Towel.

    Pastas de imagens do TableFilter incluida.

    JImagePanel agora tem suporte para exibir uma sequencia de imagens e o ImageLoopPanel que havia antigamente foi depreciado.
    Artigo sobre como usar ele adicionado 11/02/2011, veja aqui.

    Felipe Priuli fez uma contribuição no projeto adicionando mais uma classe util, a DynamicFormatter, ela é um Formatter genérico que foi feita para facilitar o uso do ObjectComboBoxModel, mas ela é genérica e serve para qualquer caso que precise de um Formatter.

    Algo que eu planejava a algum tempo é oferecer um modo onde seja possivel ter colunas em uma JTable com valores agregados como, soma, média, menor, maior e etc.., foi com essa intenção que criei as AggregateFunctions. O model que faz isso para as colunas está pronto, e é o AggregateModel.
    Mas encontrei alguns desafios não triviais para fazer isso funcionar: Não existe nem na JTable nem no JScrollPane um modo de adicionar um footer, criei o componente JTableView, com ele é possivel utilizar esse novo recurso, mas ainda tem alguns defeitos que ainda não consegui resolver (uma contribuição se alguem souber é bem vinda :) ).

    Existe um novo pacote agora, com.towel.sound, ele contém classes para audio que foi migrada do JGF (Java Game Framework) um projeto que o ViniGodoy começou, que agora será descontinuado por que o Alegria irá realizar tudo que ele faz e mais ainda.

    Desenvolvido pelo ViniGodoy agora foi migrada para o projeto a classe HashBuilder, ela serve para calcular o hash dos objetos facilmente.

    Outro colaborador dessa versão é o Eric Yuzo que melhorou o ObjectTableModel, agora contém mais comentários e também foi alterado para utilizar o fireTableCellChanged no lugar de fireTableDataChanged que eu estava usando anteriormente.

    ObjectTableModel agora implementa Iterable, então é possivel percorrer a lista dos dados com o for-each.

    Alguém que ajudou bastante comentando muitas classes foi o Marco A. Biscaro (marcobiscaro do GUJ), graças a ele e outras contribuições agora o Java Doc está quase completo.

    Em breve vou gerar o Java Doc e hospedar em algum lugar, mas por enquanto já é possivel navegar pelas classes e ler os docs nas proprias classes.

    Estarei publicando os artigos desses recursos em pouco tempo.

    A pagina Towel Project foi expandida, vale a pena dar uma olhada e baixar a nova versão.








    Seguir

    Obtenha todo post novo entregue na sua caixa de entrada.

    Junte-se a 154 outros seguidores