Dessiner un graphique linéaire simple en Java


Dans mon programme, je veux dessiner un graphique linéaire de score simple. J'ai un fichier texte et sur chaque ligne se trouve un score entier, que j'ai lu et que je veux passer en argument à ma classe de graphe. J'ai du mal à implémenter la classe graph et tous les exemples que j'ai vus ont leurs méthodes dans la même classe que leur principale, ce que je n'aurai pas.

Je veux pouvoir passer mon tableau à l'objet et générer un graphique, mais lors de l'appel de ma méthode paint, il me demande un g graphique... Ce est ce que j'ai jusqu'à présent:

public class Graph extends JPanel {

    public void paintGraph (Graphics g){

        ArrayList<Integer> scores = new ArrayList<Integer>(10);

        Random r = new Random();

        for (int i : scores){
            i = r.nextInt(20);
            System.out.println(r);
        }

        int y1;
        int y2;

        for (int i = 0; i < scores.size(); i++){
            y1 = scores.get(i);
            y2 = scores.get(i+1);
            g.drawLine(i, y1, i+1, y2);
        }
    }
}

Pour l'instant, j'ai inséré un simple générateur de nombre aléatoire pour remplir mon tableau.

J'ai un cadre existant et je veux essentiellement instancier la classe Graph et monter le panneau sur mon cadre. Je suis vraiment désolé que cette question semble si brouillée au fait, mais j'ai peu dormi...

Le code dans mon instruction principale est:

testFrame = new JFrame();
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Graph graph = new Graph();
testFrame.add(graph);

Je ne sais pas exactement ce qu'est un SSCE mais c'est ma tentative de un:

public class Test {

    JFrame testFrame;
    public Test() {
        testFrame = new JFrame();
        testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Graph graph = new Graph();
        testFrame.add(graph);
        testFrame.setBounds(100, 100, 764, 470);
        testFrame.setVisible(true);
    }

Graphique.java

public class Graph extends JPanel {
    public Graph() {
       setSize(500, 500);
    }

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D gr = (Graphics2D) g; // This is if you want to use Graphics2D
        // Now do the drawing here
        ArrayList<Integer> scores = new ArrayList<Integer>(10);

        Random r = new Random();

        for (int i : scores) {
            i = r.nextInt(20);
            System.out.println(r);
        }

        int y1;
        int y2;

        for (int i = 0; i < scores.size() - 1; i++) {
            y1 = (scores.get(i)) * 10;
            y2 = (scores.get(i + 1)) * 10;
            gr.drawLine(i * 10, y1, (i + 1) * 10, y2);
        }
    }
}
Author: Peter Mortensen, 2012-01-01

6 answers

Problèmes avec votre code et suggestions:

  • Encore une fois, vous devez changer la taille préférée du composant (ici le graphique JPanel), pas la taille
  • Ne définissez pas les limites du JFrame.
  • Appelez pack() sur votre JFrame après y avoir ajouté des composants et avant d'appeler setVisible(true)
  • Votre boucle foreach ne fonctionnera pas car la taille de votre ArrayList est 0 (testez-la pour voir que c'est correct). Utilisez plutôt une boucle for allant de 0 à 10.
  • Vous ne devriez pas avoir logique de programme à l'intérieur de votre méthode paintComponent(...) mais uniquement du code de peinture. Je ferais donc de la ArrayList une variable de classe et la remplirais à l'intérieur du constructeur de la classe.

Par exemple:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;

@SuppressWarnings("serial")
public class DrawGraph extends JPanel {
   private static final int MAX_SCORE = 20;
   private static final int PREF_W = 800;
   private static final int PREF_H = 650;
   private static final int BORDER_GAP = 30;
   private static final Color GRAPH_COLOR = Color.green;
   private static final Color GRAPH_POINT_COLOR = new Color(150, 50, 50, 180);
   private static final Stroke GRAPH_STROKE = new BasicStroke(3f);
   private static final int GRAPH_POINT_WIDTH = 12;
   private static final int Y_HATCH_CNT = 10;
   private List<Integer> scores;

   public DrawGraph(List<Integer> scores) {
      this.scores = scores;
   }

   @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 * BORDER_GAP) / (scores.size() - 1);
      double yScale = ((double) getHeight() - 2 * BORDER_GAP) / (MAX_SCORE - 1);

      List<Point> graphPoints = new ArrayList<Point>();
      for (int i = 0; i < scores.size(); i++) {
         int x1 = (int) (i * xScale + BORDER_GAP);
         int y1 = (int) ((MAX_SCORE - scores.get(i)) * yScale + BORDER_GAP);
         graphPoints.add(new Point(x1, y1));
      }

      // create x and y axes 
      g2.drawLine(BORDER_GAP, getHeight() - BORDER_GAP, BORDER_GAP, BORDER_GAP);
      g2.drawLine(BORDER_GAP, getHeight() - BORDER_GAP, getWidth() - BORDER_GAP, getHeight() - BORDER_GAP);

      // create hatch marks for y axis. 
      for (int i = 0; i < Y_HATCH_CNT; i++) {
         int x0 = BORDER_GAP;
         int x1 = GRAPH_POINT_WIDTH + BORDER_GAP;
         int y0 = getHeight() - (((i + 1) * (getHeight() - BORDER_GAP * 2)) / Y_HATCH_CNT + BORDER_GAP);
         int y1 = y0;
         g2.drawLine(x0, y0, x1, y1);
      }

      // and for x axis
      for (int i = 0; i < scores.size() - 1; i++) {
         int x0 = (i + 1) * (getWidth() - BORDER_GAP * 2) / (scores.size() - 1) + BORDER_GAP;
         int x1 = x0;
         int y0 = getHeight() - BORDER_GAP;
         int y1 = y0 - GRAPH_POINT_WIDTH;
         g2.drawLine(x0, y0, x1, y1);
      }

      Stroke oldStroke = g2.getStroke();
      g2.setColor(GRAPH_COLOR);
      g2.setStroke(GRAPH_STROKE);
      for (int i = 0; i < graphPoints.size() - 1; i++) {
         int x1 = graphPoints.get(i).x;
         int y1 = graphPoints.get(i).y;
         int x2 = graphPoints.get(i + 1).x;
         int y2 = graphPoints.get(i + 1).y;
         g2.drawLine(x1, y1, x2, y2);         
      }

      g2.setStroke(oldStroke);      
      g2.setColor(GRAPH_POINT_COLOR);
      for (int i = 0; i < graphPoints.size(); i++) {
         int x = graphPoints.get(i).x - GRAPH_POINT_WIDTH / 2;
         int y = graphPoints.get(i).y - GRAPH_POINT_WIDTH / 2;;
         int ovalW = GRAPH_POINT_WIDTH;
         int ovalH = GRAPH_POINT_WIDTH;
         g2.fillOval(x, y, ovalW, ovalH);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   private static void createAndShowGui() {
      List<Integer> scores = new ArrayList<Integer>();
      Random random = new Random();
      int maxDataPoints = 16;
      int maxScore = 20;
      for (int i = 0; i < maxDataPoints ; i++) {
         scores.add(random.nextInt(maxScore));
      }
      DrawGraph mainPanel = new DrawGraph(scores);

      JFrame frame = new JFrame("DrawGraph");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Qui va créer un graphique qui ressemble à ceci: entrez la description de l'image ici

 49
Author: Hovercraft Full Of Eels, 2012-08-24 13:40:27

Juste en complément de la solution de L'Aéroglisseur Plein D'Anguilles:

J'ai retravaillé son code, l'ai un peu modifié, en ajoutant une grille, des étiquettes d'axe et maintenant l'axe Y va de la valeur minimale présente à la valeur maximale. J'avais prévu d'ajouter quelques getters / setters mais je n'en avais pas besoin, vous pouvez les ajouter si vous le souhaitez.

Voici le lien Gist, je vais aussi coller le code ci-dessous: GraphPanel on Gist

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.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
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 GraphPanel 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;

    public GraphPanel(List<Double> scores) {
        this.scores = scores;
    }

    @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());

        List<Point> graphPoints = new ArrayList<>();
        for (int i = 0; i < scores.size(); i++) {
            int x1 = (int) (i * xScale + padding + labelPadding);
            int y1 = (int) ((getMaxScore() - scores.get(i)) * yScale + padding);
            graphPoints.add(new Point(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++) {
            int x1 = graphPoints.get(i).x;
            int y1 = graphPoints.get(i).y;
            int x2 = graphPoints.get(i + 1).x;
            int y2 = graphPoints.get(i + 1).y;
            g2.drawLine(x1, y1, x2, y2);
        }

        g2.setStroke(oldStroke);
        g2.setColor(pointColor);
        for (int i = 0; i < graphPoints.size(); i++) {
            int x = graphPoints.get(i).x - pointWidth / 2;
            int y = graphPoints.get(i).y - pointWidth / 2;
            int ovalW = pointWidth;
            int ovalH = pointWidth;
            g2.fillOval(x, y, ovalW, ovalH);
        }
    }

//    @Override
//    public Dimension getPreferredSize() {
//        return new Dimension(width, heigth);
//    }
    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);
//            scores.add((double) i);
        }
        GraphPanel mainPanel = new GraphPanel(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();
         }
      });
   }
}

Il ressemble à ceci: Exemple pic

 35
Author: Rodrigo Castro, 2013-08-24 00:15:02

Ou utilisez simplement la bibliothèque JFreechart - http://www.jfree.org/jfreechart / .

 9
Author: DejanLekic, 2012-01-01 14:54:10

Il existe de nombreux projets open source qui gèrent tout le dessin de graphiques linéaires pour vous avec quelques lignes de code. Voici comment vous pouvez dessiner un graphique linéaire à partir de données dans un fichier couple text (CSV) avec la bibliothèque XChart. Avertissement: je suis le développeur principal du projet.

Dans cet exemple, deux fichiers texte existent dans ./CSV/CSVChartRows/. Notez que chaque ligne dans les fichiers représente un point de données à tracer et que chaque fichier représente une série différente. series1 contient x, y, et error bar les données, tandis que series2 contient x et y, données.

Série1.csv

1,12,1.4
2,34,1.12
3,56,1.21
4,47,1.5

Série2.csv

1,56
2,34
3,12
4,26

Code source

public class CSVChartRows {

  public static void main(String[] args) throws Exception {

    // import chart from a folder containing CSV files
    XYChart chart = CSVImporter.getChartFromCSVDir("./CSV/CSVChartRows/", DataOrientation.Rows, 600, 400);

    // Show it
    new SwingWrapper(chart).displayChart();
  }
}

Tracé résultant

entrez la description de l'image ici

 6
Author: herrtim, 2016-06-14 07:22:50

Remplacez la méthode paintComponent de votre panneau afin que vous puissiez dessiner sur mesure. Comme ceci:

@Override
public void paintComponent(Graphics g) {
    Graphics2D gr = (Graphics2D) g; //this is if you want to use Graphics2D
    //now do the drawing here
    ...
}
 3
Author: Petar Minchev, 2012-01-01 13:56:40

Aéroglisseur Plein D'Anguilles réponse est très bonne, mais j'ai dû changer un peu pour le faire fonctionner sur mon programme:

int y1 = (int) ((this.height - 2 * BORDER_GAP) - (values.get(i) * yScale - BORDER_GAP));

Au Lieu de

int y1 = (int) (scores.get(i) * yScale + BORDER_GAP);

Parce que si j'utilisais sa façon, le graphique serait à l'envers

(vous le verriez si vous utilisiez des valeurs codées en dur (par exemple 1,3,5,7,9) au lieu de valeurs aléatoires)

 2
Author: DarkW, 2012-08-13 04:34:00