2013-02-20 2 views
3

내가 참조하는 코드는 독점적이며 멀티 캐스트 서버가 필요하므로 SSCCE 스 니펫을 게시 할 수 없습니다. 나는 이것이 내가 현재 데이터를 멀티 캐스트 수신 대기 GUI 응용 프로그램에서의 JTable를 사용하고 9JTable을 사용하여 성능이 저하되었습니다. 스트리밍 데이터 표시

자바 7 U로 컴파일하고있어 가능한 반응을 유도하는 어떤 도움이 통찰력 ...

을 배제 할 수 이해 도착한대로 표시합니다. 테이블을 스크롤하거나 열의 크기를 조정할 때 앱이 너무 느리게 반응합니다.

나는 내 코드를 적절하게 구조화했다고 생각했습니다.

wrapper 클래스를 사용했고 main() 함수에서 명령 행 인수를 처리하고 리스너를 만들고 JFrame을 만들고 JTable을 반환하는 클래스를 호출하는 자체 인스턴스를 만듭니다. 이것은 이벤트 발송 스레드 외부에서 모두 수행됩니다.

다음 줄에서는 invokeLater() 메서드를 사용하여 모든 GUI 렌더링을 처리하는 스레드를 만들었습니다. JScrollPane를 작성해, JTable를 스크롤 구획에 추가해, 스크롤 바를 설정해, 뷰포트를 설정해, 스크롤 모드를 설정해, JScrollPane를 JFrame에 추가합니다. 이것은 이벤트 발송 스레드 내에서 모두 처리됩니다.

일반적으로 행은 매우 빠르게 채워지며 일부 화면에는 30 개 이상의 행이 포함되어있는 경우도 있지만 응답 성이 양호한 것으로 보입니다. 그러나 열을 스크롤하거나 크기를 조정할 때 응답이 매우 느립니다.

내가 본 모든 예는 SwingX SwingLabs 데모를 포함하여 앞에서로드 된 초기 데이터 집합을 나타냅니다. JTable을 스트리밍 데이터와 함께 사용하는 예가 필요합니다.

누구든지 그런 예/데모를 가르쳐 줄 수 있습니까? 이 내 주() 조각입니다

...

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

PS. 나는 각자와 반응 한 모든 사람들에게 감사 드린다. 나는 3 월 11 일까지이 프로젝트를 끝내었지만 그 날의 모든 응답을 검토 할 것입니다.

+1

* "내가 참조한 코드는 독점적이며 멀티 캐스트 서버가 필요하므로 SSCCE 코드를 게시 할 수 없습니다."* SSCCE에는 멀티 캐스트 서버 또는 독점 요소가 필요하지 않습니다. SSCCE에 임의의 간격으로 데이터를 생성하여 문제를 재현 할 수있는 클래스를 만드는 것이 가능할 것으로 생각됩니다. –

+3

[예] (http://stackoverflow.com/a/4637725)에 대한 ['SwingWorker'] (http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html)가 필요할 것입니다./230513). – trashgod

+1

@trashgod는 단일 루프에 동의했지만 무한 루프에 대해서는 동의하지 않습니다 .--) SwingWorker는 한 번만 실행되도록 지정되었습니다. 이전 PC에서 발견 된 내용을 확인하십시오. – mKorbel

답변

3

JTable이 스트리밍 데이터와 전혀 잘 맞지 않는다고 생각합니다. TableModel에 실제 목록이 포함되어 있지 않고 대신 데이터 스트림에 대한 연결이있는 경우 이벤트 발송 스레드에서 처리를 유지하는 것과 같이 언급 한 모든 최적화 기술은 중요하지 않습니다.

어떻게 처리 하려는지 보지 않고 왜 속도가 느린지를 정확히 알기가 어렵습니다. 그러나 여기에 내가 그것을 repsonsive하게 만드는 방법은 다음과 같다. 리스트를 저장하는 ListModel을 생성한다. 스트림에 대한 참조가 아니라 단순한 List이다. 다른 스레드가 스트림에서 멀티 캐스트 데이터를 캡처하도록하려면 DataStreamCollector으로 전화를 걸 수 있습니다. 그런 다음 DataStreamCollector으로 확인하고 필요에 따라 ListModel을 업데이트하는 타이머 (javax.swing.Timer)에서 실행되는 스레드를 실행하십시오.

내 디자인은 UI 응답이 데이터 스트림과의 100 % 동기화보다 더 중요하다고 가정합니다. 타이머를 조정하면 반응하는 UI를 갖춘 최신 테이블을 갖게됩니다.

+0

'javax.swing.Timer'를 실행하기위한 별도의'Thread'가 필요 없습니다; 'ActionListener'는'TableModel'과'ListModel'을 갱신 할 수 있습니다. – trashgod

+0

그 관찰에 감사드립니다. 좋은 지적. – Thorn

2

어딘가에는 블랙홀에서 호출 미친 & 지저분한 SwingWorker 전에 대한 JTable & Performance이며,

이 프로젝트는 ChristmastTree 불렀다, 쓰레기 (옛 태양 튜토리얼)에 Oracles clean_up에

표준 자바 코드를 분실 Executor

import java.awt.*; 
import javax.swing.*; 
import javax.swing.event.*; 
import javax.swing.plaf.basic.*; 
import javax.swing.table.*; 

/** 
* CTTable extends JTable doing the following: <ul> <li>The UI is forced to be 
* CTTableUI so that a customer CellRendererPane can be installed. 
* <li>getCellRenderer is overriden to return the TableCellRenderer passed into 
* the constructor. <li>tableChanged is overriden to pass the call to super only 
* if the cell is visible. </ul> 
*/ 
public class CTTable extends JTable { 

    private static final long serialVersionUID = 1L; 
    private CTTableCellRenderer renderer; 

    public CTTable(CTTableCellRenderer renderer) { 
     super(); 
     this.renderer = renderer; 
     renderer.setFont(getFont()); 
    } 

    @Override 
    public void updateUI() { 
     super.updateUI(); 
     //Force the UI to be an instanceof CTTableUI. This approach will not work 
     //if you need to support more than one look and feel in your application. 
     setUI(new CTTableUI()); 
    } 

    @Override 
    public void setFont(Font font) { 
     super.setFont(font); 
     if (renderer != null) { 
      renderer.setFont(font); 
     } 
    } 

    @Override 
    public TableCellRenderer getCellRenderer(int row, int column) { 
     return renderer; 
    } 

    @Override 
    public void tableChanged(TableModelEvent e) { 
     if (e instanceof VisibleTableModelEvent && !((VisibleTableModelEvent) e).isVisible(this)) { 
      return;// Do nothing if this cell isn't visible. 
     } 
     super.tableChanged(e); 
    } 

    private static class CTTableUI extends BasicTableUI { 

     @Override 
     public void installUI(JComponent c) {   
      super.installUI(c);// Overriden to install our own CellRendererPane 
      c.remove(rendererPane); 
      rendererPane = new CTCellRendererPane(); 
      c.add(rendererPane); 
     } 
    } 

    /** 
    * CTCellRendererPane overrides paintComponent to NOT clone the Graphics 
    * passed in and NOT validate the Component passed in. This will NOT work if 
    * the painting code of the Component clobbers the graphics (scales it, 
    * installs a Paint on it...) and will NOT work if the Component needs to be 
    * validated before being painted. 
    */ 
    private static class CTCellRendererPane extends CellRendererPane { 

     private static final long serialVersionUID = 1L; 
     private Rectangle tmpRect = new Rectangle(); 

     @Override 
     public void repaint() { 
      // We can safely ignore this because we shouldn't be visible 
     } 

     @Override 
     public void repaint(int x, int y, int width, int height) { 
     } 

     @Override 
     public void paintComponent(Graphics g, Component c, Container p, int x, 
     int y, int w, int h, boolean shouldValidate) { 
      if (c == null) { 
       if (p != null) { 
        Color oldColor = g.getColor(); 
        g.setColor(p.getBackground()); 
        g.fillRect(x, y, w, h); 
        g.setColor(oldColor); 
       } 
       return; 
      } 
      if (c.getParent() != this) { 
       this.add(c); 
      } 
      c.setBounds(x, y, w, h); 
      // As we are only interested in using a JLabel as the renderer, 
      //which does nothing in validate we can override this to do nothing, 
      //if you need to support components that can do layout, this will 
      //need to be commented out, or conditionally validate. 
      shouldValidate = false; 
      if (shouldValidate) { 
       c.validate(); 
      } 
      boolean wasDoubleBuffered = false; 
      JComponent jc = (c instanceof JComponent) ? (JComponent) c : null; 
      if (jc != null && jc.isDoubleBuffered()) { 
       wasDoubleBuffered = true; 
       jc.setDoubleBuffered(false); 
      }//Don't create a new Graphics, reset the clip and translate the origin. 
      Rectangle clip = g.getClipBounds(tmpRect); 
      g.clipRect(x, y, w, h); 
      g.translate(x, y); 
      c.paint(g); 
      g.translate(-x, -y); 
      g.setClip(clip.x, clip.y, clip.width, clip.height); 
      if (wasDoubleBuffered) { 
       jc.setDoubleBuffered(true); 
      } 
      c.setBounds(-w, -h, 0, 0); 
     } 
    } 
} 

.

import java.awt.*; 
import javax.swing.*; 
import javax.swing.border.*; 
import javax.swing.table.*; 

/** 
* A custom TableCellRenderer that overrides a handful of methods: <ul> 
* <li>isOpaque and setBackground are overridden to avoid filling the 
* background, if possible. <li>firePropertyChange is overridden to do nothing. 
* If you need to support HTML text in the renderer than this should NOT be 
* overridden. <li>paint is overridden to forward the call directly to the UI, 
* avoiding the creation of a Graphics. This will NOT work if you need the 
* renderer to contain other childre or the Graphics is clobbered as part of 
* painting the UI. </ul> 
*/ 
public class CTTableCellRenderer extends DefaultTableCellRenderer { 

    private static final long serialVersionUID = 1L; 
    private Color background; 
    private Color foreground; 
    private Color editableForeground; 
    private Color editableBackground; 
    private Border focusBorder; 

    public CTTableCellRenderer() { 
     focusBorder = UIManager.getBorder("Table.focusCellHighlightBorder"); 
     editableForeground = UIManager.getColor("Table.focusCellForeground"); 
     editableBackground = UIManager.getColor("Table.focusCellBackground"); 
    } 

    @Override 
    public Component getTableCellRendererComponent(JTable table, Object value, 
      boolean isSelected, boolean hasFocus, int row, int column) { 
     boolean negative = (value != null && ((Integer) value).intValue() < 0); 
     // Reset the background based on the sign of the value. 
     if (isSelected) { 
      setForeground(table.getSelectionForeground()); 
      setBackground(table.getSelectionBackground()); 
     } else { 
      setForeground(table.getForeground()); 
      if (!negative) { 
       setBackground(null); 
      } else { 
       setBackground(Color.red); 
      } 
     }//NOTICE that we do NOT set the font here, because CTTable knows about 
     //us, it will set the font as appropriate. 
     if (hasFocus) { 
      setBorder(focusBorder); 
      if (table.isCellEditable(row, column)) { 
       setForeground(editableForeground); 
       setBackground(editableBackground); 
      } 
     } else { 
      setBorder(noFocusBorder); 
     } 
     setValue(value); 
     return this; 
    } 

    @Override 
    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { 
     // As long as you don't have any HTML text, this override is ok. 
    } 

    @Override// This override is only appropriate if this will never contain 
      // any children AND the Graphics is not clobbered during painting. 
    public void paint(Graphics g) { 
     ui.update(g, this); 
    } 

    @Override 
    public void setBackground(Color c) { 
     this.background = c; 
    } 

    @Override 
    public Color getBackground() { 
     return background; 
    } 

    @Override 
    public void setForeground(Color c) { 
     this.foreground = c; 
    } 

    @Override 
    public Color getForeground() { 
     return foreground; 
    } 

    @Override 
    public boolean isOpaque() { 
     return (background != null); 
    } 

    @Override // This is generally ok for non-Composite components (like Labels) 
    public void invalidate() { 
    } 

    @Override // Can be ignored, we don't exist in the containment hierarchy. 
    public void repaint() { 
    } 
} 

.

import javax.swing.table.*; 
import java.util.*; 

/** 
* CTTableModel, a TableModel, models a set of Datas as the rows. The data is 
* stored in a List of Lists. As the changes come in against a particular Data 
* object we also contain a map from Data to row. This can obviously be made 
* faster by pushing the row to the Data, but this may not be feasable in 
* applications of this sort. 
*/ 
public class CTTableModel extends AbstractTableModel { 

    private static final long serialVersionUID = 1L; 
    /** 
    * Maps from Data to an integer id giving the row of the data. 
    */ 
    private Map rowMap; 
    /** 
    * Number of columns to display. 
    */ 
    private int columns; 
    /** 
    * A List of Lists. 
    */ 
    private java.util.List rowData; 
    /** 
    * If true, batch cell updates using sharedModelEvent. 
    */ 
    private boolean batchChange; 
    /** 
    * Shared model event. 
    */ 
    private VisibleTableModelEvent sharedModelEvent; 

    public CTTableModel(int columns) { 
     this.columns = columns; 
     // Notice how they are not synchronized, we do NOT access this class 
     // from another thread, and therefore do not have to worry about 
     // synchronization. 
     rowData = new ArrayList(); 
     rowMap = new HashMap(); 
    } 

    public void addRow(Data rowID) { 
     int row = rowData.size(); 
     rowMap.put(rowID, new Integer(row)); 
     ArrayList colData = new ArrayList(); 
     for (int counter = 0; counter < columns; counter++) { 
      colData.add(null); 
     } 
     rowData.add(colData); 
     fireTableRowsInserted(row, row); 
    } 

    /** 
    * Toggles batch updates. When true and model changes are notified using a 
    * VisibleTableModelEvent. 
    * 
    * @param batch 
    */ 
    public void setBatchUpdates(boolean batch) { 
     this.batchChange = batch; 
     if (sharedModelEvent == null) { 
      sharedModelEvent = new VisibleTableModelEvent(this); 
     } 
     sharedModelEvent.reset(); 
    } 

    public boolean getBatchUpdates() { 
     return batchChange; 
    } 

    /** 
    * Sets the display value for a particular Data item at a particular cell. 
    * If notify is true listeners are notified, otherwise no listeners are 
    * notified. 
    * 
    * @param rowID 
    * @param col 
    * @param data 
    * @param notify 
    */ 
    public void set(Data rowID, int col, Object data, boolean notify) { 
     int row = ((Integer) rowMap.get(rowID)).intValue(); 
     ((java.util.List) rowData.get(row)).set(col, data); 
     if (notify) { 
      if (batchChange) { 
       sharedModelEvent.set(row, col); 
       fireTableChanged(sharedModelEvent); 
      } else { 
       fireTableCellUpdated(row, col); 
      } 
     } 
    } 

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

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

    @Override 
    public Object getValueAt(int rowIndex, int columnIndex) { 
     return ((java.util.List) rowData.get(rowIndex)).get(columnIndex); 
    } 
} 

.

/** 
* Unique ID for the data. 
*/ 
public class Data { 

    /** 
    * This is overriden to remind developers they should have an intelligent 
    * equals and hashCode. You do not need to override either of them, but if 
    * you override one you need to override the other. Additionaly, because 
    * they are used extensively to map the data that has changed to the table, 
    * equals and hashCode MUST be fast, cache data if you need to! 
    * 
    * @param x 
    */ 
    @Override 
    public boolean equals(Object x) { 
     return (this == x); 
    } 

    /** 
    * This is overriden to remind developers they should have an intelligent 
    * equals and hashCode. You do not need to override either of them, but if 
    * you override one you need to override the other. Additionaly, because 
    * they are used extensively to map the data that has changed to the table, 
    * equals and hashCode MUST be fast, cache data if you need to! 
    */ 
    @Override 
    public int hashCode() { 
     return super.hashCode(); 
    } 
} 

.

import java.util.ArrayList; 

/** 
* DataChange is used to associate a Data Object with a column identifier that 
* has changed. To avoid loads of garbage per update DataChanges are cached and 
* reused. 
*/ 
public class DataChange { 

    private static ArrayList sharedDataChanges = new ArrayList(); 
    private Data data; 
    private int col; 
    private int hashCode; 

    /** 
    * Obtains a DataChange for the specified Data and column. 
    * 
    * @param data 
    * @param col 
    * @return 
    */ 
    public static DataChange getDataChange(Data data, int col) { 
     synchronized (sharedDataChanges) { 
      int size = sharedDataChanges.size(); 
      if (size > 0) { 
       DataChange change = (DataChange) sharedDataChanges.remove(size - 1); 
       change.data = data; 
       change.col = col; 
       return change; 
      } 
     } 
     return new DataChange(data, col); 
    } 

    /** 
    * Indicates the DataChange is no longer needed and can be reused. 
    * 
    * @param change 
    */ 
    public static void releaseDataChange(DataChange change) { 
     synchronized (sharedDataChanges) { 
      sharedDataChanges.add(change); 
     } 
    } 

    DataChange(Data data, int col) { 
     this.data = data; 
     this.col = col; 
     hashCode = (data.hashCode() | col); 
    } 

    public Data getData() { 
     return data; 
    } 

    public int getColumn() { 
     return col; 
    } 

    @Override 
    public int hashCode() { 
     return hashCode; 
    } 

    public boolean equals(DataChange dc) { 
     if (dc == this) { 
      return true; 
     } 
     DataChange o = (DataChange) dc; 
     return (o.data == data && o.col == col); 
    } 
} 

.

import java.awt.*; 
import java.awt.event.*; 
import java.util.*; 
import javax.swing.*; 
import javax.swing.event.*; 

/** 
* The Main controller, responsible for wiring everything together. Pressing 
* return in any of the fields will trigger recreation of everything. 
*/ 
public class Main implements ActionListener { 

    // properties: columnCount, rowCount, updateSleepTime, eqSleepTime, 
    // threshold, generateSleep, generatorBatchCount 

    private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0); 
    private JTextField columnCount; 
    private JTextField rowCount; 
    private JTextField updateSleepTime; 
    private JTextField eqSleepTime; 
    private JTextField threshold; 
    private JTextField generateSleep; 
    private JTextField generatorBatchCount; 
    private JFrame frame; 
    static JLabel totalUpdateTime; 
    static JLabel notifyTime; 
    static JLabel paintTime; 
    static JLabel updateCount; 
    private JTable table; 
    private UpdateThread updateThread; 
    private GeneratorThread generatorThread; 
    private CTTableModel tableModel; 
    private static int NUM_COLUMNS = 40;// Initial values for the 7 properties. 
    private static int NUM_ROWS = 3000; 
    private static int UPDATE_SLEEP_TIME = 500; 
    private static int EQ_SLEEP_TIME = 10; 
    private static int UPDATE_ALL_THRESHOLD = 400000; 
    private static int GENERATOR_SLEEP_TIME = 40; 
    private static int BATCH_SIZE = 1000; 

    Main() { 
     frame = new JFrame(); 
     frame.getContentPane().setLayout(new GridBagLayout()); 
     columnCount = add("Columns: ", NUM_COLUMNS); 
     rowCount = add("Rows: ", NUM_ROWS); 
     updateSleepTime = add("Update Sleep: ", UPDATE_SLEEP_TIME); 
     eqSleepTime = add("EQ Sleep: ", EQ_SLEEP_TIME); 
     threshold = add("Update All Threshold: ", UPDATE_ALL_THRESHOLD); 
     generateSleep = add("Generator Sleep: ", GENERATOR_SLEEP_TIME); 
     generatorBatchCount = add("Batch Size: ", BATCH_SIZE); 
     table = new CTTable(new CTTableCellRenderer()); 
     table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 
     JScrollPane sp = new JScrollPane(table); 
     frame.getContentPane().add(sp, new GridBagConstraints(0, 3, 6, 1, 1, 1, 
       GridBagConstraints.WEST, GridBagConstraints.BOTH, EMPTY_INSETS, 0, 0)); 
     ChangeListener changeListener = new ChangeListener() { 

      @Override 
      public void stateChanged(ChangeEvent e) { 
       BoundedRangeModel m = (BoundedRangeModel) (e.getSource()); 
       if (updateThread != null) { 
        updateThread.setUpdatesEnabled(!(m.getValueIsAdjusting())); 
       } 
      } 
     }; 
     sp.getVerticalScrollBar().getModel().addChangeListener(changeListener); 
     sp.getHorizontalScrollBar().getModel().addChangeListener(changeListener); 
     totalUpdateTime = new JLabel(" "); 
     notifyTime = new JLabel(" "); 
     paintTime = new JLabel(" "); 
     updateCount = new JLabel(" "); 
     JPanel statusPanel = new JPanel(new GridBagLayout()); 
     frame.getContentPane().add(statusPanel, new GridBagConstraints(0, 4, 6, 1, 1, 0, 
       GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0)); 
     statusPanel.add(totalUpdateTime, new GridBagConstraints(0, 0, 1, 1, 1, 0, 
       GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0)); 
     statusPanel.add(notifyTime, new GridBagConstraints(1, 0, 1, 1, 1, 0, 
       GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0)); 
     statusPanel.add(paintTime, new GridBagConstraints(2, 0, 1, 1, 1, 0, 
       GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0)); 
     statusPanel.add(updateCount, new GridBagConstraints(3, 0, 1, 1, 1, 0, 
       GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0)); 
     frame.setTitle("Christmas Tree Demo Application"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setBounds(0, 0, 1000, 800); 
     frame.setVisible(true); 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException ie) { 
     } 
     reset(); 
    } 

    @Override 
    public void actionPerformed(ActionEvent ae) { 
     reset(); 
    } 

    private JTextField add(String name, int defaultValue) { 
     Container parent = frame.getContentPane(); 
     int row = parent.getComponentCount()/6; 
     int col = parent.getComponentCount() % 6; 
     parent.add(new JLabel(name), new GridBagConstraints(col, row, 1, 1, 0, 0, 
       GridBagConstraints.WEST, 0, EMPTY_INSETS, 0, 0)); 
     JTextField tf = new JTextField(Integer.toString(defaultValue)); 
     tf.addActionListener(this); 
     parent.add(tf, new GridBagConstraints(col + 1, row, 1, 1, 1, 0, 
       GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, EMPTY_INSETS, 0, 0)); 
     return tf; 
    } 

    private void reset() { 
     System.out.println("Columns: " + getInt(columnCount)); 
     System.out.println("Rows: " + getInt(rowCount)); 
     System.out.println("Update Sleep: " + getInt(updateSleepTime)); 
     System.out.println("EQ Sleep: " + getInt(eqSleepTime)); 
     System.out.println("Update All Threshold: " + getInt(threshold)); 
     System.out.println("Generator Sleep: " + getInt(generateSleep)); 
     System.out.println("Batch Size: " + getInt(generatorBatchCount)); 
     if (updateThread != null) { 
      System.out.println("interrupting!"); 
      updateThread.interrupt(); 
      generatorThread.interrupt(); 
     } 
     int cols = getInt(columnCount); 
     tableModel = new CTTableModel(cols); 
     ArrayList<Data> data = new ArrayList<Data>(); 
     for (int counter = getInt(rowCount) - 1; counter >= 0; counter--) { 
      Data dataID = new Data(); 
      data.add(dataID); 
      tableModel.addRow(dataID); 
      for (int colCounter = 0; colCounter < cols; colCounter++) { 
       if (colCounter % 2 == 0) { 
        tableModel.set(dataID, colCounter, 
          new Integer(counter * 100 + colCounter), false); 
       } else { 
        tableModel.set(dataID, colCounter, 
          new Integer(counter * -100 + colCounter), false); 
       } 
      } 
     } 
     table.setModel(tableModel); 
     generatorThread = new GeneratorThread(data, getInt(generateSleep), 
       getInt(generatorBatchCount), getInt(columnCount)); 
     updateThread = new UpdateThread(generatorThread, tableModel, 
       getInt(updateSleepTime), getInt(eqSleepTime), getInt(threshold)); 
     generatorThread.start(); 
     updateThread.start(); 
    } 

    private int getInt(JTextField tf) { 
     try { 
      return Integer.parseInt(tf.getText()); 
     } catch (NumberFormatException nfe) { 
      System.out.println("exception getting int: " + nfe); 
     } 
     return 0; 
    } 

    public static void main(String[] args) { 
     Main main = new Main(); 
    } 
} 

은 ... 방법

import java.awt.*; 
import java.lang.reflect.*; 
import java.util.*; 
import javax.swing.*; 

/** 
* Thread responsible for publishing changes to the Model. Sleeps for a defined 
* amount of time, waits for no activity in the UI and then users invokeAndWait 
* to publish changes. 
*/ 
public class UpdateThread extends Thread { 

    private int sleepTime; 
    private int eqSleepTime; 
    private int threshhold; 
    private boolean updatesEnabled; 
    private Runnable publishRunnable; 
    private Runnable emptyRunnable; 
    private GeneratorThread generator; 
    private CTTableModel model; 
    private Map<?, ?> lastData; 
    private long notifyTime; 
    private long paintTime; 
    private int updateCount; 
    private boolean done; 

    public UpdateThread(GeneratorThread generator, CTTableModel model, 
      int sleepTime, int eqSleepTime, int threshhold) { 
     super(); 
     setPriority(Thread.MIN_PRIORITY); 
     this.sleepTime = sleepTime; 
     this.eqSleepTime = eqSleepTime; 
     updatesEnabled = true; 
     this.threshhold = threshhold; 
     this.generator = generator; 
     this.model = model; 
     publishRunnable = new Runnable() { 
     // Runnable used to publish changes to the event dispatching thread 
      @Override 
      public void run() { 
       publishChangesOnEventDispatchingThread(); 
      } 
     }; 
     // Empty runnable, used to wait until the event dispatching thread 
     // has finished processing any pending events. 
     emptyRunnable = new Runnable() { 
      @Override 
      public void run() { 
      } 
     }; 
    } 

    @Override 
    public void interrupt() { 
     done = true; 
     super.interrupt(); 
    } 

    @Override 
    public void run() { 
     while (!isInterrupted() && !done) { 
      try { 
       sleep(sleepTime); 
       publishChanges(); 
      } catch (InterruptedException ie) { 
      } 
     } 
     System.out.println("UpdateThread done"); 
    } 

    /** 
    * Publishes changes on the event dispatching thread when the system isn't 
    * busy. This blocks the caller until the changes have been published. 
    */ 
    private void publishChanges() { 
     synchronized (this) {// Wait until the user isn't scrolling 
      while (!updatesEnabled) { 
       try { 
        wait(); 
       } catch (InterruptedException ie) { 
       } 
      } 
     } 
     EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
     // And wait until there are no pending events. 
     while (queue.peekEvent() != null) { 
      try { 
       sleep(eqSleepTime); 
      } catch (InterruptedException ie) { 
      } 
     } 
     final long start = System.currentTimeMillis(); 
     try { 
      SwingUtilities.invokeAndWait(publishRunnable); 
      // publish the changes on the event dispatching thread 
     } catch (InterruptedException ie) { 
     } catch (InvocationTargetException ite) { 
     } 
     try { 
     // Wait until the system has completed processing of any events we 
     // triggered as part of publishing changes. 
      SwingUtilities.invokeAndWait(emptyRunnable); 
     } catch (InterruptedException ie) { 
     } catch (InvocationTargetException ite) { 
     } 
     final long end = System.currentTimeMillis(); 
     try {// Update the display 
      SwingUtilities.invokeAndWait(new Runnable() { 
       @Override 
       public void run() { 
        Main.totalUpdateTime.setText("Total: " 
          + Integer.toString((int) (end - start))); 
        Main.notifyTime.setText("Notify: " 
          + Integer.toString((int) notifyTime)); 
        Main.paintTime.setText("Paint: " 
          + Integer.toString((int) paintTime)); 
        Main.updateCount.setText("Updated: " 
          + Integer.toString((int) updateCount)); 
       } 
      }); 
     } catch (InterruptedException ie) { 
     } catch (InvocationTargetException ite) { 
     } 
    } 

    /** 
    * Does the actual publishing of changes. 
    */ 
    private void publishChangesOnEventDispatchingThread() { 
     long start = System.currentTimeMillis(); 
     model.setBatchUpdates(true); 
     Map<?, ?> data = generator.getData(); 
     boolean notify = !(data.size() > threshhold || 
       (lastData != null && lastData.size() + data.size() > threshhold)); 
     updateCount = data.size(); 
     if (lastData != null) { 
      updateCount += lastData.size(); 
     }//Reset the data for the last set of changes we did, this forces the cells to change color. 
     if (lastData != null) { 
      publishData(lastData, true, notify); 
      Iterator<?> dataIterator = lastData.keySet().iterator(); 
      while (dataIterator.hasNext()) { 
       DataChange.releaseDataChange((DataChange) dataIterator.next()); 
      } 
      lastData.clear(); 
     } 
     publishData(data, false, notify);// Publish the current set of data. 
     model.setBatchUpdates(false); 
     if (!notify) { 
      model.fireTableDataChanged(); 
     } 
     lastData = data; 
     long end = System.currentTimeMillis(); 
     notifyTime = (end - start); 
     start = System.currentTimeMillis(); 
     RepaintManager.currentManager(null).paintDirtyRegions(); 
     end = System.currentTimeMillis(); 
     paintTime = (end - start); 
    } 

    /** 
    * Publish the passed in set of data. 
    */ 
    private void publishData(Map<?, ?> data, boolean negate, boolean notify) { 
     Iterator<?> dataIterator = data.keySet().iterator(); 
     while (dataIterator.hasNext()) { 
      DataChange change = (DataChange) dataIterator.next(); 
      Object value = data.get(change); 
      if (negate) { 
       value = new Integer(((Integer) value).intValue() * -1); 
      } 
      model.set(change.getData(), change.getColumn(), value, notify); 
     } 
    } 

    /** 
    * If enable is true, we are allowed to publish changes, otherwise we 
    * aren't. 
    * 
    * @param enable 
    */ 
    public void setUpdatesEnabled(boolean enable) { 
     synchronized (this) { 
      updatesEnabled = enable; 
      if (updatesEnabled) { 
       notify(); 
      } 
     } 
    } 

    public boolean getUpdatesEnabled() { 
     return updatesEnabled; 
    } 
} 

+0

+1은 잃어버린 것을 복원합니다. – trashgod

+0

손실되지 않음, 여기 : http://www.oracle.com/technetwork/java/christmastree-138396.html –

2

나머지는 계속 될 것입니다.

import java.awt.*; 
import javax.swing.*; 
import javax.swing.event.*; 
import javax.swing.table.*; 

/** 
* VisibleTableModelEvent adds the method isVisible to test if the cell 
* identified by the event is visible. 
*/ 
public class VisibleTableModelEvent extends TableModelEvent { 

    private static final long serialVersionUID = 1L; 
    private Point tmpPoint; 
    // This implementation caches the information for one JTable, it is 
    // certainly possible to cache it for more than one should 
    // you have this need. 
    private boolean valid; 
    private int firstVisRow; 
    private int lastVisRow; 
    private int firstVisCol; 
    private int lastVisCol; 

    public VisibleTableModelEvent(TableModel source) { 
     super(source, 0, 0, 0, UPDATE); 
     tmpPoint = new Point(); 
    } 

    /** 
    * Resets the underlying fields of the TableModelEvent. This assumes no ONE 
    * is going to cache the TableModelEvent. 
    * 
    * @param row 
    * @param col 
    */ 
    public void set(int row, int col) { 
     firstRow = row; 
     lastRow = row; 
     column = col; 
    } 

    /** 
    * Invoked to indicate the visible rows/columns need to be recalculated 
    * again. 
    */ 
    public void reset() { 
     valid = false; 
    } 

    public boolean isVisible(JTable table) { 
     if (!valid) {// Determine the visible region of the table.    
      Rectangle visRect = table.getVisibleRect(); 
      tmpPoint.x = visRect.x; 
      tmpPoint.y = visRect.y; 
      firstVisCol = table.columnAtPoint(tmpPoint); 
      firstVisRow = table.rowAtPoint(tmpPoint); 
      tmpPoint.x += visRect.width; 
      tmpPoint.y += visRect.height; 
      lastVisCol = table.columnAtPoint(tmpPoint); 
      if (lastVisCol == -1) { 
       lastVisCol = table.getColumnCount() - 1; 
      } 
      if ((lastVisRow = table.rowAtPoint(tmpPoint)) == -1) { 
       lastVisRow = table.getRowCount(); 
      } 
      valid = true; 
     } 
     return (firstRow >= firstVisRow && firstRow <= lastVisRow && column 
       >= firstVisCol && column <= lastVisCol); 
    } 
} 
관련 문제