Em um programa normalmente é esperado que o usuário tenha acesso a uma quantidade indefinida de informação.
Pense no app iFood por exemplo, onde o usuário tem acesso a uma lista de restaurantes e pratos.
Ou em uma busca do sistema de uma livraria, onde o usuário tem acesso a uma tabela de livros, cada um com informações como editora, autores e preço.
Nesta aula vamos aprender a fazer uma tabela em Java:
Mas antes: Vamos criar as classes que serão trabalhadas durantes os exemplos práticos da aula
Para os exemplos vamos usar a classe entidade Animal
com
public class Animal {
private int id;
private String nome;
private int idade;
public Animal(int id, String nome, int idade) {
this.id = id;
this.nome = nome;
this.idade = idade;
}
public int getId() {return id;}
public String getNome() {return nome;}
public int getIdade() {return idade;}
@Override
public String toString() {return getNome();}
}
Para simular um banco de dados vamos usar uma classe chamada DAODummy
que tem uma lista de animais e retorna o resultado de uma busca.
public class DAODummy {
List<Animal> animais = new ArrayList<>(Arrays.asList(
new Animal(0, "Brutus", 15),
new Animal(1, "Chirriro", 14),
new Animal(2, "Xitara", 13),
new Animal(3, "Mili", 12),
new Animal(4, "Jade", 11)
));
public List<Animal> buscaAnimais(String nome) {
List<Animal> animaisResult = new ArrayList<>();
for(Animal a: animais)
if(a.getNome().toLowerCase().contains(nome.toLowerCase()))
animaisResult.add(a);
return animaisResult;
}
}
Definidas estas classes auxiliares, vamos ver como criar uma tabela.
Para fazer uma tabela em Java precisamos de, no mínimo, 3 elementos:
TableModel
que contem os dados da tabela.JTable
que é o objeto tabela, e onde podemos configurar o jeito que os dados aparecem.JScrollPane
que é um painel, com um scroll para conter a tabela.Para criar um TableModel
completo você precisaria extender de AbstractTableModel
. Para este exemplo simples vamos usar um table model pronto chamado DefaultTableModel
.
Para criar um DefaultTableModel
basta passar os nomes das colunas e a quantidade de linhas iniciais (no caso serão 0).
Então teremos o seguinte bloco de código para criar uma tabela dentro de um panel:
String[] nomesColunas = {"Nome", "Idade"};
DefaultTableModel dtm = new DefaultTableModel(nomesColunas, 0);
JTable table = new JTable(dtm);
JScrollPane sp = new JScrollPane(table);
Explicação:
DefaultTableModel
com o nome das colunas e 0 linhas.JTable
que usará o DefaultTableModel
JScrollPane
que terá apenas o JTable
dentro dele.Vamos ver como colocar informações dentro da tabela:
void preencheTabela(List<Animal> animais) {
dtm.setRowCount(0);
for(Animal a: animais) {
Object[] row = new Object[2];
row[0] = a.getNome();
row[1] = a.getIdade();
dtm.addRow(row);
}
}
Sempre que vamos mexer nos dados da tabela, devemos mexer no TableModel dtm
.
dtm.setRowCount(0);
.Object[] row = new Object[2];
row[0] = a.getNome();
row[1] = a.getIdade();
dtm.addRow(row);
O seguinte código irá montar um exemplo completo do que foi aprendido até agora:
public class TestandoJTable extends JFrame{
String[] nomesColunas = {"Nome", "Idade"};
DefaultTableModel dtm = new DefaultTableModel(nomesColunas, 0);
JTable table = new JTable(dtm);
JScrollPane sp = new JScrollPane(table);
JTextField buscaField = new JTextField(20);
DAODummy dao = new DAODummy();
public TestandoJTable() {
setLayout(new BoxLayout(this.getContentPane(), BoxLayout.PAGE_AXIS));
add(buscaField);
JButton buscaButton = new JButton("Buscar");
add(buscaButton);
sp.setPreferredSize(new Dimension(400, 400));
add(sp);
AcaoBusca ab = new AcaoBusca();
buscaButton.addActionListener(ab);
buscaField.addActionListener(ab);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
void preencheTabela(List<Animal> animais) {
dtm.setRowCount(0);
for(Animal a: animais) {
Object[] row = new Object[2];
row[0] = a.getNome();
row[1] = a.getIdade();
dtm.addRow(row);
}
}
public static void main(String[] args) {
new TestandoJTable();
}
class AcaoBusca implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
String keyword = buscaField.getText();
List<Animal> animais = dao.buscaAnimais(keyword);
preencheTabela(animais);
}
}
}
Se você reparar o exemplo anterior, cada célula na tabela pode ser editável, vamos aprender a desligar este recurso.
Perceba que essa edição não muda o estado do objeto original.
Infelizmente não basta mudar a configuração na JTable
, precisamos criar uma classe filha da JTable
e fazer @Override
do método public boolean isCellEditable(int row, int column)
.
O bom é que podemos fazer isso na mesma linha que criamos a JTable
. Mas, ao invés de criar a JTable
, vamos criar uma classe anônima filha:
JTable table = new JTable(dtm) {
@Override public boolean isCellEditable(int row, int column) {
return false;
}
};
Podemos selecionar uma linha na tabela usando o mouse, mas como pegar essa informação e passar para o código?
int rowIndex = table.getSelectedRow();
dtm.getValueAt(rowIndex, colIndex)
Então se quisermos pegar o nome do animal da linha selecionada podemos usar
String selecionaLinha() {
int rowIndex = table.getSelectedRow();
String nome = dtm.getValueAt(rowIndex, 0).toString();
return nome;
}
Animal
da Linha Podemos fazer ainda melhor e pegar o objeto da linha.
Temos que consertar um detalhe: não guardamos nenhum objeto na linha, apenas os dados do objeto. Mais ainda, não guardamos nem todos os dados do objeto (faltou o id).
Como resolver isso?
Perceba que o toString
da classe Animal
é apenas o nome do animal.
Isso tem um propósito: podemos guardar o objeto na coluna de nomes!
Assim, quando o objeto for mostrado na tabela, só seu nome será mostrado; mas quando for acessado, o objeto inteiro será acessado!
Então vamos reescrever a função de preencher a tabela para guardar o objeto na coluna nomes:
void preencheTabela(List<Animal> animais) {
dtm.setRowCount(0);
for(Animal a: animais) {
Object[] row = new Object[2];
row[0] = a; // Soh precisou mudar essa linha!!!
row[1] = a.getIdade();
dtm.addRow(row);
}
}
Agora para recuperar o objeto podemos simplesmente fazer
Animal selecionaLinha() {
int rowIndex = table.getSelectedRow();
Animal animal = (Animal)dtm.getValueAt(rowIndex, 0);
return animal;
}
Vamos fazer modificar o programa anterior para que, quando clicado com duplo clique, apareça uma mensagem dizendo qual objeto foi selecionado.
public class TestandoJTable extends JFrame{
String[] nomesColunas = {"Nome", "Idade"};
DefaultTableModel dtm = new DefaultTableModel(nomesColunas, 0);
JTable table = new JTable(dtm) {
@Override public boolean isCellEditable(int row, int column) {return false;}
};
JScrollPane sp = new JScrollPane(table);
JTextField buscaField = new JTextField(20);
DAODummy dao = new DAODummy();
public TestandoJTable() {
setLayout(new BoxLayout(this.getContentPane(), BoxLayout.PAGE_AXIS));
add(buscaField);
JButton buscaButton = new JButton("Buscar");
add(buscaButton);
sp.setPreferredSize(new Dimension(400, 400));
add(sp);
table.addMouseListener(new AcaoClicaTabela());
AcaoBusca ab = new AcaoBusca();
buscaButton.addActionListener(ab);
buscaField.addActionListener(ab);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
void preencheTabela(List<Animal> animais) {
dtm.setRowCount(0);
for(Animal a: animais) {
Object[] row = new Object[2];
row[0] = a;
row[1] = a.getIdade();
dtm.addRow(row);
}
}
Animal selecionaLinha() {
int rowIndex = table.getSelectedRow();
Animal animal = (Animal)dtm.getValueAt(rowIndex, 0);
return animal;
}
public static void main(String[] args) {
new TestandoJTable();
}
class AcaoBusca implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
String keyword = buscaField.getText();
List<Animal> animais = dao.buscaAnimais(keyword);
preencheTabela(animais);
}
}
class AcaoClicaTabela extends MouseAdapter{
@Override
public void mouseClicked(MouseEvent e) {
if(e.getClickCount()==2) {
Animal a = selecionaLinha();
if(a==null) return;
String message =
"Animal selecionado: " + a.getId() + " - " + a.getNome() + "("+a.getIdade()+")";
JOptionPane.showMessageDialog(null, message);
}
}
}
}
OBS: Perceba que usamos um MouseAdapter
ao invés de um MouseListener
. Os dois são a mesma coisa. A diferença é que no MouseAdapter
não precisamos fazer Override dos métodos que não queremos usar.
Model-View-Controller é um padrão de arquitetura de software usado para implementa uma Interface de Usuário Final.
O objetivo do MVC é separar a lógica em três elementos:
O MVC é usado em várias áreas, em vários tipos de projetos, aqui veremos exemplos de MVC para um projeto em Java.
Neste curso faremos projetos usando o padrão MVC. Todos os projetos seguirão o seguinte padrão:
interface
View
, com
Controller
poderá fazer para o View
addActionListener
para cada interação que o usuário, indiretamente, poderá fazer com o Model
interface
Model
, com todas as requisições que o Controller
poderá fazer para o Model
.Controller
que
View
e de Model
em seu constructor
, porém, trabalha apenas com as interfaces de View
e Model
.ActionListener
para cada interação que o usuário poderá fazer com o Model
View
com o Model
Os exemplos vistos nas aulas seguirão o seguinte fluxo
Vamos considerar um projeto que guarda nomes de pessoas em um banco de dados, e uma interface de usuário que permite ao usuário fazer uma busca de uma parte do nome, e inserir um nome
Teremos uma interface View
interface View {
// configurações de gatilhos de ações
public void addAcaoInserirNome(ActionListener acaoInserirNome);
public void addAcaoBuscarNome(ActionListener acaoBuscarNome);
// mostrar dados do programa
public void mostrarNomes(List<String> nomes);
// requisições de entrada de usuários
public String getNomeBusca();
public String getNomeInserir();
}
Teremos uma interface Model
interface Model {
// ações para fazer em um banco de dados
public List<String> buscarNome(String nome);
public void inserirNome(String nome);
}
Teremos uma classe concreta Controller
programado para as interface View
e Model
class Controller {
Model model;
View view;
public Controller(Model aModel, View aView){
this.model = aModel;
this.view = aView;
}
public void init(){
view.addAcaoInserirNome(new AcaoInserirNome());
view.addAcaoBuscarNome(new AcaoBuscarNome());
}
class AcaoBuscarNome implements ActionListener{
@Override
public void actionPerformed(ActionEvent e){
// resuisita as entradas para o view
String nome = view.getNomeBusca();
// pede para o model fazer a busca
List<String> nomes = model.buscarNome(nome);
// pede para o view mostrar o resultado
view.mostrarNomes(nomes);
}
}
class AcaoInserirNome implements ActionListener{
@Override
public void actionPerformed(ActionEvent e){
// resuisita as entradas para o view
String nome = view.getNomeInserir();
// pede para o model fazer a inserção
model.inserirNome(nome);
}
}
}
Vamos considerar um projeto recebe dois números do usuário e faz algum tipo de conta.
interface View {
// configurações de gatilhos de ações
public void addAcaoCalcular(ActionListener acaoCalcular);
// mostrar dados do programa
public void mostrarResultado(int resultado);
// requisições de entrada de usuários
public int getNumeroA();
public int getNumeroB();
}
interface Model {
// ações para fazer em um banco de dados ou resolvedores matemáticos
public int calcularNumeros(int a, int b);
}
class Controller {
Model model;
View view;
public Controller(Model aModel, View aView){
this.model = aModel;
this.view = aView;
}
public void init(){
view.addAcaoCalcular(new AcaoCalcular());
}
class AcaoCalcular implements ActionListener{
@Override
public void actionPerformed(ActionEvent e){
// resuisita as entradas para o view
int numeroA = view.getNumeroA();
int numeroB = view.getNumeroB();
// pede para o model fazer as contas
int result = model.calcularNumeros(numeroA, numeroB);
// mostra o resultado para o view
view.mostrarResultado(result);
}
}
}
O modelo MVC permite que o programa seja flexível e que funcione com qualquer implementação das interfaces View
e Model
.
Por isso o Controller
trabalha apenas com as interfaces View
e Model
.
No exemplo de projeto a seguir vamos ver como usar no exemplo da tabela:
public interface Dao {
public List<Animal> buscaAnimais(String nome);
}
public interface View{
// mostra info para o user
void mostraAnimais(List<Animal> animais);
void mostraAnimal(Animal animal);
// pega info do user
String getNomeBusca();
Animal getAnimalSelecionado();
// configura o listener das acoes
void addListenerBusca(ActionListener al);
void addListenerSelecionaAnimal(ActionListener al);
}
public class Controller {
Dao model;
View view;
public Controller(View v, Dao m) {
this.view = v;
this.model = m;
view.addListenerBusca(new AcaoBuscarAnimal());
view.addListenerSelecionaAnimal(new AcaoSelecionarAnimal());
}
class AcaoBuscarAnimal implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
String keyword = view.getNomeBusca();
List<Animal> animais = model.buscaAnimais(keyword);
view.mostraAnimais(animais);
}
}
class AcaoSelecionarAnimal implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
Animal animal = view.getAnimalSelecionado();
view.mostraAnimal(animal);
}
}
}
public class DAODummy implements Dao{
List<Animal> animaisList = new ArrayList<>(Arrays.asList(
new Animal(0, "Brutus", 15),
new Animal(1, "Chirriro", 14),
new Animal(2, "Xitara", 13),
new Animal(3, "Mili", 12),
new Animal(4, "Jade", 11)
));
Map<Integer, Animal> animais = new HashMap<Integer, Animal>();
{
for(var a: animaisList)
animais.put(a.getId(), a);
}
@Override
public List<Animal> buscaAnimais(String nome) {
List<Animal> animaisResult = new ArrayList<>();
for(var a: animais.values())
if(a.getNome().toLowerCase().contains(nome.toLowerCase()))
animaisResult.add(a);
return animaisResult;
}
}
public class ViewJanela extends JFrame implements View {
String[] nomesColunas = {"Nome", "Idade"};
DefaultTableModel dtm = new DefaultTableModel(nomesColunas, 0);
JTable table = new JTable(dtm) {
@Override public boolean isCellEditable(int row, int column) {return false;}
};
JScrollPane sp = new JScrollPane(table);
JTextField buscaField = new JTextField(20);
JButton buscaButton = new JButton("Buscar");
ActionListener alSelecionaAnimal;
public ViewJanela() {
setLayout(new BoxLayout(this.getContentPane(), BoxLayout.PAGE_AXIS));
add(buscaField);
add(buscaButton);
sp.setPreferredSize(new Dimension(400, 400));
add(sp);
table.addMouseListener(new AcaoClicaTabela());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
@Override
public void mostraAnimais(List<Animal> animais) {
preencheTabela(animais);
}
void preencheTabela(List<Animal> animais) {
dtm.setRowCount(0);
for(Animal a: animais) {
Object[] row = new Object[2];
row[0] = a;
row[1] = a.getIdade();
dtm.addRow(row);
}
}
@Override
public void mostraAnimal(Animal a) {
String message =
"Animal selecionado: " + a.getId() + " - " + a.getNome() + "("+a.getIdade()+")";
JOptionPane.showMessageDialog(null, message);
}
@Override
public String getNomeBusca() {
return buscaField.getText();
}
@Override
public Animal getAnimalSelecionado() {
return selecionaLinha();
}
Animal selecionaLinha() {
int rowIndex = table.getSelectedRow();
Animal animal = (Animal)dtm.getValueAt(rowIndex, 0);
return animal;
}
@Override
public void addListenerBusca(ActionListener al) {
buscaButton.addActionListener(al);
buscaField.addActionListener(al);
}
@Override
public void addListenerSelecionaAnimal(ActionListener al) {
alSelecionaAnimal = al;
}
class AcaoClicaTabela extends MouseAdapter{
@Override
public void mouseClicked(MouseEvent e) {
if(e.getClickCount()==2) {
alSelecionaAnimal.actionPerformed(null);
}
}
}
}
public class TesteMVC {
public static void main(String[] args) {
new Controller(new ViewJanela(), new DAODummy());
}
}
.