Загрузка JTable без потери синхронизации - PullRequest
0 голосов
/ 21 мая 2019

В этом примере показано диалоговое окно JDViewCustomer, содержащее две кнопки < и > для асинхронной перезагрузки JTable (я добавил несколько вызовов Thread.sleep() для имитации загрузки сети).

Быстрое нажатие кнопок приводит к потере синхронизации приложения с экраном (см. Класс LoadCustomerOrdersWorker.java, который также выполняет повторную проверку / перерисовку после загрузки данных).

Как я могу предотвратить это?

Test.java

package testrepaint;

public class Test {

    private static JFrame frame;

    private static void createAndShowGUI() {
        frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        run();

        frame.pack();
        frame.setVisible(true);
    }

    private static void run() {

        List<CustomerOrder> customerOrders = new ArrayList<>();
        customerOrders.add(new CustomerOrder(15, 60.0f));
        customerOrders.add(new CustomerOrder(16, 280.0f));
        customerOrders.add(new CustomerOrder(17, 150.53f));
        customerOrders.add(new CustomerOrder(18, 280.0f));

        new JDViewCustomer(frame, true, customerOrders).setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

JDViewCustomer.java

package testrepaint;

public class JDViewCustomer extends javax.swing.JDialog {

    private static final long serialVersionUID = 1L;

    private static final ExecutorService SINGLE_THREAD_POOL = Executors.newSingleThreadExecutor();

    private JPanel jPanelCustomerOrders;
    private JPanel panelContainingTheOtherPanels;
    private JPanel panelAbaixoDeCustomerOrders;
    private JPanel panelContendoOsPedidos;

    private final List<CustomerOrder> customerOrders;
    private JPanel panelTituloPedidos;
    private JLabel lblPedidos;

    private JTable tableOrders;
    private JPanel panelPagination;
    private JButton btnPrevious;
    private JButton btnNext;
    private JLabel lblPageSelection;

    private int currentPage = 1;
    private int maxPage = 3;

    public JDViewCustomer(java.awt.Frame parent, boolean modal, List<CustomerOrder> customerOrders) {
        super(parent, modal);
        this.customerOrders = new ArrayList<>(Objects.requireNonNull(customerOrders));

        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));

        panelContainingTheOtherPanels = new JPanel();
        panelContainingTheOtherPanels.setLayout(new BoxLayout(panelContainingTheOtherPanels, BoxLayout.Y_AXIS));
        getContentPane().add(panelContainingTheOtherPanels);

        layoutCustomerOrdersSection();
        updateCustomerOrdersTable();

        updateCurrentPageTextField();
        toggleButtonsPreviousAndNext();

        initComponents();
    }

    private void layoutCustomerOrdersSection() {

        jPanelCustomerOrders = new JPanel();
        jPanelCustomerOrders.setBackground(new Color(255, 255, 255));
        panelContainingTheOtherPanels.add(jPanelCustomerOrders);
        jPanelCustomerOrders.setLayout(new BoxLayout(jPanelCustomerOrders, BoxLayout.Y_AXIS));


        panelAbaixoDeCustomerOrders = new JPanel();
        panelAbaixoDeCustomerOrders.setBorder(new LineBorder(new Color(0, 0, 0), 1, true));
        jPanelCustomerOrders.add(panelAbaixoDeCustomerOrders);
        panelAbaixoDeCustomerOrders.setLayout(new BoxLayout(panelAbaixoDeCustomerOrders, BoxLayout.Y_AXIS));

                panelTituloPedidos = new JPanel();
                panelAbaixoDeCustomerOrders.add(panelTituloPedidos);
                panelTituloPedidos.setBorder(new LineBorder(new Color(0, 0, 0), 1, true));
                panelTituloPedidos.setBackground(Color.LIGHT_GRAY);

                lblPedidos = new JLabel("Pedidos");
                panelTituloPedidos.add(lblPedidos);

        panelContendoOsPedidos = new JPanel();
        panelContendoOsPedidos.setBorder(new CompoundBorder(new LineBorder(new Color(0, 0, 0), 1, true), new EmptyBorder(10, 10, 10, 10)));
        panelContendoOsPedidos.setBackground(Color.WHITE);
        panelAbaixoDeCustomerOrders.add(panelContendoOsPedidos);
        panelContendoOsPedidos.setLayout(new BoxLayout(panelContendoOsPedidos, BoxLayout.Y_AXIS));

        panelPagination = new JPanel();
        panelAbaixoDeCustomerOrders.add(panelPagination);

        addPaginationComponentsToPaginationPanel();
    }

    private void addPaginationComponentsToPaginationPanel() {
        btnPrevious = new JButton("<");
        btnPrevious.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                performButtonPrevious(e);
            }
        });
        btnPrevious.setFont(new Font("Tahoma", Font.BOLD, 11));
        panelPagination.add(btnPrevious);

        lblPageSelection = new JLabel("0");
        panelPagination.add(lblPageSelection);

        btnNext = new JButton(">");
        btnNext.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                performButtonNext(e);
            }
        });
        btnNext.setFont(new Font("Tahoma", Font.BOLD, 11));
        panelPagination.add(btnNext);
    }

    private void performButtonPrevious(ActionEvent e) {

        if (currentPage > 1) {
            currentPage -= 1;
            updateCurrentPageTextField();
        }

        toggleButtonsPreviousAndNext();

        loadCustomerOrders();
    }

    private void performButtonNext(ActionEvent e) {
        if (currentPage < maxPage) {
            currentPage += 1;
            updateCurrentPageTextField();
        }

       toggleButtonsPreviousAndNext();

       loadCustomerOrders();
    }

    private void loadCustomerOrders() {
        LoadCustomerOrdersWorker worker = new LoadCustomerOrdersWorker(customerOrders, this);
        SINGLE_THREAD_POOL.execute(worker);
    }

    private void toggleButtonsPreviousAndNext() {
        togglePreviousButton();
        toggleNextButton();
    }

    private void togglePreviousButton() {
        btnPrevious.setEnabled(currentPage > 1);
    }

    private void toggleNextButton() {
        btnNext.setEnabled(currentPage < maxPage);
    }

    private void updateCurrentPageTextField() {
        lblPageSelection.setText(String.valueOf(currentPage));
    }

    private void updateCustomerOrdersTable() {

        TableModel tableModel = instantiateAbstractTableModel();

        tableOrders = new JTable(tableModel) {
            private static final long serialVersionUID = 1L;

            public Dimension getPreferredScrollableViewportSize() {
                return getPreferredSize();
            }            
        };

        centralizeTextInTableCells();

        //tableOrders.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        TableColumnModel colModel = tableOrders.getColumnModel();
        colModel.getColumn(2).setPreferredWidth(80);
        colModel.getColumn(3).setPreferredWidth(250);

        // Disables manual reordering of columns.
        tableOrders.getTableHeader().setReorderingAllowed(false);

        JScrollPane scrollPane = new JScrollPane(tableOrders);
        panelContendoOsPedidos.add(scrollPane);
    }

    private AbstractTableModel instantiateAbstractTableModel() {
        return new AbstractTableModel() {
            private static final long serialVersionUID = 1L;

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

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

            @Override
            public int getColumnCount() {
                return 5;
            }

            @Override
            public String getColumnName(int column) {
                switch(column) {
                case 0: return "Status";
                case 1: return "Número";
                case 4: return "Total";
                default: return "";
                }
            }

            @Override
            public Object getValueAt(int rowIndex, int columnIndex) {
                CustomerOrder customerOrder = customerOrders.get(rowIndex);
                switch(columnIndex) {
                case 0: return customerOrder;
                case 1: return "#" + String.valueOf(customerOrder.getNumber());
                case 4: return String.format("%.2f", customerOrder.getTotal());
                default: return "";
                }
            }

            @Override
            public Class<?> getColumnClass(int columnIndex) {
                switch(columnIndex) {
                case 0: return CustomerOrder.class;
                case 1: return String.class;
                case 4: return String.class;
                default: return Object.class;
                }
            }
        };
    }

    private void centralizeTextInTableCells() {
        DefaultTableCellRenderer stringCellRenderer = (DefaultTableCellRenderer)tableOrders.getDefaultRenderer(String.class);
        stringCellRenderer.setHorizontalAlignment(JLabel.CENTER);
        tableOrders.setDefaultRenderer(String.class, stringCellRenderer);
    }

    private void initComponents() {

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Ver Cliente");

        pack();

        // Para centralizar o JDialog em relação a seu parent.
        setLocationRelativeTo(getParent());

    }
}

LoadCustomerOrdersWorker.java

package testrepaint;

public class LoadCustomerOrdersWorker extends SwingWorker<Void, Void> {

    private final List<CustomerOrder> customerOrders;
    private final JDViewCustomer jDialogViewCustomer;

    public LoadCustomerOrdersWorker(List<CustomerOrder> customerOrders, JDViewCustomer jDialogViewCustomer) {
        this.customerOrders = customerOrders;
        this.jDialogViewCustomer = jDialogViewCustomer;
    }

    @Override
    protected Void doInBackground() throws Exception {

        this.customerOrders.clear();
        Thread.sleep(300);
        this.customerOrders.add(new CustomerOrder(15, 60.0f));
        Thread.sleep(300);
        this.customerOrders.add(new CustomerOrder(16, 280.0f));
        Thread.sleep(300);
        this.customerOrders.add(new CustomerOrder(17, 150.53f));
        Thread.sleep(300);
        this.customerOrders.add(new CustomerOrder(18, 280.0f));
        Thread.sleep(300);
        this.customerOrders.add(new CustomerOrder(19, 280.0f));
        Thread.sleep(300);

        return null;
    }

    @Override
    protected void done() {
        jDialogViewCustomer.revalidate();
        jDialogViewCustomer.repaint();
    }
}

CustomerOrder.java

package testrepaint;

public class CustomerOrder {

    private final int number;
    private final float total;


    public CustomerOrder(int number, float total) {
        this.number = number;
        this.total = total;
    }

    public int getNumber() {
        return number;
    }

    public float getTotal() {
        return total;
    }

    @Override
    public String toString() {
        return "toString()";
    }
}

1 Ответ

3 голосов
/ 22 мая 2019

Ваша базовая концепция обновления таблицы неверна:

  1. TableModel должен содержать структуру данных.То есть ArrayList должен быть частью TableModel.Затем TableModel должен иметь методы для динамического изменения данных.

  2. обновления должны быть выполнены для TableModel.Затем TableModel уведомит таблицу о перекрашивании.

  3. См. Модель таблицы строк , где приведен пошаговый пример создания пользовательской модели TableModel, включающей приведенные выше предложения.

  4. Обновления компонентов Swing (и их данных) должны выполняться на Event Dispatch Thread (EDT).Код, который выполняется в методе doInBackground (), НЕ выполняется в режиме EDIT.Поэтому вместо непосредственного обновления ArrayList вам нужно publish получить результаты, а затем обновить TableModel с помощью CustomOrder.

Прочтите раздел из учебника Swing по Задачи, которые имеют промежуточные результаты , чтобы узнать, как работает метод publish().Код, выполненный в методе publish(), выполняется в EDT.

Нет необходимости вызывать revalidate () и repaint () в методе done ().Как упоминалось ранее, при обновлении TableModel это приведет к автоматическому обновлению таблицы.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...