Java/Swing Components/Chart

Материал из Java эксперт
Перейти к: навигация, поиск

Chart Bean 2

The various table charting classes

/*
Java Swing, 2nd Edition
By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole
ISBN: 0-596-00408-7
Publisher: O"Reilly 
*/
// ChartTester.java
//A test harness for the various table charting classes. (see
//ChartPainter.java, PieChartPainter.java, TableChart.java and
//TableChartPopup.java)
//
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.plaf.ruponentUI;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
public class ChartTester extends JFrame {
  public ChartTester() {
    super("Simple JTable Test");
    setSize(300, 200);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    TableModel tm = new AbstractTableModel() {
      String data[][] = { { "Ron", "0.00", "68.68", "77.34", "78.02" },
          { "Ravi", "0.00", "70.89", "64.17", "75.00" },
          { "Maria", "76.52", "71.12", "75.68", "74.14" },
          { "James", "70.00", "15.72", "26.40", "38.32" },
          { "Ellen", "80.32", "78.16", "83.80", "85.72" } };
      String headers[] = { "", "Q1", "Q2", "Q3", "Q4" };
      public int getColumnCount() {
        return headers.length;
      }
      public int getRowCount() {
        return data.length;
      }
      public String getColumnName(int col) {
        return headers[col];
      }
      public Class getColumnClass(int col) {
        return (col == 0) ? String.class : Number.class;
      }
      public boolean isCellEditable(int row, int col) {
        return true;
      }
      public Object getValueAt(int row, int col) {
        return data[row][col];
      }
      public void setValueAt(Object value, int row, int col) {
        data[row][col] = (String) value;
        fireTableRowsUpdated(row, row);
      }
    };
    JTable jt = new JTable(tm);
    JScrollPane jsp = new JScrollPane(jt);
    getContentPane().add(jsp, BorderLayout.CENTER);
    final TableChartPopup tcp = new TableChartPopup(tm);
    JButton button = new JButton("Show me a chart of this table");
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        tcp.setVisible(true);
      }
    });
    getContentPane().add(button, BorderLayout.SOUTH);
  }
  public static void main(String args[]) {
    ChartTester ct = new ChartTester();
    ct.setVisible(true);
  }
}
//TableChartPopup.java
//A popup framework for showing a chart of table data. This class also
//turns on tooltips for the chart.
//
class TableChartPopup extends JFrame {
  public TableChartPopup(TableModel tm) {
    super("Table Chart");
    setSize(300, 200);
    TableChart tc = new TableChart(tm);
    getContentPane().add(tc, BorderLayout.CENTER);
    // Use the next line to turn on tooltips:
    ToolTipManager.sharedInstance().registerComponent(tc);
  }
}
//TableChart.java
//A chart-generating class that uses the TableModel interface to get
//its data.
//
class TableChart extends JComponent implements TableModelListener {
  protected TableModel model;
  protected ChartPainter cp;
  protected double[] percentages; // pie slices
  protected String[] labels; // labels for slices
  protected String[] tips; // tooltips for slices
  protected java.text.NumberFormat formatter = java.text.NumberFormat
      .getPercentInstance();
  public TableChart(TableModel tm) {
    setUI(cp = new PieChartPainter());
    setModel(tm);
  }
  public void setTextFont(Font f) {
    cp.setTextFont(f);
  }
  public Font getTextFont() {
    return cp.getTextFont();
  }
  public void setTextColor(Color c) {
    cp.setTextColor(c);
  }
  public Color getTextColor() {
    return cp.getTextColor();
  }
  public void setColor(Color[] clist) {
    cp.setColor(clist);
  }
  public Color[] getColor() {
    return cp.getColor();
  }
  public void setColor(int index, Color c) {
    cp.setColor(index, c);
  }
  public Color getColor(int index) {
    return cp.getColor(index);
  }
  public String getToolTipText(MouseEvent me) {
    if (tips != null) {
      int whichTip = cp.indexOfEntryAt(me);
      if (whichTip != -1) {
        return tips[whichTip];
      }
    }
    return null;
  }
  public void tableChanged(TableModelEvent tme) {
    // Rebuild the arrays only if the structure changed.
    updateLocalValues(tme.getType() != TableModelEvent.UPDATE);
  }
  public void setModel(TableModel tm) {
    // get listener code correct.
    if (tm != model) {
      if (model != null) {
        model.removeTableModelListener(this);
      }
      model = tm;
      model.addTableModelListener(this);
      updateLocalValues(true);
    }
  }
  public TableModel getModel() {
    return model;
  }
  // Run through the model and count every cell (except the very first column,
  // which we assume is the slice label column).
  protected void calculatePercentages() {
    double runningTotal = 0.0;
    for (int i = model.getRowCount() - 1; i >= 0; i--) {
      percentages[i] = 0.0;
      for (int j = model.getColumnCount() - 1; j >= 0; j--) {
        // First try the cell as a Number object.
        Object val = model.getValueAt(i, j);
        if (val instanceof Number) {
          percentages[i] += ((Number) val).doubleValue();
        } else if (val instanceof String) {
          // oops, it wasn"t numeric...
          // Ok, so try it as a string
          try {
            percentages[i] += Double.valueOf(val.toString())
                .doubleValue();
          } catch (Exception e) {
            // not a numeric string...give up.
          }
        }
      }
      runningTotal += percentages[i];
    }
    // Make each entry a percentage of the total.
    for (int i = model.getRowCount() - 1; i >= 0; i--) {
      percentages[i] /= runningTotal;
    }
  }
  // This method just takes the percentages and formats them for use as
  // tooltips.
  protected void createLabelsAndTips() {
    for (int i = model.getRowCount() - 1; i >= 0; i--) {
      labels[i] = (String) model.getValueAt(i, 0);
      tips[i] = formatter.format(percentages[i]);
    }
  }
  // Call this method to update the chart. We try to be a bit efficient here
  // in that we allocate new storage arrays only if the new table has a
  // different number of rows.
  protected void updateLocalValues(boolean freshStart) {
    if (freshStart) {
      int count = model.getRowCount();
      if ((tips == null) || (count != tips.length)) {
        percentages = new double[count];
        labels = new String[count];
        tips = new String[count];
      }
    }
    calculatePercentages();
    createLabelsAndTips();
    // Now that everything"s up-to-date, reset the chart painter with the
    // new
    // values.
    cp.setValues(percentages);
    cp.setLabels(labels);
    // Finally, repaint the chart.
    repaint();
  }
}
//PieChartPainter.java
//A pie chart implementation of the ChartPainter class.
//
class PieChartPainter extends ChartPainter {
  protected static PieChartPainter chartUI = new PieChartPainter();
  protected int originX, originY;
  protected int radius;
  private static double piby2 = Math.PI / 2.0;
  private static double twopi = Math.PI * 2.0;
  private static double d2r = Math.PI / 180.0; // Degrees to radians.
  private static int xGap = 5;
  private static int inset = 40;
  public int indexOfEntryAt(MouseEvent me) {
    int x = me.getX() - originX;
    int y = originY - me.getY(); // Upside-down coordinate system.
    // Is (x,y) in the circle?
    if (Math.sqrt(x * x + y * y) > radius) {
      return -1;
    }
    double percent = Math.atan2(Math.abs(y), Math.abs(x));
    if (x >= 0) {
      if (y <= 0) { // (IV)
        percent = (piby2 - percent) + 3 * piby2; // (IV)
      }
    } else {
      if (y >= 0) { // (II)
        percent = Math.PI - percent;
      } else { // (III)
        percent = Math.PI + percent;
      }
    }
    percent /= twopi;
    double t = 0.0;
    if (values != null) {
      for (int i = 0; i < values.length; i++) {
        if (t + values[i] > percent) {
          return i;
        }
        t += values[i];
      }
    }
    return -1;
  }
  public void paint(Graphics g, JComponent c) {
    Dimension size = c.getSize();
    originX = size.width / 2;
    originY = size.height / 2;
    int diameter = (originX < originY ? size.width - inset : size.height
        - inset);
    radius = (diameter / 2) + 1;
    int cornerX = (originX - (diameter / 2));
    int cornerY = (originY - (diameter / 2));
    int startAngle = 0;
    int arcAngle = 0;
    for (int i = 0; i < values.length; i++) {
      arcAngle = (int) (i < values.length - 1 ? Math
          .round(values[i] * 360) : 360 - startAngle);
      g.setColor(colors[i % colors.length]);
      g.fillArc(cornerX, cornerY, diameter, diameter, startAngle,
          arcAngle);
      drawLabel(g, labels[i], startAngle + (arcAngle / 2));
      startAngle += arcAngle;
    }
    g.setColor(Color.black);
    g.drawOval(cornerX, cornerY, diameter, diameter); // Cap the circle.
  }
  public void drawLabel(Graphics g, String text, double angle) {
    g.setFont(textFont);
    g.setColor(textColor);
    double radians = angle * d2r;
    int x = (int) ((radius + xGap) * Math.cos(radians));
    int y = (int) ((radius + xGap) * Math.sin(radians));
    if (x < 0) {
      x -= SwingUtilities.ruputeStringWidth(g.getFontMetrics(), text);
    }
    if (y < 0) {
      y -= g.getFontMetrics().getHeight();
    }
    g.drawString(text, x + originX, originY - y);
  }
  public static ComponentUI createUI(JComponent c) {
    return chartUI;
  }
}
//ChartPainter.java
//A simple chart-drawing UI base class. This class tracks the basic fonts
//and colors for various types of charts including pie and bar. The paint()
//method is abstract and must be implemented by subclasses for each type.
//
abstract class ChartPainter extends ComponentUI {
  protected Font textFont = new Font("Serif", Font.PLAIN, 12);
  protected Color textColor = Color.black;
  protected Color colors[] = new Color[] { Color.red, Color.blue,
      Color.yellow, Color.black, Color.green, Color.white, Color.gray,
      Color.cyan, Color.magenta, Color.darkGray };
  protected double values[] = new double[0];
  protected String labels[] = new String[0];
  public void setTextFont(Font f) {
    textFont = f;
  }
  public Font getTextFont() {
    return textFont;
  }
  public void setColor(Color[] clist) {
    colors = clist;
  }
  public Color[] getColor() {
    return colors;
  }
  public void setColor(int index, Color c) {
    colors[index] = c;
  }
  public Color getColor(int index) {
    return colors[index];
  }
  public void setTextColor(Color c) {
    textColor = c;
  }
  public Color getTextColor() {
    return textColor;
  }
  public void setLabels(String[] l) {
    labels = l;
  }
  public void setValues(double[] v) {
    values = v;
  }
  public abstract int indexOfEntryAt(MouseEvent me);
  public abstract void paint(Graphics g, JComponent c);
}