5

アプリケーションを作成しましたが、期待どおりに動作します。データベース クエリが実行されている限り、GUI は応答し続けます。SwingUtilities.invokeLater() を使用してカスタム パネルを作成すると、GUI が非常に短時間フリーズします。

SwingUtilities.invokeAndWait() を使用すると、ハイエンドのゲーミング PC で非常にスムーズに動作します。(おそらく、コーディングするのに最適なマシンではありません...) しかし、比較的遅いマシン (デュアルコア、2GB RAM) では、GUI が「遅れる」

動作を再現する非常に最小限のバージョンのプログラムを作成しました。テスト時に TEST_NUMBER_OF_PANELS の値を増やします。

派手なルックアンドフィールや追加のコンポーネントなしで、現在のPCで動作を再現するために、非常に大きな値に設定しました。しかし、私はこのように投稿したくありません。だから私はそれを100に減らした

package test;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

//import org.pushingpixels.substance.api.SubstanceLookAndFeel;
//import org.pushingpixels.substance.api.skin.BusinessBlackSteelSkin;
//import org.pushingpixels.substance.api.skin.SubstanceBusinessBlackSteelLookAndFeel;

public class Test extends JFrame {
    private static final int TEST_NUMBER_OF_PANELS = 100;
    private static JTabbedPane tabbedPane = new JTabbedPane();
    Test() {
        this.setLayout(new BorderLayout());
        this.setSize(1050, 700);
        this.setMinimumSize(new Dimension(400,200));
        this.add(tabbedPane, BorderLayout.CENTER);

        JButton testbutton = new JButton("new tab");
        testbutton.addMouseListener(new MouseListener() {
            @Override
            public void mousePressed(MouseEvent e) {
                tabbedPane.addTab("tab x", new TestTabContent());
            }
            @Override
            public void mouseReleased(MouseEvent e) {
            }
            @Override
            public void mouseExited(MouseEvent e) {
            }
            @Override
            public void mouseEntered(MouseEvent e) {
            }
            @Override
            public void mouseClicked(MouseEvent e) {
            }
        });
        this.add(testbutton, BorderLayout.NORTH);
        //tabbedPane.addTab("tab1", new TestTabContent());
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new Test().setVisible(true);
                /*
                try {
                    UIManager.setLookAndFeel(new SubstanceBusinessBlackSteelLookAndFeel());
                } catch (Exception e) {
                    System.out.println("Substance Business failed to initialize");
                }
                SubstanceLookAndFeel.setSkin(new BusinessBlackSteelSkin());
                new Test()
                .setVisible(true);
                */
            }
        });

    }

    private class TestTabContent extends JPanel {
        TestTabContent() {
            final JPanel boxContainer = new JPanel();
            boxContainer.setLayout(new BoxLayout(boxContainer, BoxLayout.Y_AXIS));
            JPanel boxContainerOuter = new JPanel();
            boxContainerOuter.setLayout(new BorderLayout());
            boxContainerOuter.add(boxContainer, BorderLayout.NORTH);
            JScrollPane mainScrollPane = new JScrollPane(boxContainerOuter);

            // create toolbar
            JPanel toolBar = new JPanel();
            toolBar.setLayout(new BorderLayout());

            //east
            JPanel InfoPanel = new JPanel();
            InfoPanel.setLayout(new BoxLayout(InfoPanel, BoxLayout.X_AXIS));
            InfoPanel.add(new JLabel("test: some info ..."));
            toolBar.add(InfoPanel, BorderLayout.WEST);
            //west
            JPanel viewOptionPanel = new JPanel();
            viewOptionPanel.setLayout(new BoxLayout(viewOptionPanel, BoxLayout.X_AXIS));
            viewOptionPanel.add(new JLabel("some controls.."));
            toolBar.add(viewOptionPanel, BorderLayout.EAST);

            // set main panel´s layout
            GroupLayout layout = new GroupLayout(this);
            this.setLayout(layout);
            layout.setHorizontalGroup(
                    layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addComponent(toolBar, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(mainScrollPane)
                    );
            layout.setVerticalGroup(
                    layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                            .addComponent(toolBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                            .addGap(0, 0, 0)
                            .addComponent(mainScrollPane, GroupLayout.DEFAULT_SIZE, 413, Short.MAX_VALUE))
                    );

            // create controls 
            SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
                @Override
                protected String doInBackground() throws Exception {
                    // RUN DATABASE QUERY
                    // ------------------
                    //Thread.sleep(1000);
                    // ------------------
                    // CREATE TESTPANELS

                    ArrayList<ArrayList<ArrayList<String>>> dataFromQuery = createDummyData();

                    for (final ArrayList<ArrayList<String>> tableData : dataFromQuery) {
                        //SwingUtilities.invokeAndWait(new Runnable() {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                // create panels on edt
                                TestPanel newTestPanel = new TestPanel(tableData);
                                JPanel seperator = new JPanel(new BorderLayout());
                                seperator.setBackground(Color.black);
                                seperator.add(newTestPanel);

                                boxContainer.add(seperator);
                            }
                        });
                    }
                    return "";
                }

            };
            worker.execute();
        }
        private ArrayList<ArrayList<ArrayList<String>>> createDummyData () {
            ArrayList<String> columns = new ArrayList<String>();
            ArrayList<ArrayList<String>> rows = new ArrayList<ArrayList<String>>();
            ArrayList<ArrayList<ArrayList<String>>> tables = new ArrayList<ArrayList<ArrayList<String>>>();
            for (int i = 0; i < 15; i ++) {
                columns.add("test213124");
            }
            for (int i=0; i < 8; i++) {
                rows.add(columns);
            }
            for (int i=0; i < TEST_NUMBER_OF_PANELS; i++) {
                tables.add(rows);
            }
            return tables;
        }
    }
    public class TestPanel extends JPanel { 
        private static final long serialVersionUID = -3853151036184428736L;
        public static final int radioButtonWidth = 20;


        private JTable table;
        private DefaultTableModel tableModel;
        private JPanel collapsiblePane; 
        private JButton collapsingButton;

        TestPanel(ArrayList<ArrayList<String>> tableData) {
            System.out.println("testpanel constructor");
            createTestPanel(tableData);
        } 

        private void createTestPanel(ArrayList<ArrayList<String>> tableData) {          
            // container with boxLayout for collapsiblePane
            JPanel boxContainer = new JPanel();
            boxContainer.setLayout(new BoxLayout(boxContainer, BoxLayout.Y_AXIS));
            boxContainer.setBorder(BorderFactory.createMatteBorder(0, 1, 1, 1, Color.BLACK));

            // set table stuff
            tableModel = new DefaultTableModel();

            tableModel.setColumnIdentifiers(
                    new Object[] {
                            "test", "test", "test", "test", "test", "test", "test", "test", "test", "test", "test", "test", "test", "test", "test"});


            for (ArrayList<String> rowData : tableData) {
                Vector<Serializable> data = new Vector<Serializable>();

                for (String columnData : rowData) {
                    data.add(columnData);
                }
                tableModel.addRow(data);

            }
            table = new JTable(tableModel) {
                public void tableChanged(TableModelEvent e) {
                    super.tableChanged(e);
                    repaint();
                }

                public Component prepareRenderer(TableCellRenderer renderer, int row, int column){
                    Component returnComp = super.prepareRenderer(renderer, row, column);
                    Color alternateColor = new Color(225,225,238);
                    Color usualColor = Color.WHITE;
                    if (!returnComp.getBackground().equals(getSelectionBackground())){
                        Color bg = (row % 2 == 0 ? alternateColor : usualColor);
                        returnComp .setBackground(bg);
                        bg = null;
                    }
                    return returnComp;
                }
            };

            boxContainer.add(table.getTableHeader(), BorderLayout.NORTH);
            boxContainer.add(table, BorderLayout.CENTER);

            // other controls / toolbar
            JPanel toolbar = new JPanel();
            toolbar.setLayout(new BorderLayout());

            // buttons to the right
            JPanel toolbarButtonGroup = new JPanel();
            toolbarButtonGroup.setLayout(new BoxLayout(toolbarButtonGroup, BoxLayout.X_AXIS));

            // test button
            JButton button = new JButton("test");
            JPanel sepPanel = new JPanel();
            sepPanel.add(button);
            toolbarButtonGroup.add(sepPanel);
            // test button
            button = new JButton("test");
            sepPanel = new JPanel();
            sepPanel.add(button);
            toolbarButtonGroup.add(sepPanel);
            // test button
            button = new JButton("test");
            sepPanel = new JPanel();
            sepPanel.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
            sepPanel.add(button);

            toolbarButtonGroup.add(sepPanel);

            toolbar.add(toolbarButtonGroup, BorderLayout.EAST);
            boxContainer.add(toolbar);

            JPanel subPanel = new JPanel();
            subPanel.setLayout(new BoxLayout(subPanel, BoxLayout.Y_AXIS));

            JPanel buttonPanel = new JPanel(); 
            buttonPanel.setLayout(new BorderLayout());
            buttonPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 24));

            collapsingButton = new JButton(tableModel.getValueAt(0, 8).toString());
            collapsingButton.setName("toggleButton"); 
            collapsingButton.setHorizontalAlignment(SwingConstants.LEFT);
            collapsingButton.setBorderPainted(false);
            collapsingButton.setFocusPainted(false);

            buttonPanel.add(collapsingButton, BorderLayout.CENTER); 
            buttonPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
            subPanel.add(buttonPanel);

            collapsiblePane = new JPanel();

            collapsiblePane.setName("collapsiblePane"); 
            collapsiblePane.setLayout(new CardLayout()); 

            collapsiblePane.add(boxContainer, ""); 
            subPanel.add(collapsiblePane); 

            add(subPanel);      
        } 
    } 
}

EDT ではなく SwingWorker でできるだけ多くのコードを実行する必要があると思います。別のスレッドで TableModel にアクセスする必要があることを認識しています。アプリケーションに JTable が 1 つしかない場合は、単純です。EDT でテーブルを作成し、スレッドでデータを更新します。

その場合、どのアプローチが望ましいかわかりません。現在、タブ関連はすべて TestPanel クラスで管理されています。これは完全に EDT で実行されます。

もちろん、TableModel を処理する TestPanel でスレッドを実行することもできますが、パネルごとに新しいスレッドを作成することは、私には賢明な考えのようには思えません。

もう 1 つの「アイデア」は、データベース クエリが実行されているのと同じスレッドの TestTab でモデルを直接作成することでした。しかし、それは TestPanel に属しているため、このアプローチは悪い設計のように思えます。

私が持っていた最も汚いアイデアは、invokeAndWait() を使用し、Tread.sleep() をしばらく放置して、ETD が数ミリ秒ごとに新しい呼び出しによって平手打ちされないようにすることでした。しかし、実際にはそのようにコーディングしたくありません。

どの設計アプローチが最も望ましいでしょうか?


実行時に JTabbedPane にいくつかのカスタム パネルを作成したいと考えています。カスタム パネルの数は、データベース クエリの結果セットによって異なります。

各カスタム パネルには、結果セットからのデータのチャンクといくつかの JButton を含む JTable が保持されます。

私の計画は、SwingWorker を使用して別のスレッドでデータベース クエリを実行することです。Swing コンポーネントは EDT で作成してアクセスする必要があるため、invokeLater() を使用してイベント ディスパッチ スレッドでタスクをスケジュールし、カスタム パネルを作成します。

このアプローチでは、結果セットに多くの行が含まれている場合、カスタム パネルの作成中に GUI がフリーズする可能性があります。

この問題を解決または最小化する最もエレガントな方法は何ですか?


4

3 に答える 3

2

何よりもまず、表示される結果の数を制限することを検討する必要があります。

それとは別に、Swing コンポーネントの作成は非常に効率的です。実際、データベースから結果が返ってくるのと同じくらい速く、オフスクリーン コンポーネント インスタンスを作成できると期待しています。これが本当にボトルネックだと確信していますか?

最後に、フリーズが心配な場合は、いつでもカスタム パネルごとに invokeLater を使用して個別のイベントを発生させることができます。次に、各パネル作成の間にイベント処理が発生する機会があり、これにより「フリーズ」効果が解消されます。

于 2013-01-07T11:43:24.663 に答える
1

私の計画は、SwingWorkerを使用して別のスレッドでデータベースクエリを実行することです。次に、EDTでスイングコンポーネントを作成してアクセスする必要があるため、invokeLater()を使用してイベントディスパッチスレッドでタスクをスケジュールし、カスタムパネルを作成します。

このアプローチでは、結果セットに多数の行が含まれている場合、カスタムパネルの作成中にGUIがフリーズする可能性があります。

それはフリーズするべきではありません、私はあなたのアプローチが正しいと思います。

それでもフリーズすると思われる場合は、ぜひお知らせください。

于 2013-01-07T11:39:48.127 に答える
1

を邪魔せずにデータベース クエリを実行したいEDTので、別のクエリをいつでも歓迎します。プログラムを 内で実行invokeLaterし、次に DB クエリ用の新しいスレッドを実行します。偶然にも、私もあなたと同じような問題に取り組んでいました。私がしたことを見てください。

  ////start a Thread  for DB query on a button click or whatever you want
   private void jButton2ActionPerformed(java.awt.event.ActionEvent evt)   
{                                         
 new Thread(new thread1()).start();

} 

めねじ:

public class thread1 implements Runnable
{

    public void run() {
     insert ins=new insert();/*make object of class if needed(class to handle Db operations)*/

    try {
        ins.insert(path); //call the function to insert into database or any other query
    } 

   catch (ClassNotFoundException ex) {
        Logger.getLogger(forminsert.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SQLException ex) {
        Logger.getLogger(forminsert.class.getName()).log(Level.SEVERE, null, ex);
    }

    JOptionPane.showMessageDialog(frame, "Successfully inserted into database");
    ///show it is inserted

    getRootPane().updateUI();//may work without this(optional)

    }
}

これで、フリーズの問題は一度もありませんでした。問題なくデータベースに挿入されました。

于 2013-01-07T12:52:12.177 に答える