Programa de Vendas - Problemas com Jtable e Sugestões referentes a GUI

Olá gente,

Estou fazendo um programa para Bar com controle de estoque, com interface gráfica, porém sem banco de dados… Estou utilizando uma classe “bd” para armazenar os objetos de produtos, clientes, etc etc… No formulário “Venda no Balcão”, criei uma combo box que lista todos os produtos cadastrados, ao lado um campo para digitar a quantidade, um botão que adiciona o item a lista, que ao clicado, adicionará o Produto e a quantidade em uma jtable abaixo.

O problema é que quando eu clico para adicionar um produto, e o mesmo já existe na tablema, ele irá adicionar o mesmo produto em outra linha abaixo, e eu queria que ao invés disso, ele procure as linhas da jtable, se encontrar o produto, modifique a columa “quantidade” somando a qntd anterior com a nova quantidade inserida… Parece ser fácil, e já dei uma pesquisada aqui no forum, mas realmente estou desnortiado.

Segue o código do ActionPerformed do botão “Adicionar à Lista”:

        try {
                       
            /*
             *  "listaProd"  é a lista TOTAL com todos os objetos de produtos cadastrados
             *  "comboProd"  é a combo que exibe os "nomes" de listaProd
             *  "campoQuant" é o JtextField onde será inserido a quantidade
             *  "quantidade" é o vetor que terá o mesmo índice do produto adicionado a lista
             *  "naLista" é o vetor que guarda os produtos JÁ adicionados à lista
             */
            
            //verifica se existe estoque do produto para ser adicionado...
            if (listaProd[comboProd.getSelectedIndex()].getQunt() < Integer.parseInt(campoQuant.getText())) {
                JOptionPane.showMessageDialog(null, "Não é possível adicionar o item: " + listaProd[comboProd.getSelectedIndex()].getNome() + " pois ele possui um estoque de: " + listaProd[comboProd.getSelectedIndex()].getQunt() + " que é inferior a quantidade querendo vender de: " + Integer.parseInt(campoQuant.getText()) + " itens.");
            } else if (Integer.parseInt(campoQuant.getText()) <= 0) {
                JOptionPane.showMessageDialog(null, "Valor inserido na Quantidade é inválido!");
            } else {

                //Adiciona o produto e a quantidade no vetor "naLista" e no vetor "quantidade"
                quantidade[count] = Integer.parseInt(campoQuant.getText());
                naLista[count] = listaProd[comboProd.getSelectedIndex()];

                //Preenche a jtable...
                preencher_jtable();

                //Diminui do estoque e fazer outras operações internas...
                naLista[count].dimEstoque(quantidade[count]);
                naLista[count].setQuantidadeVendida(quantidade[count]);
                naLista[count].setPrecoVendido(naLista[count].getPreco() * quantidade[count]);
                count++;
            }
        } catch (IndexOutOfBoundsException e) {
            JOptionPane.showMessageDialog(null, "Não existe nenhum produto cadastrado");
        } catch (NumberFormatException e) {
            JOptionPane.showMessageDialog(null, "Digite um número na 'Quantidade'");
        }

A seguir gostaria de disponibilizar o código do método “preencher_jtable()”:

[code]
public void preencher_jtable() {
jTable1.getColumnModel().getColumn(0).setPreferredWidth(150);
jTable1.getColumnModel().getColumn(1).setPreferredWidth(20);

    DefaultTableModel modelo = (DefaultTableModel) jTable1.getModel();
    modelo.setNumRows(0);
    int i;
    for (i = 0; naLista[i] != null; i++) {
        modelo.addRow(new Object[]{naLista[i].getNome(), quantidade[i]});
    }
}[/code]

É isso aí, não estou pedindo pela resposta, mas quem puder por favor me dár uma luz, agradeço!

Também gostaria que alguem olhasse a aplicação e me desse dicas, sugestões, críticas, qual quer coisa que possa melhorar a interface gráfica da aplicação, ou até mesmo sua funcionalidade… Vou anexar o .jar da aplicação aqui, e agradeço desde já as futuras respostas! (espero eu… hehe)

http://dl.dropbox.com/u/15625458/BarGatao.jar

OBS: Não é programa para trabalho escolar nem nada… Estou fazendo para aprender mesmo!

Abraços,
Noronha

  1. Não use DefaultTableModel;
  2. Esqueça o DefaultTableModel;
  3. Comece fazendo seu próprio TableModel e se livrando do DefaultTableModel;
  4. E evite usar o DefaultTableModel.

Antes de usar o JTable, é preciso saber como ele funciona em detalhes. E pra isso, você não deve construir seu próprio TableModel.

[quote=ViniGodoy]1. Não use DefaultTableModel;
2. Esqueça o DefaultTableModel;
3. Comece fazendo seu próprio TableModel e se livrando do DefaultTableModel;
4. E evite usar o DefaultTableModel.

Antes de usar o JTable, é preciso saber como ele funciona em detalhes. E pra isso, você não deve construir seu próprio TableModel.[/quote]

kkkk

tranquilo, muito obrigado pela dica!
Se você puder por favor dar uma olhada lá no jar do programa agradeço!

T++…

PS: vou estudar sobre TableModel!
go google!

abraços

Nem precisa ir tão longe, há diversos exemplos nos links da minha assinatura.

É, já dei uma olhada, estudei,
e realmente cara, é muito melhor, mais fácil, e facilita a vida em 10000kk …

Criei meu modelo para a tabela, mas estou tendo um problema… Segue primeiro o código:

package GUI;

import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import regra.*;

public class ProdTableModel  extends AbstractTableModel {

    	private static final long serialVersionUID = 1L;

	/* Lista de Sócios que representam as linhas. */
	private List&lt;Produto&gt; linhas;

	/* Array de Strings com o nome das colunas. */
	private String[] colunas = new String[] {
			"Produto", "Quantidade"};

        int quantidade[],pos;


	/* Cria um ProdutoTableModel vazio. */
	public ProdTableModel() {
		linhas = new ArrayList&lt;Produto&gt;();
	}

	/* Cria um ProdutoTableModel carregado com
	 * a lista de sócios especificada. */
	public ProdTableModel(List&lt;Produto&gt; listaDeProdutos) {
		linhas = new ArrayList&lt;Produto&gt;(listaDeProdutos);
	}


	/* Retorna a quantidade de colunas. */
	@Override
	public int getColumnCount() {
		// Está retornando o tamanho do array &quot;colunas&quot;.
		// Mas como o array é fixo, vai sempre retornar 4.
		return colunas.length;
	}

	/* Retorna a quantidade de linhas. */
	@Override
	public int getRowCount() {
		// Retorna o tamanho da lista de sócios.
		return linhas.size();
	}

	/* Retorna o nome da coluna no índice especificado.
	 * Este método é usado pela JTable para saber o texto do cabeçalho. */
	@Override
	public String getColumnName(int columnIndex) {
		// Retorna o conteúdo do Array que possui o nome das colunas
		// no índice especificado.
		return colunas[columnIndex];
	};

	/* Retorna a classe dos elementos da coluna especificada.
	 * Este método é usado pela JTable na hora de definir o editor da célula.
	 * Como todos os campos deste model são Strings (nome, e-mail e fones),
	 * o retorno será sempre a classe String. */
	@Override
	public Class&lt;?&gt; getColumnClass(int columnIndex) {
		return String.class;
	};

	/* Retorna o valor da célula especificada
	 * pelos índices da linha e da coluna. */
	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		// Pega o sócio da linha especificada.
		Produto Produto = linhas.get(rowIndex);

		// Retorna o campo referente a coluna especificada.
		// Aqui é feito um switch para verificar qual é a coluna
		// e retornar o campo adequado. As colunas são as mesmas
		// que foram especificadas no array &quot;colunas&quot;.
		switch (columnIndex) {
		case 0: // Primeira coluna é o nome.
			return Produto.getNome();
		case 1: // Segunda coluna é o e-mail.
			return quantidade[pos];
		default:
			// Se o índice da coluna não for válido, lança um
			// IndexOutOfBoundsException (Exceção de índice fora dos limites).
			// Não foi necessário verificar se o índice da linha é inválido,
			// pois o próprio ArrayList lança a exceção caso seja inválido.
			throw new IndexOutOfBoundsException(&quot;columnIndex out of bounds&quot;);
		}
	}

	/* Seta o valor da célula especificada
	 * pelos índices da linha e da coluna.
	 * Aqui ele está implementado para não fazer nada,
	 * até porque este table model não é editável. */
	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {};

	/* Retorna um valor booleano que define se a célula em questão
	 * pode ser editada ou não.
	 * Este método é utilizado pela JTable na hora de definir o editor da célula.
	 * Neste caso, estará sempre retornando false, não permitindo que nenhuma
	 * célula seja editada. */
	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return false;
	}




	/* Retorna o sócio da linha especificada. */
	public void getProduto(int indiceLinha) {
		linhas.get(indiceLinha);
	}

      
	/* Adiciona um registro. */
	public void addProduto(Produto Produto, int quantidade[], int pos) {
		// Adiciona o registro.
                this.pos=pos;
                this.quantidade = quantidade;
		linhas.add(Produto);

		// Pega a quantidade de registros e subtrai um para achar
		// o último índice. É preciso subtrair um, pois os índices
		// começam pelo zero.
		int ultimoIndice = getRowCount() - 1;

		// Reporta a mudança. O JTable recebe a notificação
		// e se redesenha permitindo que visualizemos a atualização.
		fireTableRowsInserted(ultimoIndice, ultimoIndice);
	}

	/* Remove a linha especificada. */
	public void removeProduto(int indiceLinha) {
		// Remove o sócio da linha especificada.
		linhas.remove(indiceLinha);

		// Reporta a mudança. O JTable recebe a notificação
		// e se redesenha permitindo que visualizemos a atualização.
		fireTableRowsDeleted(indiceLinha, indiceLinha);
	}

	/* Adiciona uma lista de sócios ao final dos registros. */
	public void addListaDeProdutos(List&lt;Produto&gt; Produtos) {
		// Pega o tamanho antigo da tabela.
		int tamanhoAntigo = getRowCount();

		// Adiciona os registros.
		linhas.addAll(Produtos);

		// Reporta a mudança. O JTable recebe a notificação
		// e se redesenha permitindo que visualizemos a atualização.
		fireTableRowsInserted(tamanhoAntigo, getRowCount() - 1);
	}

	/* Remove todos os registros. */
	public void limpar() {
		// Remove todos os elementos da lista de sócios.
		linhas.clear();

		// Reporta a mudança. O JTable recebe a notificação
		// e se redesenha permitindo que visualizemos a atualização.
		fireTableDataChanged();
	}

	/* Verifica se este table model está vazio. */
	public boolean isEmpty() {
		return linhas.isEmpty();
	}


}

O problema é o seguinte… lá na janelinha de gerenciar balcão, eu armazenava (antes do modelo), cada objeto num vetor chamado “naLista” e sua quantidade (a quantidade que o cara digitou na caixa) num vetor “quantidade” na mesma posição (para saber qual quantidade era de qual produto era só olhando pelo índice)… ai eu tentei incorporar isso no modelo, mas o que acontece é que os valores de cada linha anterior mudam conforme um outro produto for adicionado a lista… Não sei se ficou claro, mas o que eu quero dizer é o seguinte: adicionei o 1 produto a lista:

Produto Quantidade
Regri 5

Ai quando vou adicionar outro produto acontece o seguinte:

Produto Quantidade
Regri 2
Torta 2

O índice do anterior muda para o mesmo… eu tinha adicionado 5 na quantidade… e quando adicionei Torta com 2, mudou o índice anterior…

Acredito que se eu conseguir mudar isso, eu vou poder fazer um pequeno algoritmo para verificar se já existe um item com o msmo nome, e modificar só a coluna 1 somando o anterior com a quatnidade posterior…

agradeço se alguem puder ajudar!
t++++

[code]public Object getValueAt(int rowIndex, int columnIndex) {
Produto Produto = linhas.get(rowIndex);

    switch (columnIndex) {  
    case 0:
        return Produto.getNome();  
    case 1:
        return quantidade[rowIndex]; // A quantidade retornada deve ser referente ao índice da linha passada pelo parâmetro. A variável pos possui um valor fixo, portanto indicará sempre a mesma posição do array, seja qual for a linha.
    default: ... [/code]

Por que a quantidade não está na classe do produto?

Outra opção é fazer uma classe chamada ItemVendido com a seguinte estrutura

public class ItemVendido { private Produto produto; private int quantidade; }

E claro, adicionar aí os gets e sets. Fica muito mais fácil fazer listas disso, e representar isso no model. :wink:

Evite o máximo possível controlar listas paralelas que contém índices iguais. Além de muito pouco intuitivo, isso é difícil de manter.

Eric Yuzo funcionou do jeitinho que você disse!!

ViniGodoy eu também havia pensado em fazer dessa forma, mas resolvi não fazer por que na minha classe Produto já fiz um monte de gambiarra, tendo em vista que ainda não sei fazer relatórios, ai eu coloquei “QuantidadeVendida” e “TotalVendido”, uma que vai guardar quanto que foi vendido, e outra o total que vendeu em dinheiro… Se você olhar o jar em ‘relátorios’ tem isso lá… Meio crú hehe…

Bom, muito obrigado a vocês,
agora vou fazer o algoritmozinho para realizar o propósito final… e depois deixo a resposta aqui :wink:

PS: ninguem opiniou ainda quanto o jar e a aplicação em si, se tiverem tempo, reforço meus pedidos por opiniões… Vocês tem anos de java, tem noção de como deixar uma interface bonita, mais fácil e atraente para o cliente… no meu caso, conheço java a apenas este ano… E as coisas começaram a ficar interessante no meu curso só este ultimo semestre! hehehe

Abraços!

PRONTO!!

Seguem as modificações no modelo da tabela:

package GUI;

import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import regra.*;

public class ProdTableModel extends AbstractTableModel {

    private static final long serialVersionUID = 1L;

    private List<Produto> linhas;

    private String[] colunas = new String[]{
        "Produto", "Quantidade"};
    int quantidade2[] = new int[10000];
    int pos, countPos = 0;


    public ProdTableModel() {
        linhas = new ArrayList<Produto>();
    }

    public ProdTableModel(List<Produto> listaDeProdutos) {
        linhas = new ArrayList<Produto>(listaDeProdutos);
    }


    @Override
    public int getColumnCount() {
        return colunas.length;
    }

    @Override
    public int getRowCount() {
        return linhas.size();
    }

    @Override
    public String getColumnName(int columnIndex) {
        return colunas[columnIndex];
    }

    ;

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return String.class;
    }

    ;

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Produto Produto = linhas.get(rowIndex);

        switch (columnIndex) {
            case 0: 
                return Produto.getNome();
            case 1: 
 
                //MODIFICAÇÃO: tive que colocar um vetor de inteiros interno a classe...

                return quantidade2[rowIndex];
            default:
                throw new IndexOutOfBoundsException("columnIndex out of bounds");
        }
    }

   
   //MODIFICAÇÃO: tive que escrever este método... Ele estava vazio antes...

    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {

        Produto p = linhas.get(rowIndex);

        quantidade2[rowIndex] = (Integer.parseInt(aValue.toString()));


        fireTableDataChanged();

    }

    ;


    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return false;
    }

    public void getProduto(int indiceLinha) {
        linhas.get(indiceLinha);
    }

    
    public void addProduto(Produto Produto, int quantidade) {
        // Adiciona o registro.
        this.pos = quantidade;
        linhas.add(Produto);

        //MODIFICAÇÃO: contador de posições 

        quantidade2[countPos] = quantidade;
        countPos++;

        int ultimoIndice = getRowCount() - 1;

        fireTableRowsInserted(ultimoIndice, ultimoIndice);
    }

    public void removeProduto(int indiceLinha) {
        linhas.remove(indiceLinha);

        fireTableRowsDeleted(indiceLinha, indiceLinha);
    }

    public void addListaDeProdutos(List<Produto> Produtos) {
        int tamanhoAntigo = getRowCount();

        linhas.addAll(Produtos);

        fireTableRowsInserted(tamanhoAntigo, getRowCount() - 1);
    }

    public void limpar() {

        //MODIFICAÇÃO: ao limpar a tabela também tenho que limpar as variaveis auxiliares...

        quantidade2 = new int[10000];
        pos = 0;
        countPos = 0;

        linhas.clear();

        fireTableDataChanged();
    }


    public boolean isEmpty() {
        return linhas.isEmpty();
    }
}

O “algoritmo” para fazer as coisas funcionarem, lá na janela do balcão ao clicar em “Adicionar a Lista”:

[code]
//pega a quantidade e o produto que o cara selecionou
quantidade[count] = Integer.parseInt(campoQuant.getText());
naLista[count] = listaProd[comboProd.getSelectedIndex()];

             int i;
             int posis=0;    //relata a posição na qual encontrou o elemento
             boolean achou=false;   //Controlador de fluxo, só para parar quando encontrar o que se procura

            for (i = 0; i < modelo.getRowCount() && achou == false; i++) {   //até o fim das linhas existentes, ou até encontrar

                Object prod2 = modelo.getValueAt(i, 0);  //pega o que tiver na linha "i" na columa 0 (a columa de nome)

                if (prod2.equals(naLista[count].getNome())) {  //se o nome da linha for igual o nome do produto
                    posis = i;                                                  //achei nessa posição "i"
                    achou = true;                                            //sair do for
                }
            }

             if(achou){ 
                 Object prod2 = modelo.getValueAt(posis,1);           //agora vou pegar o valor da quantidade que estava anteriormente
                 int ant = Integer.parseInt(prod2.toString());           // "transformar o objeto" em inteiro
                 ant += quantidade[count];                                    // somar o valor anterior com o que o cara escreveu...
                 prod2 = ant;                                                        // "transformar o inteiro" em objeto
                 modelo.setValueAt(prod2, posis, 1);                      // "setar o valor"

             }else{
                 modelo.addProduto(naLista[count],quantidade[count]);   //se nao tiver encontrado o produto, apenas adicione a tabela...
             }[/code]

Obrigado a todos!!

Obs: o tópico está resolvido, mas ninguem opinou no aplicativo! ;(
O que eu faço? coloco [Resolvido] no título e crio um outro tópico para se discutir apenas esse assunto?
Sou novato no forum e não quero fazer nada de errado :stuck_out_tongue: hehe

abraços!

Siga a dica do Vini Godoy e faça uma classe para os produtos vendidos.

[quote=ViniGodoy]public class ItemVendido { private Produto produto; private int quantidade; }[/quote]
No seu TableModel matenha um lista com os itens vendidos:

Assim já elimina o array de 10000 posições e variáveis extras. No método getValueAt só precisará trabalhar em cima do item, por exemplo:

[code] @Override
public Object getValueAt(int rowIndex, int columnIndex) {
ItemVendido item = linhas.get(rowIndex);

    switch (columnIndex) {
        case 0: 
            return item.getNomeDoProduto();
        case 1: 
             return item.getQuantidade();
        default:
            throw new IndexOutOfBoundsException("columnIndex out of bounds");
    }
}[/code]

Quanto ao método setValueAt, ele deve modificar apenas a célula que for especificada pelos parâmetros rowIndex e columnIndex:

[code] @Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
ItemVendido item = linhas.get(rowIndex); // Carrega o item da linha que deve ser modificado

    switch (columnIndex) {
        case 0: // modifica o item da coluna 0
            item.setNomeDoProduto(aValue);
        case 1: // modifica o item da coluna 1
            item.setQuantidade(aValue);
        default:
            throw new IndexOutOfBoundsException("columnIndex out of bounds");

    fireTableCellUpdated(rowIndex, columnIndex); // Indica a atualização da célula
}[/code]

Eric Yuzo, valeu pelas dicas!

Já consegui resolver o problema, ja está 100% !
Agora só falta as dicas referente a GUI…

Abração!!
brigado de novo!