简体   繁体   English

图形未出现在Java Jframe Canvas中

[英]Graphics not appearing in Java Jframe Canvas

I am trying to draw a vine within a Jframe however the graphics are not appearing when I run the program. 我正在尝试在Jframe中绘制藤蔓,但是在运行程序时图形没有出现。 The idea is that a "Vine" is randomly generated and can randomly branch out into other "Vines". 这个想法是“藤”是随机生成的,可以随机分支到其他“藤”中。 If anyone can help that would be much appreciated. 如果有人可以提供帮助,将不胜感激。

import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;

    public class Vine extends JPanel {
        public void paint(Graphics g, int a, int b, int c)
          {
            super.paint(g);
            g.setColor(Color.BLACK);
            g.drawLine(10, 10, 100, 100);
            grow(g, a, b, c);
          }
        public Boolean TF(){
            Boolean A;
            int B = ((int)Math.random()+1);
            if (B==1){
                A = true;
            } else {
                A = false;
            }
            return A;
        }
        public void grow(Graphics g, int a, int b, int c){
            int x = a;
            int y = b;
            int age = c;
            for (int i=0; i<= age; i++) {
                if (TF()) {
                    if (TF()) {
                        grow(g, x, y, ((age-i)/2));
                    }
                }
                if (TF()){
                    if (TF()){
                        g.drawLine(x, y, (x+1), y);
                        x++;
                    } else {
                        g.drawLine(x, y, (x-1), y);
                        x++;
                    }
                } else {
                    g.drawLine(x, y, x, (y+1));
                    y++;
                }
            }
        }
        public static void main(String[] args)
          {
            JFrame f = new JFrame("Vine");
            f.setBounds(300, 300, 200, 120);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            Vine panel = new Vine();
            Container c = f.getContentPane();
            panel.setBackground(Color.WHITE);
            c.add(panel);
            f.setResizable(true);
            f.setVisible(true);
          }
    }

There are several issues with this code: 此代码存在多个问题:

  • Nobody is calling the paint(Graphics g, int a, int b, int c) method. 没有人在调用paint(Graphics g, int a, int b, int c)方法。 When you inherit from a Swing component like this, there are several methods that are invoked "automatically" (among them a paint(Graphics g) method). 当您从这样的Swing组件继承时,有几种方法是“自动”调用的(其中包括paint(Graphics g)方法)。 But in order to perform custom painting, you should usually override the paintComponent(Graphics g) method . 但是,为了执行自定义绘画,通常应该重写paintComponent(Graphics g)方法

  • You are generating random numbers while you are painting. 您在绘画正在生成随机数。 This will have some odd effects. 这将产生一些奇怪的影响。 The most obvious one: When you resize the frame (or something else happens that causes the frame to be repainted), a new random vine will appear. 最明显的一个:调整框架大小(或发生其他导致框架重新粉刷的情况)时,将出现一个新的随机藤蔓。 It will have nothing in common with the previous one, causing a flickering mess, in the best case. 在最佳情况下,它与上一个没有任何共同之处,从而导致闪烁的混乱。

  • In order to create reporoducible results that are still "random", I generally recommend to not use Math.random() . 为了创建仍可“随机”进行的可重复生成的结果,我通常建议不要使用Math.random() (In fact, I never use this, at all). (实际上,我从来没有使用过)。 The java.util.Random class is usually preferable. 通常最好使用java.util.Random类。 It allows creating reproducible random number sequences (which is helpful for debugging), and it offers convenient methods like Random#nextBoolean() , which has exactly the effect that you probably wanted to achieve with the (somewhat oddly implemented) TF() method. 它允许创建可重现的随机数序列(这对调试很有帮助),并且提供了诸如Random#nextBoolean()类的便捷方法,它具有您可能想通过TF()方法实现的效果。 This leads to the next point...: 这引出了下一点...:

  • Use better variable- and method names. 使用更好的变量名和方法名。 Naming variables like the c in your example, or methods TF() , will make the code unreadable. 如示例中的c类的变量或方法TF()命名将使代码不可读。 You may call them x and y if they refer to screen coordinates, but the c should probably be called age , and the TF() method ... heck, I don't know how this should be called, maybe something like shouldBranch() ? 如果它们引用屏幕坐标,则可以将它们称为xy ,但是c可能应该称为age ,并且应该使用TF()方法...哎呀,我不知道应该如何调用它,也许应该是诸如shouldBranch()

  • If you have to perform "extensive" computations (like the random branching, in your example), it is usually better to pull this computation out of the painting process. 如果您必须执行“广泛的”计算(例如您的示例中的随机分支),通常最好将此计算从绘画过程中剔除。 You can assemble the desired lines and paintings in various ways. 您可以通过各种方式组合所需的线条和绘画。 For the given example, a simple Path2D should be sufficient. 对于给定的示例,简单的Path2D应该足够了。


So far, the technical things. 到目前为止,技术方面的事情。 Apart from that: The algorithm itself will not lead to "nice" results. 除此之外:算法本身不会导致“不错”的结果。 The random branching for each pixel will cause the lines to clob together to a black, fuzzy spot. 每个像素的随机分支将导致这些线合并在一起成为一个黑色的模糊斑点。

In fact, it can be pretty hard to tweak this to create "nice" results. 实际上,很难调整它以创建“不错”的结果。 It is hard to exactly say how the "randomness" should influence the overall appearance. 很难确切地说出“随机性”应如何影响整体外观。 The branching should be random, the angles should be random, the branch lengths should be random. 分支应该是随机的,角度应该是随机的,分支长度应该是随机的。 Additionally, it will always look a bit odd when all the lines are drawn with the same thickness. 此外,当所有线条以相同的粗细绘制时,它总是看起来有些奇怪。 The thickness of the lines should probably decrease at each branch, and along the branches in general. 线的粗细可能应该在每个分支处以及通常沿着分支减小。

In some practical applications, this generation of random plant-like structures is done with Lindenmayer systems - this may be a starting point for further research. 在某些实际应用中,这种随机植物状结构的生成是通过Lindenmayer系统完成的-这可能是进一步研究的起点。

However, here is a very simple example of how simple lines can be assembled, somewhat randomly, to create something that resembles a plant: 但是,这是一个非常简单的示例,说明如何将一些简单的线以某种随机的方式组合在一起,以创建类似于植物的东西:

藤蔓

Of course, this looks like cr*p compared to what one could do, given enough time and incentive. 当然,如果有足够的时间和动力,与人可以做的事情相比,这看起来像cr * p。 But it consists only of a few lines of code that randomly assemble a few branching lines, so this is as good as it gets without considering all the possible improvements. 但是它仅由几行代码组成,这些代码随机地组合了几条分支行,因此这在没有考虑所有可能的改进的情况下就已经足够了。

import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class VinePainting
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }

            private void createAndShowGUI()
            {
                JFrame f = new JFrame("Vine");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                VinePanel panel = new VinePanel();
                Container c = f.getContentPane();
                panel.setBackground(Color.WHITE);
                c.add(panel);
                f.setSize(500, 500);
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            }
        });
    }
}


class VinePanel extends JPanel
{
    private static final Random RANDOM = new Random(0);
    private Path2D vine;

    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        if (vine == null)
        {
            vine = createVine(getWidth()/2, getHeight());
        }
        g.setColor(Color.BLACK);
        g.draw(vine);
    }

    private Path2D createVine(int x, int y)
    {
        Path2D path = new Path2D.Double();
        double angleRad = Math.toRadians(-90);
        grow(path, x, y, angleRad, 10.0, 0, 30);
        return path;
    }

    private static void grow(Path2D path, 
        double x, double y, double angleRad, 
        double stepSize, int step, int steps)
    {
        if (step == steps)
        {
            return;
        }
        path.moveTo(x, y);

        double dirX = Math.cos(angleRad);
        double dirY = Math.sin(angleRad);
        double distance = random(stepSize, stepSize + stepSize);
        double newX = x + dirX * distance;
        double newY = y + dirY * distance;
        path.lineTo(newX, newY);

        final double angleRadDeltaMin = -Math.PI * 0.2;
        final double angleRadDeltaMax =  Math.PI * 0.2;
        double progress = (double)step / steps;
        double branchProbability = 0.3;
        double branchValue = RANDOM.nextDouble();
        if (branchValue + 0.1 < branchProbability)
        {
            double angleRadDelta = random(angleRadDeltaMin, angleRadDeltaMax);
            double newAngleRad = angleRad + angleRadDelta;
            double newStepSize = (1.0 - progress * 0.1) * stepSize;
            grow(path, newX, newY, newAngleRad, newStepSize, step+1, steps);
        }
        double angleRadDelta = random(angleRadDeltaMin, angleRadDeltaMax);
        double newAngleRad = angleRad + angleRadDelta;
        double newStepSize = (1.0 - progress * 0.1) * stepSize;
        grow(path, newX, newY, newAngleRad, newStepSize, step+1, steps);
    }

    private static double random(double min, double max)
    {
        return min + RANDOM.nextDouble() * (max - min);
    }


}

A side note: This is somewhat similiar to this question . 旁注:这有点类似于这个问题 But there, randomness did not play a role, so the recursion is done while painting the tree. 但是在那里, 随机性并不起作用,因此递归是绘制树时完成的。 (Therefore, it allows playing around with some sliders, to modify the parameters and observe the effects). (因此,它允许使用一些滑块来修改参数并观察效果)。

There are lots of implementation issues here. 这里有很多实施问题。 You can override the paintComponent of the JPanel to paint in it. 您可以覆盖JPanel的paintComponent进行绘制。 Here is a quick fix to demonstrate how this is done. 这是演示如何完成此操作的快速修复。 I changed your implementation a lot to fix the issues so you can get an idea. 我已对您的实现进行了很多更改,以解决问题,以便您有所了解。

Note: This was a quick fix. 注意:这是一个快速修复。 So the code quality is low and some OOP concepts were ignored. 因此,代码质量很低,并且一些OOP概念被忽略了。 Just go through this and understand how this work and implement your own code. 只需仔细阅读并了解其工作原理并实现自己的代码即可。

Explanation: 说明:

You have to call repaint method of JPanel class to make it repaint itself. 您必须调用JPanel类的repaint方法以使其自身重新绘制。 It will paint all Lines in the LinkedList to the panel too(refer the implementation). 它将LinkedList中的所有Lines也绘制到面板上(请参阅实现)。 After repainting, create a new random line and add it to the LinkedList. 重新绘制后,创建一个新的随机行并将其添加到LinkedList。 It will be painted next time. 它将在下次绘制。

Then we have to animate this. 然后,我们必须对此进行动画处理。 So I implemented runnable interface in the Vine class. 因此,我在Vine类中实现了runnable接口。 run method will be called when we add this vine object(panel) to a Thread and start() the thread. 当我们将此藤对象(面板)添加到Threadstart() Thread时,将调用run方法。 We need to run it forever. 我们需要永远运行它。 So add a loop in the run method. 因此,在run方法中添加一个循环。 Then repaint the panel every time run method is called. 然后在每次调用run方法时repaint panel This animation is too fast, so add a Thread.sleep() to slow down the animation. 这个动画太快了,所以添加一个Thread.sleep()来减慢动画的速度。

import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.util.LinkedList;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Line {//Line class to store details of lines

    int x1, y1, x2, y2;

    public Line(int x1, int y1, int x2, int y2) {
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;
    }
}

class Vine extends JPanel implements Runnable {//implements runnable to animate it

LinkedList<Line> lines = new LinkedList<>();//LinkedList to store lines

int x = 10;
int y = 10;
Line line;

public Boolean TF() {
    Boolean A;
    int B = (int) (Math.random() * 2 + 1);//logical error fixed
    if (B == 1) {
        A = true;
    } else {
        A = false;
    }
    return A;
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    g.setColor(Color.BLACK);

    for (Line line : lines) {//paint all lines in the LinkedList
        g.drawLine(line.x1, line.y1, line.x2, line.y2);
    }

    //Create and add a next line
    if (TF()) {
        if (TF()) {
            line = new Line(x, y, (x + 1), y);
            lines.add(line);
            x++;
        } else {
            line = new Line(x, y, (x - 1), y);
            lines.add(line);
            x++;
        }
    } else {
        line = new Line(x, y, x, (y + 1));
        lines.add(line);
        y++;
    }
}

private static Vine panel;

public static void main(String[] args) {
    JFrame f = new JFrame("Vine");
    f.setSize(300, 300);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    panel = new Vine();
    Container c = f.getContentPane();
    panel.setBackground(Color.WHITE);
    c.add(panel);
    f.setResizable(true);
    f.setVisible(true);
    panel.start();//start the animation(thread)
}

private void start() {
    Thread thread = new Thread(this);
    thread.start();
}

@Override
public void run() {
    while (true) {

        try {
            Thread.sleep(100);//slow down the animation
            panel.repaint();//then repaint the panel
        } catch (InterruptedException ex) {

        }
    }
}
}

Here's a GUI I created using your code. 这是我使用您的代码创建的GUI。 I didn't understand how your grow method worked, so I pretty much left it alone. 我不了解您的成长方法是如何工作的,所以我几乎不理会它。

藤蔓

Here's what I did. 这就是我所做的。

  1. Created a GrowVine method to hold the line segments to draw. 创建了一个GrowVine方法来保存要绘制的线段。 I also created a LineSegment class to hold one line segment. 我还创建了LineSegment类来容纳一个线段。 I did this so that I could keep the model data separate from the view. 我这样做是为了使模型数据与视图分开。 By separating concerns, I could focus on one part of the problem at a time. 通过分离关注点,我可以一次专注于问题的一部分。

  2. I started the Swing application with a call to the SwingUtilities invoke later method. 我通过调用SwingUtilities稍后调用方法来启动Swing应用程序。 This ensures that the Swing components are created and used on the Event Dispatch thread . 这样可以确保在事件分发线程上创建并使用Swing组件。

  3. I created a DrawingPanel from a JPanel, and overrode the paintComponent method. 我从JPanel创建了DrawingPanel,并覆盖了paintComponent方法。 Notice that my override code does nothing but draw. 请注意,我的覆盖代码只做绘制。 All of the calculations are done in the GrowVine class. 所有计算都在GrowVine类中完成。

  4. I greatly simplified your TF method and renamed it to coinFlip. 我极大地简化了您的TF方法,并将其重命名为coinFlip。 coinFlip better indicates to future readers of the code (including yourself) that the boolean should be true half the time and false half the time. coinFlip可以更好地向代码的未来读者(包括您自己)表明,布尔值的一半时间应该为true,一半时间为false。

  5. I left your grow method alone. 我没有留下你的成长方法。 I removed the drawLine methods and had the grow method write line segments to the List. 我删除了drawLine方法,并让grow方法将线段写入List。

  6. Once you get your grow method working, you can run the GrowVine class in a separate thread to animate the drawing of the vine. 一旦您的grow方法起作用,就可以在单独的线程中运行GrowVine类,以动画化葡萄树的绘画。

Here's the code. 这是代码。 I hope this helps you. 我希望这可以帮助你。

package com.ggl.testing;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Vine implements Runnable {

    @Override
    public void run() {
        JFrame frame = new JFrame("Vine");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        DrawingPanel panel = new DrawingPanel(400, 400);
        frame.add(panel);

        frame.pack();
        frame.setVisible(true);

        new GrowVine(panel, 400, 400).run();
    }

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

    public class DrawingPanel extends JPanel {

        private static final long serialVersionUID = -8460577623396871909L;

        private List<LineSegment> lineSegments;

        public DrawingPanel(int width, int height) {
            this.setPreferredSize(new Dimension(width, height));
            this.lineSegments = new ArrayList<>();
        }

        public void setLineSegments(List<LineSegment> lineSegments) {
            this.lineSegments = lineSegments;
            repaint();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            g.setColor(Color.BLACK);

            for (int i = 0; i < lineSegments.size(); i++) {
                LineSegment ls = lineSegments.get(i);
                Point s = ls.getStartPoint();
                Point e = ls.getEndPoint();
                g.drawLine(s.x, s.y, e.x, e.y);
            }
        }
    }

    public class GrowVine implements Runnable {

        private int width;
        private int height;

        private DrawingPanel drawingPanel;

        private List<LineSegment> lineSegments;

        public GrowVine(DrawingPanel drawingPanel, int width, int height) {
            this.drawingPanel = drawingPanel;
            this.lineSegments = new ArrayList<>();
            lineSegments.add(new LineSegment(10, 10, width - 10, height - 10));
            this.width = width;
            this.height = height;
        }

        @Override
        public void run() {
            grow(width / 2, height / 2, 200);
            drawingPanel.setLineSegments(lineSegments);
        }

        public void grow(int a, int b, int c) {
            int x = a;
            int y = b;
            int age = c;
            for (int i = 0; i <= age; i++) {
                if (coinFlip()) {
                    if (coinFlip()) {
                        grow(x, y, ((age - i) / 2));
                    }
                }

                if (coinFlip()) {
                    if (coinFlip()) {
                        lineSegments.add(new LineSegment(x, y, (x + 1), y));
                        x++;
                    } else {
                        lineSegments.add(new LineSegment(x, y, (x - 1), y));
                        x++;
                    }
                } else {
                    lineSegments.add(new LineSegment(x, y, x, (y + 1)));
                    y++;
                }
            }
        }

        private boolean coinFlip() {
            return Math.random() < 0.50D;
        }

    }

    public class LineSegment {
        private final Point startPoint;
        private final Point endPoint;

        public LineSegment(int x1, int y1, int x2, int y2) {
            this.startPoint = new Point(x1, y1);
            this.endPoint = new Point(x2, y2);
        }

        public Point getStartPoint() {
            return startPoint;
        }

        public Point getEndPoint() {
            return endPoint;
        }

    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM