2012-08-14 37 views
4

我有一個表叫table和它充滿了數據,我也有一個MessageFormat頭我想作爲一個頭用於打印JTable這個打印用的MessageFormat多個標題行是MessageFormat如何使用JTable中

MessageFormat header = new MessageFormat("Product: " 
        + task.getProductName() + " Job: " 
        + task.getJobNumber() + " Task: " + task.getTaskID() 
        ); 

我想打印3行頭,一個是產品,作業和任務

我打印此table的方式是像這樣:

table.print(JTable.PrintMode.FIT_WIDTH, header, null); 

我似乎無法弄清楚如何在3個單獨的行中打印標題,我嘗試使用\n來創建一個新行,但似乎並不奏效。

回答

5

這會是很長的答案(代碼明智),因爲我發現的唯一的解決辦法是實現一個自定義Printable。當然,我自己並沒有編寫下面的代碼,我大多複製了我從jdk源代碼中提取的代碼並做了一些調整。

我們在這裏:

這是你說你調用打印方法的方式:

DefaultTableModel dtm = new DefaultTableModel(new String[] { "Column 1" }, 1); 

JTable table = new JTable(dtm) { 
@Override 
    public Printable getPrintable(PrintMode printMode, MessageFormat headerFormat, MessageFormat footerFormat) { 
     return new TablePrintable(this, printMode, headerFormat, footerFormat); 
    } 
}; 

其中TablePrintable是下面的類(對不起,不是簡潔這裏):

static class TablePrintable implements Printable { 

    private final JTable table; 
    private final JTableHeader header; 
    private final TableColumnModel colModel; 
    private final int totalColWidth; 
    private final JTable.PrintMode printMode; 
    private final MessageFormat headerFormat; 
    private final MessageFormat footerFormat; 
    private int last = -1; 
    private int row = 0; 
    private int col = 0; 
    private final Rectangle clip = new Rectangle(0, 0, 0, 0); 
    private final Rectangle hclip = new Rectangle(0, 0, 0, 0); 
    private final Rectangle tempRect = new Rectangle(0, 0, 0, 0); 
    private static final int H_F_SPACE = 8; 
    private static final float HEADER_FONT_SIZE = 18.0f; 
    private static final float FOOTER_FONT_SIZE = 12.0f; 
    private final Font headerFont; 
    private final Font footerFont; 

    public TablePrintable(JTable table, JTable.PrintMode printMode, MessageFormat headerFormat, 
      MessageFormat footerFormat) { 

     this.table = table; 

     header = table.getTableHeader(); 
     colModel = table.getColumnModel(); 
     totalColWidth = colModel.getTotalColumnWidth(); 

     if (header != null) { 
      // the header clip height can be set once since it's unchanging 
      hclip.height = header.getHeight(); 
     } 

     this.printMode = printMode; 

     this.headerFormat = headerFormat; 
     this.footerFormat = footerFormat; 

     // derive the header and footer font from the table's font 
     headerFont = table.getFont().deriveFont(Font.BOLD, HEADER_FONT_SIZE); 
     footerFont = table.getFont().deriveFont(Font.PLAIN, FOOTER_FONT_SIZE); 
    } 

    @Override 
    public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { 

     // for easy access to these values 
     final int imgWidth = (int) pageFormat.getImageableWidth(); 
     final int imgHeight = (int) pageFormat.getImageableHeight(); 

     if (imgWidth <= 0) { 
      throw new PrinterException("Width of printable area is too small."); 
     } 

     // to pass the page number when formatting the header and footer 
     // text 
     Object[] pageNumber = new Object[] { Integer.valueOf(pageIndex + 1) }; 

     // fetch the formatted header text, if any 
     String headerText = null; 
     if (headerFormat != null) { 
      headerText = headerFormat.format(pageNumber); 
     } 

     // fetch the formatted footer text, if any 
     String footerText = null; 
     if (footerFormat != null) { 
      footerText = footerFormat.format(pageNumber); 
     } 

     // to store the bounds of the header and footer text 
     Rectangle2D hRect = null; 
     Rectangle2D fRect = null; 

     // the amount of vertical space needed for the header and footer 
     // text 
     int headerTextSpace = 0; 
     int footerTextSpace = 0; 

     // the amount of vertical space available for printing the table 
     int availableSpace = imgHeight; 

     // if there's header text, find out how much space is needed for it 
     // and subtract that from the available space 
     if (headerText != null) { 
      graphics.setFont(headerFont); 
      int nbLines = headerText.split("\n").length; 
      hRect = graphics.getFontMetrics().getStringBounds(headerText, graphics); 

      hRect = new Rectangle2D.Double(hRect.getX(), Math.abs(hRect.getY()), hRect.getWidth(), 
        hRect.getHeight() * nbLines); 

      headerTextSpace = (int) Math.ceil(hRect.getHeight() * nbLines); 
      availableSpace -= headerTextSpace + H_F_SPACE; 
     } 

     // if there's footer text, find out how much space is needed for it 
     // and subtract that from the available space 
     if (footerText != null) { 
      graphics.setFont(footerFont); 
      fRect = graphics.getFontMetrics().getStringBounds(footerText, graphics); 

      footerTextSpace = (int) Math.ceil(fRect.getHeight()); 
      availableSpace -= footerTextSpace + H_F_SPACE; 
     } 

     if (availableSpace <= 0) { 
      throw new PrinterException("Height of printable area is too small."); 
     } 

     // depending on the print mode, we may need a scale factor to 
     // fit the table's entire width on the page 
     double sf = 1.0D; 
     if (printMode == JTable.PrintMode.FIT_WIDTH && totalColWidth > imgWidth) { 

      // if not, we would have thrown an acception previously 
      assert imgWidth > 0; 

      // it must be, according to the if-condition, since imgWidth > 0 
      assert totalColWidth > 1; 

      sf = (double) imgWidth/(double) totalColWidth; 
     } 

     // dictated by the previous two assertions 
     assert sf > 0; 

     // This is in a loop for two reasons: 
     // First, it allows us to catch up in case we're called starting 
     // with a non-zero pageIndex. Second, we know that we can be called 
     // for the same page multiple times. The condition of this while 
     // loop acts as a check, ensuring that we don't attempt to do the 
     // calculations again when we are called subsequent times for the 
     // same page. 
     while (last < pageIndex) { 
      // if we are finished all columns in all rows 
      if (row >= table.getRowCount() && col == 0) { 
       return NO_SUCH_PAGE; 
      } 

      // rather than multiplying every row and column by the scale 
      // factor 
      // in findNextClip, just pass a width and height that have 
      // already 
      // been divided by it 
      int scaledWidth = (int) (imgWidth/sf); 
      int scaledHeight = (int) ((availableSpace - hclip.height)/sf); 

      // calculate the area of the table to be printed for this page 
      findNextClip(scaledWidth, scaledHeight); 

      last++; 
     } 

     // create a copy of the graphics so we don't affect the one given to 
     // us 
     Graphics2D g2d = (Graphics2D) graphics.create(); 

     // translate into the co-ordinate system of the pageFormat 
     g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); 

     // to save and store the transform 
     AffineTransform oldTrans; 

     // if there's footer text, print it at the bottom of the imageable 
     // area 
     if (footerText != null) { 
      oldTrans = g2d.getTransform(); 

      g2d.translate(0, imgHeight - footerTextSpace); 

      String[] lines = footerText.split("\n"); 
      printText(g2d, lines, fRect, footerFont, imgWidth); 

      g2d.setTransform(oldTrans); 
     } 

     // if there's header text, print it at the top of the imageable area 
     // and then translate downwards 
     if (headerText != null) { 
      String[] lines = headerText.split("\n"); 
      printText(g2d, lines, hRect, headerFont, imgWidth); 

      g2d.translate(0, headerTextSpace + H_F_SPACE); 
     } 

     // constrain the table output to the available space 
     tempRect.x = 0; 
     tempRect.y = 0; 
     tempRect.width = imgWidth; 
     tempRect.height = availableSpace; 
     g2d.clip(tempRect); 

     // if we have a scale factor, scale the graphics object to fit 
     // the entire width 
     if (sf != 1.0D) { 
      g2d.scale(sf, sf); 

      // otherwise, ensure that the current portion of the table is 
      // centered horizontally 
     } else { 
      int diff = (imgWidth - clip.width)/2; 
      g2d.translate(diff, 0); 
     } 

     // store the old transform and clip for later restoration 
     oldTrans = g2d.getTransform(); 
     Shape oldClip = g2d.getClip(); 

     // if there's a table header, print the current section and 
     // then translate downwards 
     if (header != null) { 
      hclip.x = clip.x; 
      hclip.width = clip.width; 

      g2d.translate(-hclip.x, 0); 
      g2d.clip(hclip); 
      header.print(g2d); 

      // restore the original transform and clip 
      g2d.setTransform(oldTrans); 
      g2d.setClip(oldClip); 

      // translate downwards 
      g2d.translate(0, hclip.height); 
     } 

     // print the current section of the table 
     g2d.translate(-clip.x, -clip.y); 
     g2d.clip(clip); 
     table.print(g2d); 

     // restore the original transform and clip 
     g2d.setTransform(oldTrans); 
     g2d.setClip(oldClip); 

     // draw a box around the table 
     g2d.setColor(Color.BLACK); 
     g2d.drawRect(0, 0, clip.width, hclip.height + clip.height); 

     // dispose the graphics copy 
     g2d.dispose(); 

     return PAGE_EXISTS; 
    } 

    private void printText(Graphics2D g2d, String[] lines, Rectangle2D rect, Font font, int imgWidth) { 

     g2d.setColor(Color.BLACK); 
     g2d.setFont(font); 

     for (int i = 0; i < lines.length; i++) { 
      int tx; 

      // if the text is small enough to fit, center it 
      if (rect.getWidth() < imgWidth) { 
       tx = (int) (imgWidth/2 - g2d.getFontMetrics().getStringBounds(lines[i], g2d).getWidth()/2); 

       // otherwise, if the table is LTR, ensure the left side of 
       // the text shows; the right can be clipped 
      } else if (table.getComponentOrientation().isLeftToRight()) { 
       tx = 0; 

       // otherwise, ensure the right side of the text shows 
      } else { 
       tx = -(int) (Math.ceil(rect.getWidth()) - imgWidth); 
      } 

      int ty = (int) Math.ceil(Math.abs(rect.getY() + i * rect.getHeight()/lines.length)); 
      g2d.drawString(lines[i], tx, ty); 
     } 
    } 

    private void findNextClip(int pw, int ph) { 
     final boolean ltr = table.getComponentOrientation().isLeftToRight(); 

     // if we're ready to start a new set of rows 
     if (col == 0) { 
      if (ltr) { 
       // adjust clip to the left of the first column 
       clip.x = 0; 
      } else { 
       // adjust clip to the right of the first column 
       clip.x = totalColWidth; 
      } 

      // adjust clip to the top of the next set of rows 
      clip.y += clip.height; 

      // adjust clip width and height to be zero 
      clip.width = 0; 
      clip.height = 0; 

      // fit as many rows as possible, and at least one 
      int rowCount = table.getRowCount(); 
      int rowHeight = table.getRowHeight(row); 
      do { 
       clip.height += rowHeight; 

       if (++row >= rowCount) { 
        break; 
       } 

       rowHeight = table.getRowHeight(row); 
      } while (clip.height + rowHeight <= ph); 
     } 

     // we can short-circuit for JTable.PrintMode.FIT_WIDTH since 
     // we'll always fit all columns on the page 
     if (printMode == JTable.PrintMode.FIT_WIDTH) { 
      clip.x = 0; 
      clip.width = totalColWidth; 
      return; 
     } 

     if (ltr) { 
      // adjust clip to the left of the next set of columns 
      clip.x += clip.width; 
     } 

     // adjust clip width to be zero 
     clip.width = 0; 

     // fit as many columns as possible, and at least one 
     int colCount = table.getColumnCount(); 
     int colWidth = colModel.getColumn(col).getWidth(); 
     do { 
      clip.width += colWidth; 
      if (!ltr) { 
       clip.x -= colWidth; 
      } 

      if (++col >= colCount) { 
       // reset col to 0 to indicate we're finished all columns 
       col = 0; 

       break; 
      } 

      colWidth = colModel.getColumn(col).getWidth(); 
     } while (clip.width + colWidth <= pw); 

    } 
} 

這裏是結果(我希望這是你所期望的): JTable with multiple line header when printed

+0

它看起來不錯,明天我會在工作中試一下,看看它是否可行。 – 2012-08-24 03:49:14

+1

+1,但我更願意委託給表的默認打印而不是c&p全部 – kleopatra 2012-08-24 07:33:45

+0

我也是。如果我能延長班級的話,我會延長班級,所以我不能... – aymeric 2012-08-24 08:40:40

2

你可以嘗試

StringBuilder builder = new StringBuilder(); 
builder.append("Product: "); 
builder.append(task.getProductName()); 
builder.append(System.getProperty("line.separator")); 
builder.append("Job: "); 
builder.append(task.getJobNumber()); 
builder.append(System.getProperty("line.separator")); 
builder.append("Task: "); 
builder.append(task.getTaskID(); 

MessageFormat header = new MessageFormat(builder.toString()); 

如果這不工作,那麼你將不得不建立自己的打印機工作,只要你想它正是佈局中的頭。

+0

不討論JTable標題,而是打印頁面的標題,標題與實際表格分開。我試過了,HTML沒有被解析。 – 2012-08-20 17:41:56

+0

@Epicmaster:您可能希望在您打印的問題中更清楚。 – 2012-08-20 18:18:07

+2

我說:「我想打印3行...」我說「我打印這張表的方式是:」表達式中使用了print(),我認爲這很明顯,但我會嘗試使下次更明顯。但是使用System.getProperty(「line.separator」)似乎無法使用或不使用StringBuilder,即使構建器似乎正確顯示在控制檯中。 – 2012-08-20 19:17:44

2

如果您使用getPrintable方法而不添加頁眉/頁腳文本,則可以將返回的Printable包含/修飾爲可以更好地控制標題的位置,以及指定多行標題的位置。請參閱提及的方法的javadoc

將此Printable封裝在另一箇中以便創建複雜的報告和文檔是完全有效的。您甚至可能要求將不同的頁面渲染到不同大小的可打印區域。該實現必須準備好處理這個問題(可能通過在運行中進行佈局計算)。但是,如果PrintMode.NORMAL不得不在頁面之間傳播列,則爲每個頁面提供不同的高度可能無法很好地工作。

我有Printable不是足夠的經驗來幫助您進一步瞭解如何真正做到這一點

2

基本上,@aymeric的答案是正確的:自定義可打印實現無法實現。略少一個辦法做到這一點ç& p是有一個自定義的實現,

  • 接管頁眉/頁腳打印
  • 代表表打印本身默認的打印

訣竅在這種做法是欺騙代表tablePrintable認爲該頁面比實際小,與自定義頁面格式

more details (and code)

0

我已將兩個MessageFormat陣列用作問題的簡潔解決方案。您會在下面打印出最終結果: enter image description here

代碼概述如下。

 try  
     { 
      PrinterJob job = PrinterJob.getPrinterJob(); 

      MessageFormat[] header = new MessageFormat[6]; 

      // Assign the arrays with 6 String values for the headers 
      header[0] = new MessageFormat(""); 
      header[1] = new MessageFormat(theExamSelection); 
      header[2] = new MessageFormat(""); 
      header[3] = new MessageFormat("Scrud 60 - Grade Returns - Random Sample"); 
      header[4] = new MessageFormat(""); 
      header[5] = new MessageFormat(theSubjectSelection+" - "+theLevelSelection+" - "+thePaperSelection); 

      MessageFormat[] footer = new MessageFormat[4]; 

      // Assign the 4 Strings to the footer array 
      footer[0] = new MessageFormat("Assistant Examiner Signature:______________ Date:___ /___ /_____ "); 
      footer[1] = new MessageFormat(""); 
      footer[2] = new MessageFormat(""); 
      footer[3] = new MessageFormat("Advising Examiner Signature:______________ Date:___ /___ /_____ "); 

      //here you place the JTable to print 
      // in this case its called randomSample_gradeBreakdown_jTable 
      // along with the header and footer arrays 
      job.setPrintable(new PrintTableMultiLine(randomSample_gradeBreakdown_jTable, JTable.PrintMode.FIT_WIDTH, header, footer)); 

      job.print(); 

     } 
     catch (java.awt.print.PrinterException e) 
     { 
      System.err.format("Cannot print %s%n", e.getMessage()); 

      JOptionPane.showMessageDialog(this, 
        "Check that your printer is working correctly","PRINT ERROR",JOptionPane.ERROR_MESSAGE 
        ); 
     }