java swing graphics paintcomponent mousemotionlistener

Java Swing-Repaint() no funciona correctamente



graphics paintcomponent (1)

Tomo el programa de este tema . Intento editar puntos en modo de tiempo real. Agrego MouseMotionListener al constructor y escribo algunas funciones básicas para acercarme al punto sobre el mouse y edito este punto. Cuando obtengo (x, y) puntos en el constructor, repaint() funciona de manera extraña. Cuando obtengo (x, y) puntos en paintComponent , repaint() no funciona en absoluto. Entonces, estas son imágenes con get (x, y) en el constructor y en paintComponent . ¿Dónde está mi error?

package simplegrapher2; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import static java.lang.Math.sqrt; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class SimpleGrapher2 extends JPanel { private int width = 800; private int heigth = 400; private int padding = 25; private int labelPadding = 25; private Color lineColor = new Color(44, 102, 230, 180); private Color pointColor = new Color(100, 100, 100, 180); private Color gridColor = new Color(200, 200, 200, 200); private static final Stroke GRAPH_STROKE = new BasicStroke(2f); private int pointWidth = 4; private int numberYDivisions = 10; private List<Double> scores; private static double serieX = 0; public List<Point2D.Double> graphPoints; public SimpleGrapher2(List<Double> scores) { this.scores = scores; addMouseMotionListener(new MouseMotionListener() { @Override public void mouseDragged(MouseEvent me) { double x = me.getX(); double y = me.getY(); findNearPoint(x, y); editSerie(x, y); revalidate(); repaint(); } @Override public void mouseMoved(MouseEvent me) {} }); double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1); double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore()); graphPoints = new ArrayList<>(); for (int i = 0; i < scores.size(); i++) { double x1 = (double) (i * xScale + padding + labelPadding); double y1 = (double) ((getMaxScore() - scores.get(i)) * yScale + padding); graphPoints.add(new Point2D.Double(x1, y1)); } } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); /*double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1); double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore()); graphPoints = new ArrayList<>(); for (int i = 0; i < scores.size(); i++) { double x1 = (double) (i * xScale + padding + labelPadding); double y1 = (double) ((getMaxScore() - scores.get(i)) * yScale + padding); graphPoints.add(new Point2D.Double(x1, y1)); }*/ // draw white background g2.setColor(Color.WHITE); g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding, getHeight() - 2 * padding - labelPadding); g2.setColor(Color.BLACK); // create hatch marks and grid lines for y axis. for (int i = 0; i < numberYDivisions + 1; i++) { int x0 = padding + labelPadding; int x1 = pointWidth + padding + labelPadding; int y0 = getHeight() - ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding); int y1 = y0; if (scores.size() > 0) { g2.setColor(gridColor); g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y1); g2.setColor(Color.BLACK); String yLabel = ((int) ((getMinScore() + (getMaxScore() - getMinScore()) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0 + ""; FontMetrics metrics = g2.getFontMetrics(); int labelWidth = metrics.stringWidth(yLabel); g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3); } g2.drawLine(x0, y0, x1, y1); } // and for x axis for (int i = 0; i < scores.size(); i++) { if (scores.size() > 1) { int x0 = i * (getWidth() - padding * 2 - labelPadding) / (scores.size() - 1) + padding + labelPadding; int x1 = x0; int y0 = getHeight() - padding - labelPadding; int y1 = y0 - pointWidth; if ((i % ((int) ((scores.size() / 20.0)) + 1)) == 0) { g2.setColor(gridColor); g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x1, padding); g2.setColor(Color.BLACK); String xLabel = i + ""; FontMetrics metrics = g2.getFontMetrics(); int labelWidth = metrics.stringWidth(xLabel); g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3); } g2.drawLine(x0, y0, x1, y1); } } // create x and y axes g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding); g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding, getHeight() - padding - labelPadding); Stroke oldStroke = g2.getStroke(); g2.setColor(lineColor); g2.setStroke(GRAPH_STROKE); for (int i = 0; i < graphPoints.size() - 1; i++) { double x1 = graphPoints.get(i).x; double y1 = graphPoints.get(i).y; double x2 = graphPoints.get(i + 1).x; double y2 = graphPoints.get(i + 1).y; g2.draw(new Line2D.Double(x1, y1, x2, y2)); } g2.setStroke(oldStroke); g2.setColor(pointColor); for (int i = 0; i < graphPoints.size(); i++) { double x = graphPoints.get(i).x - pointWidth / 2; double y = graphPoints.get(i).y - pointWidth / 2; int ovalW = pointWidth; int ovalH = pointWidth; //g2.fillOval(x, y, ovalW, ovalH); } } public void findNearPoint(double x, double y) { double delta = 0; for (int j = 0; j < graphPoints.size(); j++) { double sx = graphPoints.get(j).x; double sy = graphPoints.get(j).y; double root = sqrt((x-sx)*(x-sx) + (y-sy)*(y-sy)); if (j == 0) { delta = root; serieX = sx; } else if (root < delta) { serieX = sx; delta = root; } } } public void editSerie(double x, double y) { double tmpSerieX = serieX; int ind = 0; for (int i = 0; i < graphPoints.size(); i++) { if (graphPoints.get(i).x == tmpSerieX) { ind = i; } } graphPoints.remove(ind); graphPoints.add(ind, new Point2D.Double(x, y)); serieX = x; } private double getMinScore() { double minScore = Double.MAX_VALUE; for (Double score : scores) { minScore = Math.min(minScore, score); } return minScore; } private double getMaxScore() { double maxScore = Double.MIN_VALUE; for (Double score : scores) { maxScore = Math.max(maxScore, score); } return maxScore; } public void setScores(List<Double> scores) { this.scores = scores; invalidate(); this.repaint(); } public List<Double> getScores() { return scores; } private static void createAndShowGui() { List<Double> scores = new ArrayList<>(); Random random = new Random(); int maxDataPoints = 40; int maxScore = 10; for (int i = 0; i < maxDataPoints; i++) { scores.add((double) random.nextDouble() * maxScore); } SimpleGrapher2 mainPanel = new SimpleGrapher2(scores); mainPanel.setPreferredSize(new Dimension(800, 600)); JFrame frame = new JFrame("DrawGraph"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(mainPanel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } }


El problema está en tu constructor cuando calculas x/yScale ...

public SimpleGrapher2(List<Double> scores) { //... double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1); double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore()); //... }

En el punto en que se construye el componente, el tamaño de los componentes es 0x0 . Debe esperar hasta que el componente cambie de tamaño de alguna manera significativa antes de calcular los puntos ...

Esto también es problemático, ya que el componente puede redimensionarse varias veces, en rápida sucesión, cuando se realiza por primera vez en la pantalla.

Lo que hice en el pasado es usar un Swing Timer configurado para un pequeño retardo para permitir que el componente cambie de tamaño varias veces en una escala de tiempo pequeña. Una vez que todo esté establecido, se disparará el Timer y podrá realizar las operaciones requeridas, por ejemplo ...

public SimpleGrapher2(List<Double> scores) { //... addComponentListener(new ComponentAdapter() { private Timer initTimer = new Timer(200, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { initTimer.stop(); double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1); double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore()); graphPoints = new ArrayList<>(); for (int i = 0; i < scores.size(); i++) { double x1 = (double) (i * xScale + padding + labelPadding); double y1 = (double) ((getMaxScore() - scores.get(i)) * yScale + padding); graphPoints.add(new Point2D.Double(x1, y1)); } repaint(); } }); @Override public void componentResized(ComponentEvent e) { if (graphPoints == null) { initTimer.restart(); } } }); }

Todo lo que hace es esperar hasta que el temporizador entre el evento compoentResized sea ​​más de 200 milisegundos (puede jugar con este valor para acercarlo más a lo que desea, pero he encontrado aproximadamente 250 milisegundos en un retraso razonable)

Deberá corregir paintComponent para permitir que graphPoints null .