アプリケーションを作成しましたが、期待どおりに動作します。データベース クエリが実行されている限り、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 がフリーズする可能性があります。
この問題を解決または最小化する最もエレガントな方法は何ですか?