JTable

2011-02-03 28 views
11

'da son satıra kaydırma Son noktaya yeni veri kaydının eklendiği yolda JTable kullanmayı deniyorum. Garip olan şey, kaydırma çubuğunun tablonun sonuna gitmemesidir; bunun yerine, her zaman sondan ikinci gösterir. Kaydırma çubuğuna her zaman tablonun sonuna gitmek için herhangi bir yolu? İşteJTable

benim kod parçasıdır: Sadece bu sorun koştu

table.scrollRectToVisible(table.getCellRect(table.getRowCount()-1, 0, true)); 

cevap

16

- bu kod satırı yanlış bir şey aslında yoktur; Sorun, onu yürüttüğünüzde yatar.

iseniz, benim gibi, (hatta invokeLater aracılığıyla) ya da TableModelListener kullanarak TableModel manipüle sonra hemen yürütmeye çalışırken size tarif ettiğiniz sorunu alırsınız. Sorun modeli yeni verilerle güncellenen iken (table.getRowCount() basitçe getRowCount() sizin TableModel üzerinde yöntem için bir geçiş olduğunu) yani JTable bileşeni görsel sahiptir değil.

daha önce açıklanan yerlerde kod satırını çalıştırmak

, gerçekte ( JTable.scrollRectToVisible kaydırma davranışını sağlayacak bir ebeveyn, örneğin JScrollPane için herhangi bir işlem defers JScrollPane anlatmaya çalışıyoruz) ekteki JTable bileşeninin sonunun ötesine kaydırmak için. Bunu yapmayı reddeder ve bunun yerine JTable bileşeninin geçerli sonuna kaydırır.

Bir noktada daha sonra, JTable bileşeni kendisini görsel olarak güncelleştirir ve daha önce kaydırılan satırın altına yeni eklenen satırı ekler. Bu kod satırının, yeni satırlar ekleyen koddan bağımsız olarak yürüten bir düğme ekleyerek çalıştığını doğrulayabilirsiniz.

private JTable _table = new JTable(); 
... 
JButton b = new JButton("Force scroll to bottom"); 
b.addActionListener(new ActionListener() { 
    public void actionPerformed(ActionEvent e) { 
     _table.scrollRectToVisible(_table.getCellRect(_table.getRowCount()-1, 0, true)); 
    } 
}); 
this.add(b); 

Bu sorunun çözümü biraz dolaylı, ancak testlerimde güvenilir bir şekilde çalışıyor. Sorun, nesnelerin görsel tarafında yer aldığından, diğer şeylerin yanı sıra componentResized yöntemini sağlayan ComponentListener'a bağlanmaya karar verdim. Bir satır eklendiğinde veya kaldırıldığında, JTable, JScrollPane'ın görünüm portundan dolayı görsel olarak görülemese bile yeniden boyutlandırılır. Bu nedenle, yalnızca bu dinleyici yöntemindeki kod satırını çalıştırın ve işler beklendiği gibi çalışacaktır. Sam'in cevap ve ben başka bir yerde bulunan başka bir sayfaya

private JTable _table = new JTable(); 
... 
_table.addComponentListener(new ComponentAdapter() { 
    public void componentResized(ComponentEvent e) { 
     _table.scrollRectToVisible(_table.getCellRect(_table.getRowCount()-1, 0, true)); 
    } 
}); 
+0

sevindim gerçi Meraklı :-) bir çözüm buldu? – kleopatra

+0

gerçekten teşekkür ederim, açık bir şekilde salıncakta kötü bir tasarım, onu düzeltmek zorundalar. – AvrDragon

+0

@AvrDragon tasarımla ilgili yanlış bir şey yok ... sadece alışılmışa kadar bekle-iç-kullanıcılar güncellendi. Son yorumumda zaten belirtildiği gibi, genellikle bir invokeLater'da yapılır. – kleopatra

1

Teşekkürler, ben bu sorunu çözmek için başardı.

Çözümü paylaşacağımı düşündüm ki, sıradaki adam hepsini bir araya getirmek zorunda değil.

import java.awt.Rectangle; 
import java.awt.event.ComponentAdapter; 
import java.awt.event.ComponentEvent; 
import java.security.NoSuchAlgorithmException; 
import java.security.SecureRandom; 
import java.text.SimpleDateFormat; 
import java.util.Date; 

import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.JViewport; 
import javax.swing.ScrollPaneConstants; 
import javax.swing.SwingUtilities; 
import javax.swing.table.DefaultTableModel; 

/** 
* Demonstrate displaying a specific cell in a JTable when a row is added. 
* <p> 
* The Table Row Index is displayed in one of the table's columns. 
* <p> 
* The cell containing the Value will be selected for displaying. 
* <p> 
* The specified cell will be made visible and, if possible, positioned in the center of the Viewport. 
* <p> 
* The code works regardless of: 
* <ul> 
* <li>Whether or not the table data is sorted</li> 
* <li>The position/visibility of the "Value" column</li> 
* </ul> 
*/ 
public class JTableScrollToRow 
{ 
    static SecureRandom   random; 
    private DefaultTableModel dtm; 

    static 
    { 
     try 
     { 
      random = SecureRandom.getInstance("SHA1PRNG"); 
      int seed = Integer.parseInt((new SimpleDateFormat("SSS")).format(new Date())); 
      random.setSeed(random.generateSeed(seed)); 
     } 
     catch (NoSuchAlgorithmException e) 
     { 
      e.printStackTrace(); 
     } 
    } 

    public void buildGUI() 
    { 
     Object[][] data = {}; 
     Object colNames[] = { 
       "Value", 
       "TableRowIx", 
       "Column A", 
       "Column B", 
       "Column C", 
       "Column D", 
       "Column E", 
       "Column F" }; 

     dtm = new DefaultTableModel(data, colNames); 
     final JTable sampleTable = new JTable(dtm); 
     sampleTable.setDragEnabled(false); 
     sampleTable.setAutoCreateRowSorter(true); 

     // Turn off auto-resizing to allow for columns moved out of the Viewport 
     sampleTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 

     // Populate the table with some data 
     for (int x = 0; x < 200; x++) 
     { 
      addRow(x); 
     } 

     // Create a ScrollPane 
     JScrollPane sp = new JScrollPane(sampleTable); 

     // Provide a horizontal scroll bar so that columns can be scrolled out of the Viewport 
     sp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); 

     final JFrame f = new JFrame(); 
     f.getContentPane().add(sp); 
     f.setTitle("JTable cell display example"); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setVisible(true); 

     // Create a thread that periodically adds a row to the table 
     Thread rowAdder = new Thread(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       do 
       { 
        try 
        { 
         int secs = 5; 
         Thread.sleep(secs * 1000); 
        } 
        catch (InterruptedException e) 
        { 
         e.printStackTrace(); 
        } 

        // Add a row 
        addRow(dtm.getRowCount()); 
       } while (true); 
      } 
     }); 
     rowAdder.start(); 

     // Add the custom ComponentListener 
     sampleTable.addComponentListener(new JTableCellDisplayer(sampleTable)); 
    } 

    /** 
    * Display a table row when it is added to a JTable.<br> 
    * Details available at <a 
    * href="http://stackoverflow.com/questions/4890282/howto-to-scroll-to-last-row-on-jtable">StackOverflow</a>. 
    * <p> 
    * <b>Key information:</b> Whenever a row is added or removed the JTable resizes. This occurs even if the row is 
    * outside of the JScrollPane's Viewport (i.e., the row is not visible). 
    */ 
    class JTableCellDisplayer extends ComponentAdapter 
    { 
     boolean selRow  = false; 
     boolean selCol  = false; 
     boolean firstTime = true; 
     boolean selectData = false; 
     JTable table; 

     public JTableCellDisplayer(JTable jTable) 
     { 
      table = jTable; 
     } 

     @Override 
     public void componentResized(ComponentEvent e) 
     { 
      if (firstTime) 
      { 
       firstTime = false; 
       return; 
      } 

      int viewIx = table.convertRowIndexToView(table.getRowCount() - 1); 

      if (!selRow 
        && !selCol) 
      { 
       System.out.println(" - Select nothing - selectData=" 
         + selectData); 
      } 
      else if (selRow 
        && !selCol) 
      { 
       System.out.println(" - Select row only - selectData=" 
         + selectData); 
      } 
      else if (!selRow 
        && selCol) 
      { 
       System.out.println(" - Select column only - selectData=" 
         + selectData); 
      } 
      else 
      { 
       System.out.println(" - Select cell - selectData=" 
         + selectData); 
      } 

      // If data should be selected, set the selection policies on the table. 
      if (selectData) 
      { 
       table.setRowSelectionAllowed(selRow); 
       table.setColumnSelectionAllowed(selCol); 
      } 

      // Scroll to the VALUE cell (columnIndex=0) that was added 
      displayTableCell(table, viewIx, table.convertColumnIndexToView(0), selectData); 

      // Cycle through all possibilities 
      if (!selRow 
        && !selCol) 
      { 
       selRow = true; 
      } 
      else if (selRow 
        && !selCol) 
      { 
       selRow = false; 
       selCol = true; 
      } 
      else if (!selRow 
        && selCol) 
      { 
       selRow = true; 
       selCol = true; 
      } 
      else 
      { 
       selRow = false; 
       selCol = false; 
       selectData = !selectData; 
      } 

     } 
    } 

    /** 
    * Assuming the table is contained in a JScrollPane, scroll to the cell (vRowIndex, vColIndex). <br> 
    * The specified cell is guaranteed to be made visible.<br> 
    * Every attempt will be made to position the cell in the center of the Viewport. <b>Note:</b> This may not be 
    * possible if the row is too close to the top or bottom of the Viewport. 
    * <p> 
    * It is possible to select the specified cell. The amount of data selected (none, entire row, entire column or a 
    * single cell) is dependent on the settings specified by {@link JTable#setColumnSelectionAllowed(boolean)} and 
    * {@link JTable#setRowSelectionAllowed(boolean)}. 
    * <p> 
    * Original code found <a href="http://www.exampledepot.com/egs/javax.swing.table/VisCenter.html">here</a>. 
    * <p> 
    * 
    * @param table 
    *   - The table 
    * @param vRowIndex 
    *   - The view row index 
    * @param vColIndex 
    *   - The view column index 
    * @param selectCell 
    *   - If <code>true</code>, the cell will be selected in accordance with the table's selection policy; 
    *   otherwise the selected data will not be changed. 
    * @see JTable#convertRowIndexToView(int) 
    * @see JTable#convertColumnIndexToView(int) 
    */ 
    public static void displayTableCell(JTable table, int vRowIndex, int vColIndex, boolean selectCell) 
    { 
     if (!(table.getParent() instanceof JViewport)) 
     { 
      return; 
     } 

     JViewport viewport = (JViewport) table.getParent(); 

     /* This rectangle is relative to the table where the 
     * northwest corner of cell (0,0) is always (0,0). 
     */ 
     Rectangle rect = table.getCellRect(vRowIndex, vColIndex, true); 

     // The location of the view relative to the table 
     Rectangle viewRect = viewport.getViewRect(); 

     /* 
     * Translate the cell location so that it is relative 
     * to the view, assuming the northwest corner of the 
     * view is (0,0). 
     */ 
     rect.setLocation(rect.x 
       - viewRect.x, rect.y 
       - viewRect.y); 

     // Calculate location of rectangle if it were at the center of view 
     int centerX = (viewRect.width - rect.width)/2; 
     int centerY = (viewRect.height - rect.height)/2; 

     /* 
     * Fake the location of the cell so that scrollRectToVisible 
     * will move the cell to the center 
     */ 
     if (rect.x < centerX) 
     { 
      centerX = -centerX; 
     } 
     if (rect.y < centerY) 
     { 
      centerY = -centerY; 
     } 
     rect.translate(centerX, centerY); 

     // If desired and allowed, select the appropriate cell 
     if (selectCell 
       && (table.getRowSelectionAllowed() || table.getColumnSelectionAllowed())) 
     { 
      // Clear any previous selection 
      table.clearSelection(); 

      table.setRowSelectionInterval(vRowIndex, vRowIndex); 
      table.setColumnSelectionInterval(vColIndex, vColIndex); 
     } 

     // Scroll the area into view. 
     viewport.scrollRectToVisible(rect); 
    } 

    private String addRow(int tableRowIndex) 
    { 
     String retVal; 

     int value = random.nextInt(99999999); 
     dtm.addRow(new Object[] { 
       value, 
       tableRowIndex, 
       random.nextInt(99999999), 
       random.nextInt(99999999), 
       random.nextInt(99999999), 
       random.nextInt(99999999), 
       random.nextInt(99999999), 
       random.nextInt(99999999), }); 

     retVal = "Row added - value=" 
       + value + " & tableRowIx=" + tableRowIndex; 

     System.out.println(retVal); 
     return retVal; 
    } 

    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       new JTableScrollToRow().buildGUI(); 
      } 
     }); 
    } 

} 
+2

a) EDT'ye uyumu b) EDT – kleopatra

1

Neden sizin TableModel uygulanmasında güncellenmesi üzerine fireTableRowsInserted çağrı ?

Ben usally benim TableModel uygulanmasında aşağıda böyle bir şey var:

public void addRow (MyDataType valToAdd){ 
rows.add(valToAdd); 
fireTableRowsInserted(rows.size()-1,rows.size()-1); 
} 
2

çağrıyı bu yöntem, tablonun bot aşağı kaydırmak istediğinizde. Ve bu sorunun üstesinden bu problem çözülür. işe yaramamıştı invokeLater içine scrollRectToVisible sarma hiç görülmemiş bir bağlam - Eğer bu oldu bir sscce göstermek dikkate alacağını:

public void scrolltable() 
{ 
    table.addComponentListener(new ComponentAdapter() { 
     public void componentResized(ComponentEvent e) { 
      int lastIndex =table.getCellRect(table.getRowCount()-1; 
      table.changeSelection(lastIndex, 0,false,false); 
     } 
    }); 
} 
+1

'un dışındaki Swing bileşenlerine erişmeyin lütfen sadece bir satırı düzenleyin: - int lastIndex = table.getRowCount() - 1; Bu işe yarıyor ..! –