今週の土曜日に予定されている学校のプロジェクトに取り組んでいるときに、これまでに遭遇した中で最も厄介なバグに遭遇しました。World Wide Web のどこにも解決策が見つからないようです。私の古き良き友人である Google でさえ、私をがっかりさせているようです。それで、ここに質問があります:
私は JTabbedPane で作業しています。この JTabbedPane では、さまざまな JPane を表示し、それぞれの情報を示します。JPanes の 1 つで、棒グラフを描画しています。
棒グラフを個別にテストすると、すべて正常に動作します。JTabbedPane の右側のタブで自分の情報をロードするときにも同様です。ただし、このタブに戻るとすぐに、ペイント中に棒グラフが上に移動し、すべてが歪んで見えます (写真を参照)。
私は常に正しい座標で描画していることを知っているので、JTabbedPane のタブの代わりに JPanel を使用する必要があります。この奇妙な動作の原因について何か考えはありますか?
右:あるべき姿。左: このタブに戻った後の外観。
私の BarGraphPanel クラスは本当に混乱しています。コードは次のとおりです。
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package gui;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.text.DecimalFormat;
import java.util.ArrayList;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
*
* @author Scuba Kay
*/
public class BarGraphPanel extends JPanel implements ManagementPanel {
private ArrayList<String[]> info;
private String infoName;
private double maxValue;
private int size;
private int barWidth;
private double left;
private double right;
private double width;
private double height;
private double bottom;
private double top;
private int barSpacing;
private double barPart;
private double graphHeight;
private double graphWidth;
private int cap;
public BarGraphPanel(){
super();
width = 600;
height = 480;
setSize((int) width, (int) height);
// The maximum number of results to show
cap = 30;
this.infoName = "Management information";
this.add(new JLabel("This query is not suited for Bar Graph view: please select another one."));
}
/**
* Sets both x and y, min and max and the info list
* @author Kay van Bree
* @param info
*/
@Override
public void setManagementInfo(ArrayList<String[]> info) {
reset();
this.info = info;
// Set important values
this.maxValue = getMaxValue(info);
this.size = info.size();
setSizes();
repaint();
}
/**
* Resets the panel, taken from TablePanel
* @author Joepe Kemperman
*/
public void reset() {
removeAll();
invalidate();
validate();
}
/**
* Set the sizes needed for calculating bar heights etc.
* @author Kay van Bree
*/
protected void setSizes(){
left = (width / 100) * 7;
right = width - left*3;
top = (height / 100) * 7;
bottom = height - top*3;
// The dimensions of the graph
graphHeight = bottom - top;
graphWidth = right - left;
// barWidth is... Just guess what it is.
barWidth = (int) ((double) (graphWidth / size) * 0.95);
// barSpacing is the space between the bars
barSpacing = (int) (graphWidth - (barWidth*size)) / size;
if(barSpacing == 0){
barSpacing++;
barWidth--;
}
// barPart is the height of a bar of value 1
barPart = graphHeight / maxValue;
}
/**
* Return the max value of the info arraylist
* @author Kay van Bree
* @param info
*/
private int getMaxValue(ArrayList<String[]> info){
int max = 0;
for(String[] row: info){
if(Integer.valueOf(row[1]) > max){
max = Integer.valueOf(row[1]);
}
}
return max;
}
/**
* Draw the damn thing!
* @author Kay van Bree
* @param g
*/
@Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
if(info != null){
g.setColor(Color.BLACK);
drawTitle(g);
drawBlocks(g);
if(size <= cap){
drawBars(g);
} else {
drawError(g);
}
drawAxes(g);
} else {
this.setInformationNotSuitable();
}
}
/**
* Set an error displaying to many results
* @author Kay van Bree
* @param g
*/
private void drawError(Graphics g){
g.setColor(Color.BLACK);
g.drawString("Too many results to show!", (int)(right-left*2)/2, (int)(bottom-top*2)/2);
}
/**
* Draw the names under the bars
* @author Kay van Bree
* @param g
*/
private void drawName(Graphics g, String name, int x){
Graphics2D g2d = (Graphics2D)g;
// clockwise 90 degrees
AffineTransform at = new AffineTransform();
// thanks to M.C. Henle for the bug fix!
at.setToRotation(Math.PI/2.0, x + (barWidth/4), bottom + 3);
g2d.setTransform(at);
g2d.setColor(Color.BLACK);
g2d.drawString(name, (int) (x + (barWidth/4)), (int) bottom + 3);
g2d.setTransform(new AffineTransform());
}
/**
* Draw the lines behind the bars
* @author Kay van Bree
* @param g
*/
private void drawBlocks(Graphics g){
g.setColor(Color.lightGray);
// Ten parts
double part = maxValue / 10;
double total = maxValue;
// Draw the numbers
for(int i=0; i<10; i++){
double y = (((bottom-top)/10)*i)+top;
g.drawLine(((int) left)-5, (int) y, ((int) right), (int) y);
}
g.drawLine((int) right, (int) top, (int) right, (int) bottom);
}
/**
* Draw the title of the Graph
* @author Kay van Bree
* @param g
*/
private void drawTitle(Graphics g){
int tLeft = 100;
g.drawString(infoName, tLeft, (int) top / 2);
}
/**
* Draw the axes of this Bar Graph
* @author Kay van Bree
* @param g
*/
private void drawAxes(Graphics g){
g.setColor(Color.BLACK);
// draw lines from (x, y) to (x, y)
g.drawLine((int) left, (int) top, (int) left, (int) bottom);
g.drawLine((int) left, (int) bottom, (int) right, (int) bottom);
// Ten parts
double part = maxValue / 10;
double total = maxValue;
// Draw the numbers
for(int i=0; i<10; i++){
double y = (((bottom-top)/10)*i)+top;
String number = round(total);
FontMetrics fontMetrics = g.getFontMetrics();
g.drawString(number, (int) (left * 0.80) - fontMetrics.stringWidth(number), ((int) y)+5);
total = (total - part);
g.drawLine(((int) left)-5 , (int) y, ((int) left), (int) y);
}
}
/**
* Rounds the number to 1 decimal
* @author Kay van Bree
* @param number
* @return
*/
private String round(double number){
DecimalFormat df = new DecimalFormat("#.#");
Double dubbel = new Double(number);
return df.format(dubbel);
}
/**
* Round a number, make it int
* @author Kay van Bree
* @param number
* @return
*/
private int roundToInt(double number){
DecimalFormat df = new DecimalFormat("#");
Double dubbel = new Double(number);
return Integer.valueOf(df.format(dubbel));
}
/**
* We need info right?
* Then draw the damn bars already!
* @author Kay van Bree
* @param g
*/
private void drawBars(Graphics g){
double currentX = left + barSpacing + 1;
for(String[] row: info){
double value = Integer.valueOf(row[1]);
double barHeight = (value * barPart);
System.out.println("Value: " + value + " height: " + barHeight + " top: " + top);
double barStart = bottom - barHeight;
System.out.println("Barstart: " + barStart);
barHeight = ((int) bottom - (int) barStart);
g.setColor(Color.BLUE);
g.fillRect((int) currentX, (int) barStart, (int) barWidth, roundToInt(barHeight));
drawName(g, row[0], (int) currentX);
currentX = (currentX + barSpacing + barWidth);
}
}
public void setInformationNotSuitable() {
// JOptionPane.showMessageDialog(this, "This query is not suited for Bar Graph view. Please select another", "INSANE WARNING!", JOptionPane.WARNING_MESSAGE);
}
}
役立つ情報:
setManagementInformation は、JTabbedPane によって 1 回呼び出されます。ここですべてのコンテンツをロードします (他のタブも)
EDIT Okeなので、Xeonの答えは私の問題をほぼ解決しました。このタブに戻るたびに、グラフのバーが完全に整列するようになりました。ただし、元に戻すと、バーの下の名前はまだシフトしています。更新されたコードは次のとおりです。
/**
* Draw the names under the bars
* @author Kay van Bree
* @param g
*/
private void drawName(Graphics g, String name, int x){
Graphics2D g2d = (Graphics2D) g.create();
// Clockwise 90 degrees
AffineTransform at = new AffineTransform();
// Rotate the text
at.setToRotation(Math.PI/2.0, x + (barWidth/4), bottom + 3);
AffineTransform prev = g2d.getTransform();
g2d.setTransform(at);
g2d.setColor(Color.BLACK);
g2d.drawString(name, (int) (x + (barWidth/4)), (int) bottom + 3);
g2d.dispose();
}
それで、これもAffineTransformと関係がありますか?この問題の解決策はありますか?